| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | """Synchronization metaclass.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This metaclass  makes it possible to declare synchronized methods. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-25 13:05:15 +00:00
										 |  |  | import _thread as thread | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # First we need to define a reentrant lock. | 
					
						
							|  |  |  | # This is generally useful and should probably be in a standard Python | 
					
						
							|  |  |  | # library module.  For now, we in-line it. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Lock: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Reentrant lock.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This is a mutex-like object which can be acquired by the same | 
					
						
							|  |  |  |     thread more than once.  It keeps a reference count of the number | 
					
						
							|  |  |  |     of times it has been acquired by the same thread.  Each acquire() | 
					
						
							|  |  |  |     call must be matched by a release() call and only the last | 
					
						
							|  |  |  |     release() call actually releases the lock for acquisition by | 
					
						
							|  |  |  |     another thread. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The implementation uses two locks internally: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __mutex is a short term lock used to protect the instance variables | 
					
						
							|  |  |  |     __wait is the lock for which other threads wait | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     A thread intending to acquire both locks should acquire __wait | 
					
						
							|  |  |  |     first. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    The implementation uses two other instance variables, protected by | 
					
						
							|  |  |  |    locking __mutex: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     __tid is the thread ID of the thread that currently has the lock | 
					
						
							|  |  |  |     __count is the number of times the current thread has acquired it | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     When the lock is released, __tid is None and __count is zero. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         """Constructor.  Initialize all instance variables.""" | 
					
						
							|  |  |  |         self.__mutex = thread.allocate_lock() | 
					
						
							|  |  |  |         self.__wait = thread.allocate_lock() | 
					
						
							|  |  |  |         self.__tid = None | 
					
						
							|  |  |  |         self.__count = 0 | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def acquire(self, flag=1): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         """Acquire the lock.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the optional flag argument is false, returns immediately | 
					
						
							|  |  |  |         when it cannot acquire the __wait lock without blocking (it | 
					
						
							|  |  |  |         may still block for a little while in order to acquire the | 
					
						
							|  |  |  |         __mutex lock). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The return value is only relevant when the flag argument is | 
					
						
							|  |  |  |         false; it is 1 if the lock is acquired, 0 if not. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.__mutex.acquire() | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if self.__tid == thread.get_ident(): | 
					
						
							|  |  |  |                 self.__count = self.__count + 1 | 
					
						
							|  |  |  |                 return 1 | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self.__mutex.release() | 
					
						
							|  |  |  |         locked = self.__wait.acquire(flag) | 
					
						
							|  |  |  |         if not flag and not locked: | 
					
						
							|  |  |  |             return 0 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.__mutex.acquire() | 
					
						
							|  |  |  |             assert self.__tid == None | 
					
						
							|  |  |  |             assert self.__count == 0 | 
					
						
							|  |  |  |             self.__tid = thread.get_ident() | 
					
						
							|  |  |  |             self.__count = 1 | 
					
						
							|  |  |  |             return 1 | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self.__mutex.release() | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def release(self): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         """Release the lock.
 | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         If this thread doesn't currently have the lock, an assertion | 
					
						
							|  |  |  |         error is raised. | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         Only allow another thread to acquire the lock when the count | 
					
						
							|  |  |  |         reaches zero after decrementing it. | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         self.__mutex.acquire() | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             assert self.__tid == thread.get_ident() | 
					
						
							|  |  |  |             assert self.__count > 0 | 
					
						
							|  |  |  |             self.__count = self.__count - 1 | 
					
						
							|  |  |  |             if self.__count == 0: | 
					
						
							|  |  |  |                 self.__tid = None | 
					
						
							|  |  |  |                 self.__wait.release() | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self.__mutex.release() | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _testLock(): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     done = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def f2(lock, done=done): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         lock.acquire() | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("f2 running in thread %d\n" % thread.get_ident(), end=' ') | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         lock.release() | 
					
						
							|  |  |  |         done.append(1) | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def f1(lock, f2=f2, done=done): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         lock.acquire() | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("f1 running in thread %d\n" % thread.get_ident(), end=' ') | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             f2(lock) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             lock.release() | 
					
						
							|  |  |  |         done.append(1) | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     lock = Lock() | 
					
						
							|  |  |  |     lock.acquire() | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |     f1(lock)                            # Adds 2 to done | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  |     lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     lock.acquire() | 
					
						
							| 
									
										
										
										
											2004-07-18 05:56:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  |     thread.start_new_thread(f1, (lock,)) # Adds 2 | 
					
						
							|  |  |  |     thread.start_new_thread(f1, (lock, f1)) # Adds 3 | 
					
						
							|  |  |  |     thread.start_new_thread(f2, (lock,)) # Adds 1 | 
					
						
							|  |  |  |     thread.start_new_thread(f2, (lock,)) # Adds 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     lock.release() | 
					
						
							|  |  |  |     import time | 
					
						
							|  |  |  |     while len(done) < 9: | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print(len(done)) | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         time.sleep(0.001) | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |     print(len(done)) | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Now, the Locking metaclass is a piece of cake. | 
					
						
							|  |  |  | # As an example feature, methods whose name begins with exactly one | 
					
						
							|  |  |  | # underscore are not synchronized. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from Meta import MetaClass, MetaHelper, MetaMethodWrapper | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LockingMethodWrapper(MetaMethodWrapper): | 
					
						
							|  |  |  |     def __call__(self, *args, **kw): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         if self.__name__[:1] == '_' and self.__name__[1:] != '_': | 
					
						
							| 
									
										
										
										
											2006-03-17 08:00:19 +00:00
										 |  |  |             return self.func(self.inst, *args, **kw) | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         self.inst.__lock__.acquire() | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2006-03-17 08:00:19 +00:00
										 |  |  |             return self.func(self.inst, *args, **kw) | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         finally: | 
					
						
							|  |  |  |             self.inst.__lock__.release() | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class LockingHelper(MetaHelper): | 
					
						
							|  |  |  |     __methodwrapper__ = LockingMethodWrapper | 
					
						
							|  |  |  |     def __helperinit__(self, formalclass): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         MetaHelper.__helperinit__(self, formalclass) | 
					
						
							|  |  |  |         self.__lock__ = Lock() | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class LockingMetaClass(MetaClass): | 
					
						
							|  |  |  |     __helper__ = LockingHelper | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Locking = LockingMetaClass('Locking', (), {}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _test(): | 
					
						
							|  |  |  |     # For kicks, take away the Locking base class and see it die | 
					
						
							|  |  |  |     class Buffer(Locking): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         def __init__(self, initialsize): | 
					
						
							|  |  |  |             assert initialsize > 0 | 
					
						
							|  |  |  |             self.size = initialsize | 
					
						
							|  |  |  |             self.buffer = [None]*self.size | 
					
						
							|  |  |  |             self.first = self.last = 0 | 
					
						
							|  |  |  |         def put(self, item): | 
					
						
							|  |  |  |             # Do we need to grow the buffer? | 
					
						
							|  |  |  |             if (self.last+1) % self.size != self.first: | 
					
						
							|  |  |  |                 # Insert the new item | 
					
						
							|  |  |  |                 self.buffer[self.last] = item | 
					
						
							|  |  |  |                 self.last = (self.last+1) % self.size | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             # Double the buffer size | 
					
						
							|  |  |  |             # First normalize it so that first==0 and last==size-1 | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |             print("buffer =", self.buffer) | 
					
						
							|  |  |  |             print("first = %d, last = %d, size = %d" % ( | 
					
						
							|  |  |  |                 self.first, self.last, self.size)) | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |             if self.first <= self.last: | 
					
						
							|  |  |  |                 temp = self.buffer[self.first:self.last] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 temp = self.buffer[self.first:] + self.buffer[:self.last] | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |             print("temp =", temp) | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |             self.buffer = temp + [None]*(self.size+1) | 
					
						
							|  |  |  |             self.first = 0 | 
					
						
							|  |  |  |             self.last = self.size-1 | 
					
						
							|  |  |  |             self.size = self.size*2 | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |             print("Buffer size doubled to", self.size) | 
					
						
							|  |  |  |             print("new buffer =", self.buffer) | 
					
						
							|  |  |  |             print("first = %d, last = %d, size = %d" % ( | 
					
						
							|  |  |  |                 self.first, self.last, self.size)) | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |             self.put(item)              # Recursive call to test the locking | 
					
						
							|  |  |  |         def get(self): | 
					
						
							|  |  |  |             # Is the buffer empty? | 
					
						
							|  |  |  |             if self.first == self.last: | 
					
						
							|  |  |  |                 raise EOFError          # Avoid defining a new exception | 
					
						
							|  |  |  |             item = self.buffer[self.first] | 
					
						
							|  |  |  |             self.first = (self.first+1) % self.size | 
					
						
							|  |  |  |             return item | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def producer(buffer, wait, n=1000): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         import time | 
					
						
							|  |  |  |         i = 0 | 
					
						
							|  |  |  |         while i < n: | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |             print("put", i) | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |             buffer.put(i) | 
					
						
							|  |  |  |             i = i+1 | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("Producer: done producing", n, "items") | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         wait.release() | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def consumer(buffer, wait, n=1000): | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         import time | 
					
						
							|  |  |  |         i = 0 | 
					
						
							|  |  |  |         tout = 0.001 | 
					
						
							|  |  |  |         while i < n: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 x = buffer.get() | 
					
						
							|  |  |  |                 if x != i: | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |                     raise AssertionError("get() returned %s, expected %s" % (x, i)) | 
					
						
							|  |  |  |                 print("got", i) | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |                 i = i+1 | 
					
						
							|  |  |  |                 tout = 0.001 | 
					
						
							|  |  |  |             except EOFError: | 
					
						
							|  |  |  |                 time.sleep(tout) | 
					
						
							|  |  |  |                 tout = tout*2 | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |         print("Consumer: done consuming", n, "items") | 
					
						
							| 
									
										
										
										
											1998-09-14 16:44:15 +00:00
										 |  |  |         wait.release() | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     pwait = thread.allocate_lock() | 
					
						
							|  |  |  |     pwait.acquire() | 
					
						
							|  |  |  |     cwait = thread.allocate_lock() | 
					
						
							|  |  |  |     cwait.acquire() | 
					
						
							|  |  |  |     buffer = Buffer(1) | 
					
						
							|  |  |  |     n = 1000 | 
					
						
							|  |  |  |     thread.start_new_thread(consumer, (buffer, cwait, n)) | 
					
						
							|  |  |  |     thread.start_new_thread(producer, (buffer, pwait, n)) | 
					
						
							|  |  |  |     pwait.acquire() | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |     print("Producer done") | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  |     cwait.acquire() | 
					
						
							| 
									
										
										
										
											2007-07-17 20:59:35 +00:00
										 |  |  |     print("All done") | 
					
						
							|  |  |  |     print("buffer size ==", len(buffer.buffer)) | 
					
						
							| 
									
										
										
										
											1997-08-26 00:08:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     _testLock() | 
					
						
							|  |  |  |     _test() |