-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathtest_manager_fork_debug.py
More file actions
149 lines (123 loc) · 4.32 KB
/
test_manager_fork_debug.py
File metadata and controls
149 lines (123 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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)