Threading problems at program exit

Dave Cole djc at object-craft.com.au
Fri Nov 29 18:53:32 EST 2002


The following program demonstrates a problem I am experiencing with
the threading module.  I have an object (A) which holds a lock on
another object (B).  When object A is deleted I want it to release any
lock it may still be holding on object B.

Everything works fine except when the program terminates.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
import sys
import threading

class Locked:
    def __init__(self, lock):
        self._lock = lock
        self._lock_count = 0
        self._thread = None
        self._log = sys.stderr.write
        self._current_thread = threading.currentThread
        self.lock()

    def lock(self):
        self._log('locked in %s\n' % self._current_thread())
        self._lock.acquire()
        self._lock_count += 1

    def unlock(self):
        self._log('unlocked in %s\n' % self._current_thread())
        self._lock_count -= 1
        self._lock.release()

    def __del__(self):
        while self._lock_count:
            self.unlock()

obj = Locked(threading.RLock())
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

When I run this program I get the following:

locked in <_MainThread(MainThread, started)>
unlocked in <_DummyThread(Dummy-1, started daemon)>
Exception exceptions.AssertionError: <exceptions.AssertionError instance at 0x814fbfc> in <bound method Locked.__del__ of <__main__.Locked instance at 0x816e5f4>> ignored

It looks like interpreter is deleting thread objects before objects
which hold locks in those threads.  Is there any kosher way I can
avoid the problem?

The only way I can think to fix it is a bit non-kosher:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
import sys
import threading

class Locked:
    def __init__(self, lock):
        self._lock = lock
        self._lock_count = 0
        self._thread = None
        self._log = sys.stderr.write
        self._current_thread = threading.currentThread
        self.lock()

    def lock(self):
        self._log('locked in %s\n' % self._current_thread())
        self._lock.acquire()
        self._lock_count += 1

    def unlock(self):
        self._log('unlocked in %s\n' % self._current_thread())
        self._lock_count -= 1
        self._lock.release()

    def __del__(self):
        if self._lock_count:
            count, owner = self._lock._release_save()
            self._log('owner was %s\n' % owner)
            owner = self._current_thread()
            self._log('owner is now %s\n' % owner)
            self._lock._acquire_restore((count, owner))
            while self._lock_count:
                self.unlock()

obj = Locked(threading.RLock())
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

This prints the following:

locked in <_MainThread(MainThread, started)>
owner was <_MainThread(MainThread, stopped)>
owner is now <_DummyThread(Dummy-1, started daemon)>
unlocked in <_DummyThread(Dummy-1, started daemon)>

The trouble is that it requires the use of private methods of the
RLock class.

Is there a better or more kosher way?

- Dave

-- 
http://www.object-craft.com.au



More information about the Python-list mailing list