1"""This test case provides support for checking forking and wait behavior.
2
3To test different wait behavior, override the wait_impl method.
4
5We want fork1() semantics -- only the forking thread survives in the
6child after a fork().
7
8On some systems (e.g. Solaris without posix threads) we find that all
9active threads survive in the child after a fork(); this is an error.
10"""
11
12import os, sys, time, unittest
13import threading
14from test import support
15from test.support import threading_helper
16
17
18LONGSLEEP = 2
19SHORTSLEEP = 0.5
20NUM_THREADS = 4
21
22class ForkWait(unittest.TestCase):
23
24    def setUp(self):
25        self._threading_key = threading_helper.threading_setup()
26        self.alive = {}
27        self.stop = 0
28        self.threads = []
29
30    def tearDown(self):
31        # Stop threads
32        self.stop = 1
33        for thread in self.threads:
34            thread.join()
35        thread = None
36        self.threads.clear()
37        threading_helper.threading_cleanup(*self._threading_key)
38
39    def f(self, id):
40        while not self.stop:
41            self.alive[id] = os.getpid()
42            try:
43                time.sleep(SHORTSLEEP)
44            except OSError:
45                pass
46
47    def wait_impl(self, cpid, *, exitcode):
48        support.wait_process(cpid, exitcode=exitcode)
49
50    def test_wait(self):
51        for i in range(NUM_THREADS):
52            thread = threading.Thread(target=self.f, args=(i,))
53            thread.start()
54            self.threads.append(thread)
55
56        # busy-loop to wait for threads
57        deadline = time.monotonic() + support.SHORT_TIMEOUT
58        while len(self.alive) < NUM_THREADS:
59            time.sleep(0.1)
60            if deadline < time.monotonic():
61                break
62
63        a = sorted(self.alive.keys())
64        self.assertEqual(a, list(range(NUM_THREADS)))
65
66        prefork_lives = self.alive.copy()
67
68        if sys.platform in ['unixware7']:
69            cpid = os.fork1()
70        else:
71            cpid = os.fork()
72
73        if cpid == 0:
74            # Child
75            time.sleep(LONGSLEEP)
76            n = 0
77            for key in self.alive:
78                if self.alive[key] != prefork_lives[key]:
79                    n += 1
80            os._exit(n)
81        else:
82            # Parent
83            self.wait_impl(cpid, exitcode=0)
84