mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
167 lines
4.1 KiB
Python
167 lines
4.1 KiB
Python
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import _interpchannels
|
|
import _interpreters
|
|
|
|
|
|
def roundtrip_buffer(view):
|
|
cid = _interpchannels.create(3)
|
|
try:
|
|
_interpchannels.send_buffer(cid, view, blocking=False)
|
|
received, unbound = _interpchannels.recv(cid)
|
|
assert unbound is None
|
|
return received
|
|
finally:
|
|
_interpchannels.destroy(cid)
|
|
|
|
|
|
data = bytearray(range(12))
|
|
for view in (memoryview(data)[2:10:2], memoryview(data).cast("I")):
|
|
received = roundtrip_buffer(view)
|
|
assert received.tolist() == view.tolist()
|
|
assert received.shape == view.shape
|
|
assert received.strides == view.strides
|
|
assert received.format == view.format
|
|
assert received.itemsize == view.itemsize
|
|
|
|
|
|
# A subinterpreter's finalizing thread need not be the process main thread.
|
|
# Keep a regression bounded: the old eval-breaker parked this worker forever.
|
|
subprocess.run(
|
|
[
|
|
sys.executable,
|
|
"-c",
|
|
"""
|
|
from concurrent import interpreters
|
|
from threading import Thread
|
|
|
|
completed = []
|
|
|
|
def worker():
|
|
interp = interpreters.create()
|
|
interp.exec('''
|
|
import gc, weakref
|
|
gc.disable()
|
|
class C:
|
|
pass
|
|
item = C()
|
|
item.cycle = item
|
|
ref = weakref.ref(item, lambda _: None)
|
|
del item
|
|
''')
|
|
interp.close()
|
|
completed.append(True)
|
|
|
|
thread = Thread(target=worker)
|
|
thread.start()
|
|
thread.join()
|
|
assert completed == [True]
|
|
""",
|
|
],
|
|
check=True,
|
|
timeout=30,
|
|
)
|
|
|
|
|
|
# Script globals persist in the isolated copy, without replacing worker __main__.
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
script = Path(directory) / "pool_main.py"
|
|
script.write_text(
|
|
"""
|
|
from concurrent.futures import InterpreterPoolExecutor
|
|
|
|
counter = 0
|
|
|
|
def increment():
|
|
global counter
|
|
import __main__
|
|
counter += 1
|
|
return counter, hasattr(__main__, 'increment')
|
|
|
|
def inspect_state():
|
|
import __main__
|
|
return counter, hasattr(__main__, 'increment')
|
|
|
|
def consume(queue):
|
|
from threading import Barrier, Thread
|
|
barrier = Barrier(2)
|
|
results = []
|
|
def receive():
|
|
barrier.wait()
|
|
results.append(queue.get()())
|
|
threads = [Thread(target=receive) for _ in range(2)]
|
|
for thread in threads:
|
|
thread.start()
|
|
for thread in threads:
|
|
thread.join()
|
|
return tuple(results)
|
|
|
|
if __name__ == '__main__':
|
|
from concurrent import interpreters
|
|
queue = interpreters.create_queue()
|
|
queue.put(inspect_state)
|
|
queue.put(inspect_state)
|
|
interp = interpreters.create()
|
|
try:
|
|
assert interp.call(consume, queue) == ((0, False), (0, False))
|
|
finally:
|
|
interp.close()
|
|
with InterpreterPoolExecutor(max_workers=1) as pool:
|
|
assert pool.submit(increment).result() == (1, False)
|
|
assert pool.submit(increment).result() == (2, False)
|
|
assert counter == 0
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
subprocess.run([sys.executable, str(script)], check=True, timeout=30)
|
|
|
|
|
|
# Releasing queued buffers can run a finalizer which reenters the channel API.
|
|
subprocess.run(
|
|
[
|
|
sys.executable,
|
|
"-c",
|
|
"""
|
|
import _interpchannels as channels
|
|
import _interpreters
|
|
import sys
|
|
|
|
released = []
|
|
|
|
class Exporter(bytearray):
|
|
def __del__(self):
|
|
channels.list_all()
|
|
released.append(True)
|
|
|
|
for operation in ("destroy", "close", "drop", "timeout"):
|
|
# CPython 3.14.6 also deadlocks when close/drop releases a reentrant exporter.
|
|
if sys.implementation.name == "cpython" and operation in ("close", "drop"):
|
|
continue
|
|
channel = channels.create(3)
|
|
released.clear()
|
|
if operation == "timeout":
|
|
try:
|
|
channels.send_buffer(channel, Exporter(b"data"), timeout=0)
|
|
except TimeoutError:
|
|
pass
|
|
else:
|
|
raise AssertionError("send must time out without a receiver")
|
|
else:
|
|
channels.send_buffer(channel, Exporter(b"data"), blocking=False)
|
|
assert not released
|
|
if operation == "destroy":
|
|
channels.destroy(channel)
|
|
elif operation == "close":
|
|
channels.close(channel, force=True)
|
|
else:
|
|
del channel
|
|
assert released == [True], operation
|
|
""",
|
|
],
|
|
check=True,
|
|
timeout=30,
|
|
)
|