• Victor Stinner's avatar
    Merged revisions 80552-80556,80564-80566,80568-80571 via svnmerge from · 45df8205
    Victor Stinner yazdı
    svn+ssh://pythondev@svn.python.org/python/trunk
    
    ........
      r80552 | victor.stinner | 2010-04-27 23:46:03 +0200 (mar., 27 avril 2010) | 3 lines
    
      Issue #7449, part 1: fix test_support.py for Python compiled without thread
    ........
      r80553 | victor.stinner | 2010-04-27 23:47:01 +0200 (mar., 27 avril 2010) | 1 line
    
      Issue #7449, part 2: regrtest.py -j option requires thread support
    ........
      r80554 | victor.stinner | 2010-04-27 23:51:26 +0200 (mar., 27 avril 2010) | 9 lines
    
      Issue #7449 part 3, test_doctest: import trace module in test_coverage()
    
      Import trace module fail if the threading module is missing. test_coverage() is
      only used if test_doctest.py is used with the -c option. This commit allows to
      execute the test suite without thread support.
    
      Move "import trace" in test_coverage() and use
      test_support.import_module('trace').
    ........
      r80555 | victor.stinner | 2010-04-27 23:56:26 +0200 (mar., 27 avril 2010) | 6 lines
    
      Issue #7449, part 4: skip test_multiprocessing if thread support is disabled
    
      import threading after _multiprocessing to raise a more revelant error message:
      "No module named _multiprocessing". _multiprocessing is not compiled without
      thread support.
    ........
      r80556 | victor.stinner | 2010-04-28 00:01:24 +0200 (mer., 28 avril 2010) | 8 lines
    
      Issue #7449, part 5: split Test.test_open() of ctypes/test/test_errno.py
    
       * Split Test.test_open() in 2 functions: test_open() and test_thread_open()
       * Skip test_open() and test_thread_open() if we are unable to find the C
         library
       * Skip test_thread_open() if thread support is disabled
       * Use unittest.skipUnless(os.name == "nt", ...) on test_GetLastError()
    ........
      r80564 | victor.stinner | 2010-04-28 00:59:35 +0200 (mer., 28 avril 2010) | 4 lines
    
      Issue #7449, part 6: fix test_hashlib for missing threading module
    
      Move @test_support.reap_thread decorator from test_main() to test_threaded_hashing().
    ........
      r80565 | victor.stinner | 2010-04-28 01:01:29 +0200 (mer., 28 avril 2010) | 6 lines
    
      Issue #7449, part 7: simplify threading detection in test_capi
    
       * Skip TestPendingCalls if threading module is missing
       * Test if threading module is present or not, instead of test the presence of
         _testcapi._test_thread_state
    ........
      r80566 | victor.stinner | 2010-04-28 01:03:16 +0200 (mer., 28 avril 2010) | 4 lines
    
      Issue #7449, part 8: don't skip the whole test_asynchat if threading is missing
    
      TestFifo can be executed without the threading module
    ........
      r80568 | victor.stinner | 2010-04-28 01:14:58 +0200 (mer., 28 avril 2010) | 6 lines
    
      Issue #7449, part 9: fix test_xmlrpclib for missing threading module
    
       * Skip testcases using threads if threading module is missing
       * Use "http://" instead of URL in ServerProxyTestCase if threading is missing
         because URL is not set in this case
    ........
      r80569 | victor.stinner | 2010-04-28 01:33:58 +0200 (mer., 28 avril 2010) | 6 lines
    
      Partial revert of r80556 (Issue #7449, part 5, fix ctypes test)
    
      Rewrite r80556: the thread test have to be executed just after the test on
      libc_open() and so the test cannot be splitted in two functions (without
      duplicating code, and I don't want to duplicate code).
    ........
      r80570 | victor.stinner | 2010-04-28 01:51:16 +0200 (mer., 28 avril 2010) | 8 lines
    
      Issue #7449, part 10: test_cmd imports trace module using test_support.import_module()
    
      Use test_support.import_module() instead of import to raise a SkipTest
      exception if the import fail. Import trace fails if the threading module is
      missing.
    
      See also part 3: test_doctest: import trace module in test_coverage().
    ........
      r80571 | victor.stinner | 2010-04-28 01:55:59 +0200 (mer., 28 avril 2010) | 6 lines
    
      Issue #7449, last part (11): fix many tests if thread support is disabled
    
       * Use try/except ImportError or test_support.import_module() to import thread
         and threading modules
       * Add @unittest.skipUnless(threading, ...) to testcases using threads
    ........
    45df8205
test_thread.py 7.44 KB
import os
import unittest
import random
from test import support
thread = support.import_module('_thread')
import time
import sys
import weakref

from test import lock_tests

NUMTASKS = 10
NUMTRIPS = 3

_print_mutex = thread.allocate_lock()

def verbose_print(arg):
    """Helper function for printing out debugging output."""
    if support.verbose:
        with _print_mutex:
            print(arg)

class BasicThreadTest(unittest.TestCase):

    def setUp(self):
        self.done_mutex = thread.allocate_lock()
        self.done_mutex.acquire()
        self.running_mutex = thread.allocate_lock()
        self.random_mutex = thread.allocate_lock()
        self.created = 0
        self.running = 0
        self.next_ident = 0


class ThreadRunningTests(BasicThreadTest):

    def newtask(self):
        with self.running_mutex:
            self.next_ident += 1
            verbose_print("creating task %s" % self.next_ident)
            thread.start_new_thread(self.task, (self.next_ident,))
            self.created += 1
            self.running += 1

    def task(self, ident):
        with self.random_mutex:
            delay = random.random() / 10000.0
        verbose_print("task %s will run for %sus" % (ident, round(delay*1e6)))
        time.sleep(delay)
        verbose_print("task %s done" % ident)
        with self.running_mutex:
            self.running -= 1
            if self.created == NUMTASKS and self.running == 0:
                self.done_mutex.release()

    def test_starting_threads(self):
        # Basic test for thread creation.
        for i in range(NUMTASKS):
            self.newtask()
        verbose_print("waiting for tasks to complete...")
        self.done_mutex.acquire()
        verbose_print("all tasks done")

    def test_stack_size(self):
        # Various stack size tests.
        self.assertEqual(thread.stack_size(), 0, "initial stack size is not 0")

        thread.stack_size(0)
        self.assertEqual(thread.stack_size(), 0, "stack_size not reset to default")

        if os.name not in ("nt", "os2", "posix"):
            return

        tss_supported = True
        try:
            thread.stack_size(4096)
        except ValueError:
            verbose_print("caught expected ValueError setting "
                            "stack_size(4096)")
        except thread.error:
            tss_supported = False
            verbose_print("platform does not support changing thread stack "
                            "size")

        if tss_supported:
            fail_msg = "stack_size(%d) failed - should succeed"
            for tss in (262144, 0x100000, 0):
                thread.stack_size(tss)
                self.assertEqual(thread.stack_size(), tss, fail_msg % tss)
                verbose_print("successfully set stack_size(%d)" % tss)

            for tss in (262144, 0x100000):
                verbose_print("trying stack_size = (%d)" % tss)
                self.next_ident = 0
                self.created = 0
                for i in range(NUMTASKS):
                    self.newtask()

                verbose_print("waiting for all tasks to complete")
                self.done_mutex.acquire()
                verbose_print("all tasks done")

            thread.stack_size(0)

    def test__count(self):
        # Test the _count() function.
        orig = thread._count()
        mut = thread.allocate_lock()
        mut.acquire()
        started = []
        def task():
            started.append(None)
            mut.acquire()
            mut.release()
        thread.start_new_thread(task, ())
        while not started:
            time.sleep(0.01)
        self.assertEqual(thread._count(), orig + 1)
        # Allow the task to finish.
        mut.release()
        # The only reliable way to be sure that the thread ended from the
        # interpreter's point of view is to wait for the function object to be
        # destroyed.
        done = []
        wr = weakref.ref(task, lambda _: done.append(None))
        del task
        while not done:
            time.sleep(0.01)
        self.assertEqual(thread._count(), orig)


class Barrier:
    def __init__(self, num_threads):
        self.num_threads = num_threads
        self.waiting = 0
        self.checkin_mutex  = thread.allocate_lock()
        self.checkout_mutex = thread.allocate_lock()
        self.checkout_mutex.acquire()

    def enter(self):
        self.checkin_mutex.acquire()
        self.waiting = self.waiting + 1
        if self.waiting == self.num_threads:
            self.waiting = self.num_threads - 1
            self.checkout_mutex.release()
            return
        self.checkin_mutex.release()

        self.checkout_mutex.acquire()
        self.waiting = self.waiting - 1
        if self.waiting == 0:
            self.checkin_mutex.release()
            return
        self.checkout_mutex.release()


class BarrierTest(BasicThreadTest):

    def test_barrier(self):
        self.bar = Barrier(NUMTASKS)
        self.running = NUMTASKS
        for i in range(NUMTASKS):
            thread.start_new_thread(self.task2, (i,))
        verbose_print("waiting for tasks to end")
        self.done_mutex.acquire()
        verbose_print("tasks done")

    def task2(self, ident):
        for i in range(NUMTRIPS):
            if ident == 0:
                # give it a good chance to enter the next
                # barrier before the others are all out
                # of the current one
                delay = 0
            else:
                with self.random_mutex:
                    delay = random.random() / 10000.0
            verbose_print("task %s will run for %sus" %
                          (ident, round(delay * 1e6)))
            time.sleep(delay)
            verbose_print("task %s entering %s" % (ident, i))
            self.bar.enter()
            verbose_print("task %s leaving barrier" % ident)
        with self.running_mutex:
            self.running -= 1
            # Must release mutex before releasing done, else the main thread can
            # exit and set mutex to None as part of global teardown; then
            # mutex.release() raises AttributeError.
            finished = self.running == 0
        if finished:
            self.done_mutex.release()

class LockTests(lock_tests.LockTests):
    locktype = thread.allocate_lock


class TestForkInThread(unittest.TestCase):
    def setUp(self):
        self.read_fd, self.write_fd = os.pipe()

    @unittest.skipIf(sys.platform.startswith('win'),
                     "This test is only appropriate for POSIX-like systems.")
    @support.reap_threads
    def test_forkinthread(self):
        def thread1():
            try:
                pid = os.fork() # fork in a thread
            except RuntimeError:
                os._exit(1) # exit the child

            if pid == 0: # child
                try:
                    os.close(self.read_fd)
                    os.write(self.write_fd, b"OK")
                finally:
                    os._exit(0)
            else: # parent
                os.close(self.write_fd)

        thread.start_new_thread(thread1, ())
        self.assertEqual(os.read(self.read_fd, 2), b"OK",
                         "Unable to fork() in thread")

    def tearDown(self):
        try:
            os.close(self.read_fd)
        except OSError:
            pass

        try:
            os.close(self.write_fd)
        except OSError:
            pass


def test_main():
    support.run_unittest(ThreadRunningTests, BarrierTest, LockTests,
                         TestForkInThread)

if __name__ == "__main__":
    test_main()