Claudiu
Claudiu

Reputation: 229341

python: elegant way to deal with lock on a variable?

I have code that looks like something like this:

def startSearching(self):
    self.searchingLock.acquire()
    searching = self.searching
    if self.searching:
        self.searchingLock.release()
        self.logger.error("Already searching!")
        return False

    self.searching = True
    self.searchingLock.release()

    #some more init code, then start the thread which
    #constantly checks self.searching to determine when to stop

it's kind of ugly, though. lots of acquires and releases. this looks prettier:

def startSearching(self):
    with self.searchingLock:
        if self.searching:
            self.logger.error("Already searching!")
            return False

        self.searching = True

    #some more init code, then start the thread which
    #constantly checks self.searching to determine when to stop

but this keeps the lock longer than strictly necessary, espcially if self.logger.error takes a while (like if it writes to disk, which it does). is there any middle ground between holding the lock as little as possible but having prettier code?

Upvotes: 7

Views: 4165

Answers (3)

Macke
Macke

Reputation: 25680

How about wrapping the variable & lock in a class:

class LockedVariable(object):
    def __init__(self, value, lock=None):
        self._value = value
        self._lock = lock if lock else threading.RLock()
        self._locked = false:

    @property
    def locked(self):
        return self._locked

    def assign(self, value):
        with self:
            self._value = value

    def release():
        self._locked = False
        return self._lock.release()

    def __enter__(self):
        self._lock.__enter__()
        self._locked = True
        return self._value

    def __exit__(self, *args, **kwargs):
        if self._locked:
            self._locked = False
            return self._lock.__exit__(*args, **kwargs)

And use as this:

locked_dict = LockedVariable({})

with locked_dict as value: 
    value['answer'] = 42

    if locked_dict.locked:
        locked_dict.release()
        print 'eureka! :)'   
        return       

if locked_dict.locked:
    print 'bahh! :('          

Comment:

I sometimes use boost::shared_ptr with a custom deleter to achieve the same thing, i.e. return an unlocked variable that's released when it goes out of scope.

Upvotes: 4

Odomontois
Odomontois

Reputation: 16308

Maybe you need to separate this logic like:

def initSearch(self):
    with self.searchingLock:
        if self.searching : raise SearchingError('AlreadySearching')
        self.searching = True
def startSearching(self):
    try: self.initSearch()
    except SearchingError as error :
        self.logger.error(error.message)
        return False
    #some more init code, then start the thread which
    #constantly checks self.searching to determine when to stop

And additionaly you telling your searchingLock the reason to release it automaticaly.

Upvotes: 6

getekha
getekha

Reputation: 2583

This will save you one "self.searchingLock.release()" Guess it's not very pythonic or anything but it does the job

def startSearching(self):
    self.searchingLock.acquire()
    already_searching = self.searching
    self.searching = True # Since it'll be true in both scenarios 
    self.searchingLock.release()

    if already_searching:
        self.logger.error("Already searching!")

    return not already_searching

Upvotes: 1

Related Questions