Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ opt-level = 3
lto = "thin"

[patch.crates-io]
parking_lot_core = { git = "https://github.com/youknowone/parking_lot", branch = "rustpython" }
# REDOX START, Uncomment when you want to compile/check with redoxer
# REDOX END

Expand Down
4 changes: 1 addition & 3 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1459,7 +1459,7 @@ def _acquire_release(lock, timeout, l=None, n=1):
for _ in range(n):
lock.release()

@unittest.skip("TODO: RUSTPYTHON; flaky timeout")
@unittest.skip("TODO: RUSTPYTHON; flaky timeout - thread start latency")
def test_repr_rlock(self):
if self.TYPE != 'processes':
self.skipTest('test not appropriate for {}'.format(self.TYPE))
Expand Down Expand Up @@ -4415,7 +4415,6 @@ def test_shared_memory_across_processes(self):

sms.close()

@unittest.skip("TODO: RUSTPYTHON; flaky")
@unittest.skipIf(os.name != "posix", "not feasible in non-posix platforms")
def test_shared_memory_SharedMemoryServer_ignores_sigint(self):
# bpo-36368: protect SharedMemoryManager server process from
Expand All @@ -4440,7 +4439,6 @@ def test_shared_memory_SharedMemoryServer_ignores_sigint(self):

smm.shutdown()

@unittest.skip("TODO: RUSTPYTHON: sem_unlink cleanup race causes spurious stderr output")
@unittest.skipIf(os.name != "posix", "resource_tracker is posix only")
@resource_tracker_format_subtests
def test_shared_memory_SharedMemoryManager_reuses_resource_tracker(self):
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_asyncio/test_unix_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1179,8 +1179,6 @@ async def runner():
wsock.close()


# TODO: RUSTPYTHON, fork() segfaults due to stale parking_lot global state
@unittest.skip("TODO: RUSTPYTHON")
@support.requires_fork()
class TestFork(unittest.TestCase):

Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_concurrent_futures/test_process_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ def test_traceback(self):
self.assertIn('raise RuntimeError(123) # some comment',
f1.getvalue())

@unittest.skip('TODO: RUSTPYTHON flaky EOFError')
@hashlib_helper.requires_hashdigest('md5')
def test_ressources_gced_in_workers(self):
# Ensure that argument for a job are correctly gc-ed after the job
Expand Down
15 changes: 0 additions & 15 deletions Lib/test/test_concurrent_futures/test_wait.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,20 +200,5 @@ def future_func():
def setUpModule():
setup_module()

class ProcessPoolForkWaitTest(ProcessPoolForkWaitTest): # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON flaky")
def test_first_completed(self): super().test_first_completed() # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON Fatal Python error: Segmentation fault")
def test_first_completed_some_already_completed(self): super().test_first_completed_some_already_completed() # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform != 'win32', "TODO: RUSTPYTHON flaky")
def test_first_exception(self): super().test_first_exception() # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON flaky")
def test_first_exception_one_already_failed(self): super().test_first_exception_one_already_failed() # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform != 'win32', "TODO: RUSTPYTHON flaky")
def test_first_exception_some_already_complete(self): super().test_first_exception_some_already_complete() # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON Fatal Python error: Segmentation fault")
def test_timeout(self): super().test_timeout() # TODO: RUSTPYTHON


if __name__ == "__main__":
unittest.main()
17 changes: 0 additions & 17 deletions Lib/test/test_multiprocessing_fork/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,5 @@

install_tests_in_module_dict(globals(), 'fork', only_type="manager")

import sys # TODO: RUSTPYTHON
class WithManagerTestCondition(WithManagerTestCondition): # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', 'TODO: RUSTPYTHON, times out')
def test_notify_all(self): super().test_notify_all() # TODO: RUSTPYTHON

class WithManagerTestQueue(WithManagerTestQueue): # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', 'TODO: RUSTPYTHON, times out')
def test_fork(self): super().test_fork() # TODO: RUSTPYTHON

local_globs = globals().copy() # TODO: RUSTPYTHON
for name, base in local_globs.items(): # TODO: RUSTPYTHON
if name.startswith('WithManagerTest') and issubclass(base, unittest.TestCase): # TODO: RUSTPYTHON
base = unittest.skipIf( # TODO: RUSTPYTHON
sys.platform == 'linux', # TODO: RUSTPYTHON
'TODO: RUSTPYTHON flaky BrokenPipeError, flaky ConnectionRefusedError, flaky ConnectionResetError, flaky EOFError'
)(base) # TODO: RUSTPYTHON

if __name__ == '__main__':
unittest.main()
19 changes: 0 additions & 19 deletions Lib/test/test_multiprocessing_fork/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,5 @@

install_tests_in_module_dict(globals(), 'fork', exclude_types=True)

import sys # TODO: RUSTPYTHON
class TestManagerExceptions(TestManagerExceptions): # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON flaky")
def test_queue_get(self): super().test_queue_get() # TODO: RUSTPYTHON

@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON flaky")
class TestInitializers(TestInitializers): pass # TODO: RUSTPYTHON

class TestStartMethod(TestStartMethod): # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON flaky")
def test_nested_startmethod(self): super().test_nested_startmethod() # TODO: RUSTPYTHON

@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON flaky")
class TestSyncManagerTypes(TestSyncManagerTypes): pass # TODO: RUSTPYTHON

class MiscTestCase(MiscTestCase): # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON flaky")
def test_forked_thread_not_started(self): super().test_forked_thread_not_started() # TODO: RUSTPYTHON

if __name__ == '__main__':
unittest.main()
9 changes: 0 additions & 9 deletions Lib/test/test_multiprocessing_fork/test_threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,5 @@

install_tests_in_module_dict(globals(), 'fork', only_type="threads")

import os, sys # TODO: RUSTPYTHON
class WithThreadsTestPool(WithThreadsTestPool): # TODO: RUSTPYTHON
@unittest.skip("TODO: RUSTPYTHON; flaky environment pollution when running rustpython -m test --fail-env-changed due to unknown reason")
def test_terminate(self): super().test_terminate() # TODO: RUSTPYTHON

class WithThreadsTestManagerRestart(WithThreadsTestManagerRestart): # TODO: RUSTPYTHON
@unittest.skipIf(sys.platform == 'linux', 'TODO: RUSTPYTHON flaky flaky BrokenPipeError, flaky ConnectionRefusedError, flaky ConnectionResetError, flaky EOFError')
def test_rapid_restart(self): super().test_rapid_restart() # TODO: RUSTPYTHON

if __name__ == '__main__':
unittest.main()
149 changes: 149 additions & 0 deletions extra_tests/test_manager_fork_debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""Minimal reproduction of multiprocessing Manager + fork failure."""

import multiprocessing
import os
import sys
import time
import traceback

import pytest

pytestmark = pytest.mark.skipif(not hasattr(os, "fork"), reason="requires os.fork")


def test_basic_manager():
"""Test Manager without fork - does it work at all?"""
print("=== Test 1: Basic Manager (no fork) ===")
ctx = multiprocessing.get_context("fork")
manager = ctx.Manager()
try:
ev = manager.Event()
print(f" Event created: {ev}")
ev.set()
print(f" Event set, is_set={ev.is_set()}")
assert ev.is_set()
print(" PASS")
finally:
manager.shutdown()


def test_manager_with_process():
"""Test Manager shared between parent and child process."""
print("\n=== Test 2: Manager with forked child ===")
ctx = multiprocessing.get_context("fork")
manager = ctx.Manager()
try:
result = manager.Value("i", 0)
ev = manager.Event()

def child_fn():
try:
ev.set()
result.value = 42
except Exception as e:
print(f" CHILD ERROR: {e}", file=sys.stderr)
traceback.print_exc()
sys.exit(1)

print(f" Starting child process...")
process = ctx.Process(target=child_fn)
process.start()
print(f" Waiting for child (pid={process.pid})...")
process.join(timeout=10)

if process.exitcode != 0:
print(f" FAIL: child exited with code {process.exitcode}")
return False

print(f" Child done. result={result.value}, event={ev.is_set()}")
assert result.value == 42
assert ev.is_set()
print(" PASS")
return True
finally:
manager.shutdown()


def test_manager_server_alive_after_fork():
"""Test that Manager server survives after forking a child."""
print("\n=== Test 3: Manager server alive after fork ===")
ctx = multiprocessing.get_context("fork")
manager = ctx.Manager()
try:
ev = manager.Event()

# Fork a child that does nothing with the manager
pid = os.fork()
if pid == 0:
# Child - exit immediately
os._exit(0)

# Parent - wait for child
os.waitpid(pid, 0)

# Now try to use the manager in the parent
print(f" After fork, trying to use Manager in parent...")
ev.set()
print(f" ev.is_set() = {ev.is_set()}")
assert ev.is_set()
print(" PASS")
return True
finally:
manager.shutdown()


def test_manager_server_alive_after_fork_with_child_usage():
"""Test that Manager server survives when child also uses it."""
print("\n=== Test 4: Manager server alive after fork + child usage ===")
ctx = multiprocessing.get_context("fork")
manager = ctx.Manager()
try:
child_ev = manager.Event()
parent_ev = manager.Event()

def child_fn():
try:
child_ev.set()
except Exception as e:
print(f" CHILD ERROR: {e}", file=sys.stderr)
traceback.print_exc()
sys.exit(1)

process = ctx.Process(target=child_fn)
process.start()
process.join(timeout=10)

if process.exitcode != 0:
print(f" FAIL: child exited with code {process.exitcode}")
return False

# Now use manager in parent AFTER child is done
print(f" Child done. Trying parent usage...")
parent_ev.set()
print(f" child_ev={child_ev.is_set()}, parent_ev={parent_ev.is_set()}")
assert child_ev.is_set()
assert parent_ev.is_set()
print(" PASS")
return True
finally:
manager.shutdown()


if __name__ == "__main__":
test_basic_manager()

passed = 0
total = 10
for i in range(total):
print(f"\n--- Iteration {i + 1}/{total} ---")
ok = True
ok = ok and test_manager_with_process()
ok = ok and test_manager_server_alive_after_fork()
ok = ok and test_manager_server_alive_after_fork_with_child_usage()
if ok:
passed += 1
else:
print(f" FAILED on iteration {i + 1}")

print(f"\n=== Results: {passed}/{total} passed ===")
sys.exit(0 if passed == total else 1)
Loading