[pypy-svn] pypy default: Fix test_thread_and_gc, and add another test_thread_and_gc_with_fork.

arigo commits-noreply at bitbucket.org
Wed Feb 2 16:33:36 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r41566:a63129a046bc
Date: 2011-02-01 15:56 +0100
http://bitbucket.org/pypy/pypy/changeset/a63129a046bc/

Log:	Fix test_thread_and_gc, and add another
	test_thread_and_gc_with_fork. It passes here (meaning without
	asmgcc).

diff --git a/pypy/translator/c/test/test_standalone.py b/pypy/translator/c/test/test_standalone.py
--- a/pypy/translator/c/test/test_standalone.py
+++ b/pypy/translator/c/test/test_standalone.py
@@ -844,6 +844,12 @@
             gc.collect()
             ll_thread.gc_thread_die()
 
+        def new_thread():
+            ll_thread.gc_thread_prepare()
+            ident = ll_thread.start_new_thread(bootstrap, ())
+            time.sleep(0.5)    # enough time to start, hopefully
+            return ident
+
         def entry_point(argv):
             os.write(1, "hello world\n")
             state.xlist = []
@@ -852,19 +858,14 @@
             state.ll_lock = ll_thread.allocate_ll_lock()
             after()
             invoke_around_extcall(before, after)
-            ll_thread.gc_thread_prepare()
-            ident1 = ll_thread.start_new_thread(bootstrap, ())
-            ll_thread.gc_thread_prepare()
-            ident2 = ll_thread.start_new_thread(bootstrap, ())
+            ident1 = new_thread()
+            ident2 = new_thread()
             #
             gc.collect()
             #
-            ll_thread.gc_thread_prepare()
-            ident3 = ll_thread.start_new_thread(bootstrap, ())
-            ll_thread.gc_thread_prepare()
-            ident4 = ll_thread.start_new_thread(bootstrap, ())
-            ll_thread.gc_thread_prepare()
-            ident5 = ll_thread.start_new_thread(bootstrap, ())
+            ident3 = new_thread()
+            ident4 = new_thread()
+            ident5 = new_thread()
             # wait for the 5 threads to finish
             while True:
                 gc.collect()
@@ -892,3 +893,85 @@
                                      '3 ok',
                                      '4 ok',
                                      '5 ok']
+
+
+    def test_thread_and_gc_with_fork(self):
+        # Ideally, it should test that memory allocated for the shadow
+        # stacks of the other threads is really released when doing a
+        # fork(), but it's hard.  Instead it's a "does not crash" kind
+        # of test.
+        import time, gc, os
+        from pypy.module.thread import ll_thread
+        from pypy.rlib.objectmodel import invoke_around_extcall
+        if not hasattr(os, 'fork'):
+            py.test.skip("requires fork()")
+
+        class State:
+            pass
+        state = State()
+
+        def before():
+            ll_assert(not ll_thread.acquire_NOAUTO(state.ll_lock, False),
+                      "lock not held!")
+            ll_thread.release_NOAUTO(state.ll_lock)
+        def after():
+            ll_thread.acquire_NOAUTO(state.ll_lock, True)
+            ll_thread.gc_thread_run()
+
+        class Cons:
+            def __init__(self, head, tail):
+                self.head = head
+                self.tail = tail
+
+        def bootstrap():
+            for i in range(10):
+                state.xlist.append(Cons(123, Cons(456, None)))
+                time.sleep(0.01)
+            childpid = os.fork()
+            gc.collect()        # collect both in the child and in the parent
+            if childpid == 0:
+                os.write(state.write_end, 'c')   # "I did not die!" from child
+            else:
+                os.write(state.write_end, 'p')   # "I did not die!" from parent
+            ll_thread.gc_thread_die()
+
+        def new_thread():
+            ll_thread.gc_thread_prepare()
+            ident = ll_thread.start_new_thread(bootstrap, ())
+            time.sleep(0.5)    # enough time to start, hopefully
+            return ident
+
+        def entry_point(argv):
+            os.write(1, "hello world\n")
+            state.xlist = []
+            state.read_end, state.write_end = os.pipe()
+            x2 = Cons(51, Cons(62, Cons(74, None)))
+            # start 5 new threads
+            state.ll_lock = ll_thread.allocate_ll_lock()
+            after()
+            invoke_around_extcall(before, after)
+            ident1 = new_thread()
+            ident2 = new_thread()
+            ident3 = new_thread()
+            ident4 = new_thread()
+            ident5 = new_thread()
+            # wait for 4 more seconds, which should be plenty of time
+            time.sleep(4)
+            # return everything that was written to the pipe so far,
+            # followed by the final dot.
+            os.write(state.write_end, '.')
+            result = os.read(state.read_end, 256)
+            os.write(1, "got: %s\n" % result)
+            return 0
+
+        t, cbuilder = self.compile(entry_point)
+        data = cbuilder.cmdexec('')
+        print repr(data)
+        header, footer = data.splitlines()
+        assert header == 'hello world'
+        assert footer.startswith('got: ')
+        result = footer[5:]
+        # check that all 5 threads and 5 forked processes
+        # finished successfully
+        assert (len(result) == 11 and result[10] == '.'
+                and result.count('c') == result.count('p') == 5)


More information about the Pypy-commit mailing list