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
14import test.support as support
15
16
17LONGSLEEP = 2
18SHORTSLEEP = 0.5
19NUM_THREADS = 4
20
21class ForkWait(unittest.TestCase):
22
23    def setUp(self):
24        self._threading_key = support.threading_setup()
25        self.alive = {}
26        self.stop = 0
27        self.threads = []
28
29    def tearDown(self):
30        # Stop threads
31        self.stop = 1
32        for thread in self.threads:
33            thread.join()
34        thread = None
35        self.threads.clear()
36        support.threading_cleanup(*self._threading_key)
37
38    def f(self, id):
39        while not self.stop:
40            self.alive[id] = os.getpid()
41            try:
42                time.sleep(SHORTSLEEP)
43            except OSError:
44                pass
45
46    def wait_impl(self, cpid):
47        for i in range(10):
48            # waitpid() shouldn't hang, but some of the buildbots seem to hang
49            # in the forking tests.  This is an attempt to fix the problem.
50            spid, status = os.waitpid(cpid, os.WNOHANG)
51            if spid == cpid:
52                break
53            time.sleep(2 * SHORTSLEEP)
54
55        self.assertEqual(spid, cpid)
56        self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
57
58    def test_wait(self):
59        for i in range(NUM_THREADS):
60            thread = threading.Thread(target=self.f, args=(i,))
61            thread.start()
62            self.threads.append(thread)
63
64        # busy-loop to wait for threads
65        deadline = time.monotonic() + 10.0
66        while len(self.alive) < NUM_THREADS:
67            time.sleep(0.1)
68            if deadline < time.monotonic():
69                break
70
71        a = sorted(self.alive.keys())
72        self.assertEqual(a, list(range(NUM_THREADS)))
73
74        prefork_lives = self.alive.copy()
75
76        if sys.platform in ['unixware7']:
77            cpid = os.fork1()
78        else:
79            cpid = os.fork()
80
81        if cpid == 0:
82            # Child
83            time.sleep(LONGSLEEP)
84            n = 0
85            for key in self.alive:
86                if self.alive[key] != prefork_lives[key]:
87                    n += 1
88            os._exit(n)
89        else:
90            # Parent
91            self.wait_impl(cpid)
92