# Invoke only by a single thread

I wrote a simple class in Python, which controls code invocation, in a multi-threaded environment, with the following logic:

The class' main method, named try_to_do, takes two function pointers as arguments: yes_we_can_fn and no_we_cannot_fn.

In any point in time, only a single thread can invoke code the function passed as yes_we_can_fn argument.

If a specific thread tries to invoke its code, but some other thread is already invoking its code, then the no_we_cannot_fn is invoked instead of yes_we_can_fn.

If there's an exception in the code being executed, it should raise to the calling context.

The code:

from threading import Lock

class MyController():
def __init__(self):
self.locker = Lock()
self.is_active = False

def try_to_do(self, yes_we_can_fn, no_we_cannot_fn):
with self.locker:
if self.is_active:
we_can_do_it = False
else:
we_can_do_it = True
self.is_active = True

try:
if we_can_do_it:
yes_we_can_fn()
else:
no_we_cannot_fn()
finally:
if we_can_do_it:
with self.locker:
self.is_active = False


Usage:

ctl = MyController()

def p1():
from time import sleep
print 'before sleep'
sleep(2)
print 'done'

def p2():
print 'too busy, will try some other time'

ctl.try_to_do(p1, p2)


I'd like to get some reviews: thread safety (maybe I'm missing something?), coding style, etc.

You could avoid the is_active variable by using the lock in a non-blocking manner:

def try_to_do(self, yes_we_can_fn, no_we_cannot_fn):
if self.locker.acquire(False):
try:
yes_we_can_fn()
finally:
self.locker.release()
else:
no_we_cannot_fn()

• That's a great idea. Note that there should also be a try/finally around the no_we_cannot_fn(). – Ron Klein Jan 20 '14 at 19:04
• @RonKlein In your version the finally block does nothing in the case where no_we_cannot_fn() gets called. – Janne Karila Jan 20 '14 at 19:09
• After another look in your suggested code, I see that even if there was as exception while invoking no_we_cannot_fn, the class' state won't get affected. In my (original) code, it would be. So you're absolutely right, there's no need for a try wrapper around no_we_cannot_fn() in your suggestion. – Ron Klein Jan 20 '14 at 21:52