Metaclasses, decorators, and synchronization

Jp Calderone exarkun at
Mon Sep 26 06:21:28 CEST 2005

On Sun, 25 Sep 2005 23:30:21 -0400, Victor Ng <crankycoder at> wrote:
>You could do it with a metaclass, but I think that's probably overkill.
>It's not really efficient as it's doing test/set of an RLock all the
>time, but hey - you didn't ask for efficient.  :)

There's a race condition in this version of synchronized which can allow two or more threads to execute the synchronized function simultaneously.

>      1 import threading
>      2
>      3 def synchronized(func):
>      4     def innerMethod(self, *args, **kwargs):
>      5         if not hasattr(self, '_sync_lock'):

Imagine two threads reach the above test at the same time - they both discover there is no RLock protecting this function.  They both entire this suite to create one.

>      6             self._sync_lock = threading.RLock()

Now one of them zooms ahead, creating the RLock and acquiring it on the next line.  The other one finally manages to get some runtime again afterwards and creates another RLock, clobbering the first.

>      7         self._sync_lock.acquire()

Now it proceeds to this point and acquires the newly created RLock.  Woops.  Two threads now think they are allowed to run this function.

>      8         print 'acquired %r' % self._sync_lock
>      9         try:
>     10             return func(self, *args, **kwargs)

And so they do.

>     11         finally:
>     12             self._sync_lock.release()
>     13             print 'released %r' % self._sync_lock

Of course, when the second gets to the finally suite, it will explode, since it will be releasing the same lock the first thread to get here has already released.

>     14     return innerMethod
>     15
>     16 class Foo(object):
>     17     @synchronized
>     18     def mySyncMethod(self):
>     19         print "blah"
>     20
>     21
>     22 f = Foo()
>     23 f.mySyncMethod()

To avoid this race condition, you need to serialize lock creation.  This is exactly what Twisted's implementation does.  You can read that version at <>.
The code is factored somewhat differently: the functionality is presented as pre- and post-execution hooks, and there is function decorator.  The concept is the same, however.


More information about the Python-list mailing list