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