@@ -180,15 +180,22 @@ def acquire(self, *args, **kwargs):
180180 def _release (self , inner_func , * args , ** kwargs ):
181181 # type (typing.Any, typing.Any) -> None
182182
183- start = None
184- if hasattr (self , "_self_acquired_at" ):
185- # _self_acquired_at is only set when the acquire was captured
186- # if it's not set, we're not capturing the release
187- start = self ._self_acquired_at
188- try :
189- del self ._self_acquired_at
190- except AttributeError :
191- LOG .debug ("Failed to delete _self_acquired_at" )
183+ # The underlying threading.Lock class is implemented using C code, and
184+ # it doesn't have the __dict__ attribute. So we can't do
185+ # self.__dict__.pop("_self_acquired_at", None) to remove the attribute.
186+ # Instead, we need to use the following workaround to retrieve and
187+ # remove the attribute.
188+ start = getattr (self , "_self_acquired_at" , None )
189+ try :
190+ # Though it should generally be avoided to call release() from
191+ # multiple threads, it is possible to do so. In that scenario, the
192+ # following statement code will raise an AttributeError. This should
193+ # not be propagated to the caller and to the users. The inner_func
194+ # will raise an RuntimeError as the threads are trying to release()
195+ # and unlocked lock, and the expected behavior is to propagate that.
196+ del self ._self_acquired_at
197+ except AttributeError :
198+ LOG .debug ("Failed to delete _self_acquired_at" )
192199 try :
193200 return inner_func (* args , ** kwargs )
194201 finally :
0 commit comments