Thread-safe way to prevent decorator from being nested

M

Michael

I'm writing a decorator that I never want to be nested. Following from the answer on my StackOverflow question (http://stackoverflow.com/a/16905779/106244), I've adapted it to the following.

Can anyone spot any issues with this? It'll be run in a multi-threaded environment serving Django requests and also be a part of Celery tasks.


import threading
from contextlib import contextmanager
from functools import wraps

thread_safe_globals = threading.local()

@contextmanager
def flag():
thread_safe_globals._within_special_context = True
try:
yield
finally:
thread_safe_globals._within_special_context = False

def within_special_wrapper():
try:
return thread_safe_globals._within_special_context
except AttributeError:
return False

def my_special_wrapper(f):
@wraps(f)
def internal(*args, **kwargs):
if not within_special_wrapper():
with flag():
f(*args, **kwargs)
else:
raise Exception("No nested calls!")
return internal

@my_special_wrapper
def foo():
print(within_special_wrapper())
bar()
print('Success!')

@my_special_wrapper
def bar():
pass

foo()
 
P

Peter Otten

Michael said:
I'm writing a decorator that I never want to be nested. Following from the
answer on my StackOverflow question
(http://stackoverflow.com/a/16905779/106244), I've adapted it to the
following.

Can anyone spot any issues with this? It'll be run in a multi-threaded
environment serving Django requests and also be a part of Celery tasks.

I'm not sure I understand what you are trying to do, but this
if not within_special_wrapper():
with flag():

looks suspiciously like race condition.
thread_safe_globals = threading.local()

I'm not an expert in the area, but I think you need a lock, something like

class NestingError(Exception):
pass

nest_lock = threading.Lock()

def my_special_wrapper(f):
@wraps(f)
def internal(*args, **kwargs):
if nest_lock.acquire(False): # non-blocking
try:
f(*args, **kwargs)
finally:
nest_lock.release()
else:
raise NestingError
return internal
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top