I’m trying to create a @synchronized wrapper that creates one Lock per object and makes method calls thread safe. I can only do this if I can access method.im_self of the method in the wrapped method.
class B: def f(self): pass assert inspect.ismethod( B.f ) # OK assert inspect.ismethod( B().f ) # OK print B.f # <unbound method B.f> print B().f # <bound method B.f of <__main__.B instance at 0x7fa2055e67e8>> def synchronized(func): # func is not bound or unbound! print func # <function f at 0x7fa20561b9b0> !!!! assert inspect.ismethod(func) # FAIL # ... allocate one lock per C instance return func class C: @synchronized def f(self): pass
(1) What’s confusing is that the func parameter passed to my decorator changes type before it gets passed into the wrapper-generator. This seem is rude and unnecessary. Why does this happen?
(2) Is there some decorator magic by which I can make method calls to an object mutex-ed (i.e. one lock per object, not per class).
UPDATE: There are many examples of @synchronized(lock) wrappers. However, really what I want is @synchronized(self). I can solve it like this:
def synchronizedMethod(func): def _synchronized(*args, **kw): self = args[0] lock = oneLockPerObject(self) with lock: return func(*args, **kw) return _synchronized
However, because its much more efficient, I’d prefer:
def synchronizedMethod(func): lock = oneLockPerObject(func.im_self) def _synchronized(*args, **kw): with lock: return func(*args, **kw) return _synchronized
Is this possible?
Advertisement
Answer
Go read:
and in particular:
- https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/07-the-missing-synchronized-decorator.md
- https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/08-the-synchronized-decorator-as-context-manager.md
The wrapt
module then contains the @synchronized
decorator described there.
The full implementation is flexible enough to do:
@synchronized # lock bound to function1 def function1(): pass @synchronized # lock bound to function2 def function2(): pass @synchronized # lock bound to Class class Class(object): @synchronized # lock bound to instance of Class def function_im(self): pass @synchronized # lock bound to Class @classmethod def function_cm(cls): pass @synchronized # lock bound to function_sm @staticmethod def function_sm(): pass
Along with context manager like usage as well:
class Object(object): @synchronized def function_im_1(self): pass def function_im_2(self): with synchronized(self): pass
Further information and examples can also be found in:
There is also a conference talk you can watch on how this is implemented at: