# testing gevent's Event, Lock, RLock, Semaphore, BoundedSemaphore with standard test_threadingfrom__future__importwith_statementfromgeventimportmonkey;monkey.patch_all()fromgevent.eventimportEventfromgevent.corosimportRLock,Semaphore,BoundedSemaphorefromgevent.threadimportallocate_lockasLockimporttest.test_supportfromtest.test_supportimportverboseimportrandomimportreimportsysimportthreadingimportthreadimporttimeimportunittestimportweakrefthreading.Event=Eventthreading.Lock=Lockthreading.RLock=RLockthreading.Semaphore=Semaphorethreading.BoundedSemaphore=BoundedSemaphoreifnothasattr(threading,'current_thread'):threading.current_thread=threading.currentThreadifnothasattr(threading.Thread,'name'):threading.Thread.name=property(lambdaself:self.getName())ifnothasattr(threading.Thread,'is_alive'):threading.Thread.is_alive=threading.Thread.isAliveifnothasattr(threading._Condition,'notify_all'):threading._Condition.notify_all=threading._Condition.notifyAllimportlock_tests# A trivial mutable counter.classCounter(object):def__init__(self):self.value=0definc(self):self.value+=1defdec(self):self.value-=1defget(self):returnself.valueclassTestThread(threading.Thread):def__init__(self,name,testcase,sema,mutex,nrunning):threading.Thread.__init__(self,name=name)self.testcase=testcaseself.sema=semaself.mutex=mutexself.nrunning=nrunningdefrun(self):delay=random.random()/10000.0ifverbose:print'task %s will run for %.1f usec'%(self.name,delay*1e6)withself.sema:withself.mutex:self.nrunning.inc()ifverbose:printself.nrunning.get(),'tasks are running'self.testcase.assert_(self.nrunning.get()<=3)time.sleep(delay)ifverbose:print'task',self.name,'done'withself.mutex:self.nrunning.dec()self.testcase.assert_(self.nrunning.get()>=0)ifverbose:print'%s is finished. %d tasks are running'%(self.name,self.nrunning.get())classThreadTests(unittest.TestCase):# Create a bunch of threads, let each do some work, wait until all are# done.deftest_various_ops(self):# This takes about n/3 seconds to run (about n/3 clumps of tasks,# times about 1 second per clump).NUMTASKS=10# no more than 3 of the 10 can run at oncesema=threading.BoundedSemaphore(value=3)mutex=threading.RLock()numrunning=Counter()threads=[]foriinrange(NUMTASKS):t=TestThread("<thread %d>"%i,self,sema,mutex,numrunning)threads.append(t)ifhasattr(t,'ident'):self.failUnlessEqual(t.ident,None)self.assert_(re.match('<TestThread\(.*, initial\)>',repr(t)))t.start()ifverbose:print'waiting for all tasks to complete'fortinthreads:t.join(NUMTASKS)self.assert_(nott.is_alive())ifhasattr(t,'ident'):self.failIfEqual(t.ident,0)self.assertFalse(t.identisNone)self.assert_(re.match('<TestThread\(.*, \w+ -?\d+\)>',repr(t)))ifverbose:print'all tasks done'self.assertEqual(numrunning.get(),0)ifsys.version_info[:2]>(2,5):deftest_ident_of_no_threading_threads(self):# The ident still must work for the main thread and dummy threads.self.assertFalse(threading.currentThread().identisNone)deff():ident.append(threading.currentThread().ident)done.set()done=threading.Event()ident=[]thread.start_new_thread(f,())done.wait()self.assertFalse(ident[0]isNone)# Kill the "immortal" _DummyThreaddelthreading._active[ident[0]]# run with a small(ish) thread stack size (256kB)deftest_various_ops_small_stack(self):ifverbose:print'with 256kB thread stack size...'try:threading.stack_size(262144)exceptthread.error:ifverbose:print'platform does not support changing thread stack size'returnself.test_various_ops()threading.stack_size(0)# run with a large thread stack size (1MB)deftest_various_ops_large_stack(self):ifverbose:print'with 1MB thread stack size...'try:threading.stack_size(0x100000)exceptthread.error:ifverbose:print'platform does not support changing thread stack size'returnself.test_various_ops()threading.stack_size(0)defBOGUS_test_foreign_thread(self):# Check that a "foreign" thread can use the threading module.deff(mutex):# Calling current_thread() forces an entry for the foreign# thread to get made in the threading._active map.threading.current_thread()mutex.release()mutex=threading.Lock()mutex.acquire()tid=thread.start_new_thread(f,(mutex,))# Wait for the thread to finish.mutex.acquire()self.assert_(tidinthreading._active)self.assert_(isinstance(threading._active[tid],threading._DummyThread))delthreading._active[tid]# PyThreadState_SetAsyncExc() is a CPython-only gimmick, not (currently)# exposed at the Python level. This test relies on ctypes to get at it.defSKIP_test_PyThreadState_SetAsyncExc(self):try:importctypesexceptImportError:ifverbose:print"test_PyThreadState_SetAsyncExc can't import ctypes"return# can't do anythingset_async_exc=ctypes.pythonapi.PyThreadState_SetAsyncExcclassAsyncExc(Exception):passexception=ctypes.py_object(AsyncExc)# `worker_started` is set by the thread when it's inside a try/except# block waiting to catch the asynchronously set AsyncExc exception.# `worker_saw_exception` is set by the thread upon catching that# exception.worker_started=threading.Event()worker_saw_exception=threading.Event()classWorker(threading.Thread):defrun(self):self.id=thread.get_ident()self.finished=Falsetry:whileTrue:worker_started.set()time.sleep(0.1)exceptAsyncExc:self.finished=Trueworker_saw_exception.set()t=Worker()t.daemon=True# so if this fails, we don't hang Python at shutdownt.start()ifverbose:print" started worker thread"# Try a thread id that doesn't make sense.ifverbose:print" trying nonsensical thread id"result=set_async_exc(ctypes.c_long(-1),exception)self.assertEqual(result,0)# no thread states modified# Now raise an exception in the worker thread.ifverbose:print" waiting for worker thread to get started"worker_started.wait()ifverbose:print" verifying worker hasn't exited"self.assert_(nott.finished)ifverbose:print" attempting to raise asynch exception in worker"result=set_async_exc(ctypes.c_long(t.id),exception)self.assertEqual(result,1)# one thread state modifiedifverbose:print" waiting for worker to say it caught the exception"worker_saw_exception.wait(timeout=10)self.assert_(t.finished)ifverbose:print" all OK -- joining worker"ift.finished:t.join()# else the thread is still running, and we have no way to kill itifsys.version_info[:2]>(2,5):deftest_limbo_cleanup(self):# Issue 7481: Failure to start thread should cleanup the limbo map.deffail_new_thread(*args):raisethread.error()_start_new_thread=threading._start_new_threadthreading._start_new_thread=fail_new_threadtry:t=threading.Thread(target=lambda:None)self.assertRaises(thread.error,t.start)self.assertFalse(tinthreading._limbo,"Failed to cleanup _limbo map on failure of Thread.start().")finally:threading._start_new_thread=_start_new_threadifsys.version_info[:2]>(2,5):deftest_finalize_runnning_thread(self):# Issue 1402: the PyGILState_Ensure / _Release functions may be called# very late on python exit: on deallocation of a running thread for# example.try:importctypesexceptImportError:ifverbose:print("test_finalize_with_runnning_thread can't import ctypes")return# can't do anythingimportsubprocessrc=subprocess.call([sys.executable,"-c","""if 1: import ctypes, sys, time, thread # This lock is used as a simple event variable. ready = thread.allocate_lock() ready.acquire() # Module globals are cleared before __del__ is run # So we save the functions in class dict class C: ensure = ctypes.pythonapi.PyGILState_Ensure release = ctypes.pythonapi.PyGILState_Release def __del__(self): state = self.ensure() self.release(state) def waitingThread(): x = C() ready.release() time.sleep(100) thread.start_new_thread(waitingThread, ()) ready.acquire() # Be sure the other thread is waiting. sys.exit(42) """])self.assertEqual(rc,42)deftest_finalize_with_trace(self):# Issue1733757# Avoid a deadlock when sys.settrace steps into threading._shutdownimportsubprocessrc=subprocess.call([sys.executable,"-c","""if 1: import sys, threading # A deadlock-killer, to prevent the # testsuite to hang forever def killer(): import os, time time.sleep(2) print 'program blocked; aborting' os._exit(2) t = threading.Thread(target=killer) t.daemon = True t.start() # This is the trace function def func(frame, event, arg): threading.current_thread() return func sys.settrace(func) """])self.failIf(rc==2,"interpreted was blocked")self.failUnless(rc==0,"Unexpected error")ifsys.version_info[:2]>(2,5):deftest_join_nondaemon_on_shutdown(self):# Issue 1722344# Raising SystemExit skipped threading._shutdownimportsubprocessp=subprocess.Popen([sys.executable,"-c","""if 1: import threading from time import sleep def child(): sleep(1) # As a non-daemon thread we SHOULD wake up and nothing # should be torn down yet print "Woke up, sleep function is:", sleep threading.Thread(target=child).start() raise SystemExit """],stdout=subprocess.PIPE,stderr=subprocess.PIPE)stdout,stderr=p.communicate()self.assertEqual(stdout.strip(),"Woke up, sleep function is: <built-in function sleep>")stderr=re.sub(r"^\[\d+ refs\]","",stderr,re.MULTILINE).strip()self.assertEqual(stderr,"")deftest_enumerate_after_join(self):# Try hard to trigger #1703448: a thread is still returned in# threading.enumerate() after it has been join()ed.enum=threading.enumerateold_interval=sys.getcheckinterval()try:foriinxrange(1,100):# Try a couple times at each thread-switching interval# to get more interleavings.sys.setcheckinterval(i//5)t=threading.Thread(target=lambda:None)t.start()t.join()l=enum()self.assertFalse(tinl,"#1703448 triggered after %d trials: %s"%(i,l))finally:sys.setcheckinterval(old_interval)ifsys.version_info[:2]>(2,5):deftest_no_refcycle_through_target(self):classRunSelfFunction(object):def__init__(self,should_raise):# The links in this refcycle from Thread back to self# should be cleaned up when the thread completes.self.should_raise=should_raiseself.thread=threading.Thread(target=self._run,args=(self,),kwargs={'yet_another':self})self.thread.start()def_run(self,other_ref,yet_another):ifself.should_raise:raiseSystemExitcyclic_object=RunSelfFunction(should_raise=False)weak_cyclic_object=weakref.ref(cyclic_object)cyclic_object.thread.join()delcyclic_objectself.assertEquals(None,weak_cyclic_object(),msg=('%d references still around'%sys.getrefcount(weak_cyclic_object())))raising_cyclic_object=RunSelfFunction(should_raise=True)weak_raising_cyclic_object=weakref.ref(raising_cyclic_object)raising_cyclic_object.thread.join()delraising_cyclic_objectself.assertEquals(None,weak_raising_cyclic_object(),msg=('%d references still around'%sys.getrefcount(weak_raising_cyclic_object())))classThreadJoinOnShutdown(unittest.TestCase):def_run_and_join(self,script):script="""if 1: import sys, os, time, threading # a thread, which waits for the main program to terminate def joiningfunc(mainthread): mainthread.join() print 'end of thread'\n"""+scriptimportsubprocessp=subprocess.Popen([sys.executable,"-c",script],stdout=subprocess.PIPE)rc=p.wait()data=p.stdout.read().replace('\r','')self.assertEqual(data,"end of main\nend of thread\n")self.failIf(rc==2,"interpreter was blocked")self.failUnless(rc==0,"Unexpected error")deftest_1_join_on_shutdown(self):# The usual case: on exit, wait for a non-daemon threadscript="""if 1: import os t = threading.Thread(target=joiningfunc, args=(threading.current_thread(),)) t.start() time.sleep(0.1) print 'end of main' """self._run_and_join(script)deftest_2_join_in_forked_process(self):# Like the test above, but from a forked interpreterimportosifnothasattr(os,'fork'):returnscript="""if 1: childpid = os.fork() if childpid != 0: os.waitpid(childpid, 0) sys.exit(0) t = threading.Thread(target=joiningfunc, args=(threading.current_thread(),)) t.start() print 'end of main' """self._run_and_join(script)deftest_3_join_in_forked_from_thread(self):# Like the test above, but fork() was called from a worker thread# In the forked process, the main Thread object must be marked as stopped.importosifnothasattr(os,'fork'):return# Skip platforms with known problems forking from a worker thread.# See http://bugs.python.org/issue3863.ifsys.platformin('freebsd4','freebsd5','freebsd6','os2emx'):print>>sys.stderr,('Skipping test_3_join_in_forked_from_thread'' due to known OS bugs on'),sys.platformreturnscript="""if 1: main_thread = threading.current_thread() def worker(): childpid = os.fork() if childpid != 0: os.waitpid(childpid, 0) sys.exit(0) t = threading.Thread(target=joiningfunc, args=(main_thread,)) print 'end of main' t.start() t.join() # Should not block: main_thread is already stopped w = threading.Thread(target=worker) w.start() """self._run_and_join(script)classThreadingExceptionTests(unittest.TestCase):# A RuntimeError should be raised if Thread.start() is called# multiple times.deftest_start_thread_again(self):thread=threading.Thread()thread.start()self.assertRaises(RuntimeError,thread.start)deftest_joining_current_thread(self):current_thread=threading.current_thread()self.assertRaises(RuntimeError,current_thread.join);deftest_joining_inactive_thread(self):thread=threading.Thread()self.assertRaises(RuntimeError,thread.join)deftest_daemonize_active_thread(self):thread=threading.Thread()thread.start()self.assertRaises(RuntimeError,setattr,thread,"daemon",True)classLockTests(lock_tests.LockTests):locktype=staticmethod(threading.Lock)classRLockTests(lock_tests.RLockTests):locktype=staticmethod(threading.RLock)classEventTests(lock_tests.EventTests):eventtype=staticmethod(threading.Event)classConditionAsRLockTests(lock_tests.RLockTests):# An Condition uses an RLock by default and exports its API.locktype=staticmethod(threading.Condition)classConditionTests(lock_tests.ConditionTests):condtype=staticmethod(threading.Condition)classSemaphoreTests(lock_tests.SemaphoreTests):semtype=staticmethod(threading.Semaphore)classBoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests):semtype=staticmethod(threading.BoundedSemaphore)deftest_main():test.test_support.run_unittest(LockTests,RLockTests,EventTests,ConditionAsRLockTests,ConditionTests,SemaphoreTests,BoundedSemaphoreTests,ThreadTests,ThreadJoinOnShutdown,ThreadingExceptionTests,)if__name__=="__main__":test_main()