Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyQt: QMutexLocker not released on exception

Tags:

python

pyqt

I have the following code:

def query(self,query):
  lock = QMutexLocker(self.mutex)
  reply = self.conn.query(query)
  if (re.search("error", reply) != None):
    raise GeneralError("Query error")

  #more code...  
  return reply

Now, if the exception is thrown lock doesnt seem to be deleted, cause the mutex is not released. I can ofcourse do "del lock" everywhere, but that takes away the whole point of qmutexlocker. Does this have to do with Python garbage-collection? If so, that must mean QMutexLocker is not usable at all in Python?

like image 479
Rolle Avatar asked Jun 29 '12 12:06

Rolle


1 Answers

You aren't using the QMutexLocker properly. Use it like a context manager:

from PyQt4.QtCore import QMutex, QMutexLocker

def bad_lock(aLock):
    locker = QMutexLocker(aLock)
    print "Locked"
    raise RuntimeError("Test exception")

    return "Should not get here"

def good_lock(aLock):
    with QMutexLocker(aLock):
        print "Locked"
        raise RuntimeError("Test exception")

    return "Should not get here"

lock = QMutex()

bad_lock(lock)
print lock.tryLock()
# False

lock.unlock()

good_lock(lock)
print lock.tryLock()
# True

In the test, you see in the first example, the lock returns still locked. In the second, when the exception is raised, the context manager releases the lock before leaving the function.

When used in C++, I am sure the QMutexLocker does what it is supposed to, unlocking whenever the scope ends. But in Python, as you have discovered, the garbage collector should not be relied upon to do the unlocking. Context managers via the with statement are perfect for this. You can tell by the way the C++ examples of this class show it simply being created at the top of the function. Whereas the python version has both an __enter__ and __exit__ method.

Lastly, the with context lets you wrap the critical code blocks in a lock to limit the amount of the the lock needs to be in place, so you can do something like this:

def good_lock(aLock):

    # do a bunch of stuff here
    ...

    # critical section
    with QMutexLocker(aLock):
        # do critical stuff here
        ...

    # do other stuff here
    ...

    return True
like image 82
jdi Avatar answered Oct 12 '22 07:10

jdi