In projects which dynamically determine whether to activate eventlet, it can be hard not to import a low level module like logging before eventlet. When logging is imported it initialises a threading.RLock which it uses to protect the logging configuration. If two greenthreads attempt to claim this lock, the second one will block the /native/ thread not just itself. As green systems usually only have one native thread, this will freeze the whole system. Search the GC for unsafe RLocks and replace their internal Lock with a safe one while monkey-patching. The tests pass, but were they to fail, the test process would never return. To deal with this, I've added a test dependency on subprocess32 which is a backport of the stdlib subprocess module from Python3. This offers a timeout option on Popen#communicate, which I've arbitrarily set at 30 seconds.
		
			
				
	
	
		
			43 lines
		
	
	
		
			865 B
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			43 lines
		
	
	
		
			865 B
		
	
	
	
		
			Python
		
	
	
	
	
	
__test__ = False
 | 
						|
 | 
						|
 | 
						|
def take(lock, sync1, sync2):
 | 
						|
    sync2.acquire()
 | 
						|
    sync1.release()
 | 
						|
    with lock:
 | 
						|
        sync2.release()
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    import sys
 | 
						|
    import threading
 | 
						|
    lock = threading.RLock()
 | 
						|
    lock.acquire()
 | 
						|
    import eventlet
 | 
						|
    eventlet.monkey_patch()
 | 
						|
 | 
						|
    lock.release()
 | 
						|
    try:
 | 
						|
        lock.release()
 | 
						|
    except RuntimeError as e:
 | 
						|
        assert e.args == ('cannot release un-acquired lock',)
 | 
						|
    lock.acquire()
 | 
						|
 | 
						|
    sync1 = threading.Lock()
 | 
						|
    sync2 = threading.Lock()
 | 
						|
    sync1.acquire()
 | 
						|
    eventlet.spawn(take, lock, sync1, sync2)
 | 
						|
    # Ensure sync2 has been taken
 | 
						|
    with sync1:
 | 
						|
        pass
 | 
						|
 | 
						|
    # an RLock should be reentrant
 | 
						|
    lock.acquire()
 | 
						|
    lock.release()
 | 
						|
    lock.release()
 | 
						|
    # To acquire sync2, 'take' must have acquired lock, which has been locked
 | 
						|
    # until now
 | 
						|
    sync2.acquire()
 | 
						|
 | 
						|
    print('pass')
 |