feedback requested

castironpi at gmail.com castironpi at gmail.com
Fri Feb 29 12:52:39 EST 2008


On Feb 29, 5:52 am, castiro... at gmail.com wrote:
> On Feb 29, 12:55 am, Dennis Lee Bieber <wlfr... at ix.netcom.com> wrote:
>
> > On Thu, 28 Feb 2008 08:09:01 -0800 (PST), castiro... at gmail.com declaimed
> > the following in comp.lang.python:
>
> > > My goal is to return Deadlock from acquire() if its blocking would
> > > directly create deadlock.  Basic example:
> > [ The safeguard is never worth the cost.]
>
> > I am NOT going to look any further into this line...
>
> I am.

Can anyone tell me if this deadlocks?  Sample test block included,
feel free to shred.  Does not currently permit reentrance.
Simplification of above, denies reentrant calls, eliminates ThreadDesc
class.  Getting a /beep/ on threaded printing in 3.0, none in 2.5.
Got a case too, where 2444 == 2444 but 2444 is not 2444.  Of course
they aren't guaranteed to, but I am testing for identity in the return
from thread.get_ident().  "The 'is' operator compares the identity of
two objects."  "[After] a = 1; b = 1, a and b may or may not refer to
the same object."  Humph.  Anyway, without further ado, direct to you
at no extra cost, thin and narrow for your cut-and-pasting
convenience, drumroll please.
-=-=-=-=-=-=-=-
from __future__ import with_statement
import threading
import thread
from time import sleep
from collections import deque

try:
    from collections import namedtuple
    ThreadLockPair= namedtuple( "ThreadLockPair",
        "thread lock" )
except:
    class ThreadLockPair:
        def __init__( self, thread, lock ):
            self.thread, self.lock= thread, lock

Acquires, Deadlocks, Timesout= \
    object(), object(), object()
resultdict= { Acquires: 'Acquires',
    Deadlocks: 'Deadlocks', Timesout: 'Timesout' }

class Trylock:
    _count= 0
    _alllocks= set()
    _goplock= threading.Lock()
    def __init__( self, *ar, **kwar ):
        self._lock= threading.Lock()
        self._owner= None
        self._waiters= set()
        Trylock._alllocks.add( self )
        self.id= Trylock._count
        Trylock._count+= 1
    def __repr__( self ):
        return '<Trylock %i>'% self.id
    def acquire( self ):
        caller= thread.get_ident()
        with Trylock._goplock:
            if caller in self._waiters:
                return Deadlocks
            if self._cycles():
                return Deadlocks
            self._waiters.add( caller )
        assert self._lock.acquire()
        with Trylock._goplock:
            assert self._owner is None
            self._owner= caller
        return Acquires
    def release( self ):
        with Trylock._goplock:
            self._waiters.remove( self._owner )
            self._owner= None
            self._lock.release()
    def __enter__( self ):
        if self.acquire() is Deadlock:
            raise Exception( 'Deadlock' )
    def __exit__( self, t, v, tb ):
        self.release()
    def _cycles( self, thd= None ):
        lck= self
        if thd is None:
            thd= thread.get_ident()
        edges= [ ThreadLockPair( th, ck )
            for ck in Trylock._alllocks
            for th in ck._waiters ]
        inpair= ThreadLockPair( thd, lck )
        edges.append( inpair )

        d= deque( [ e for e in edges
            if e.lock is lck ] )
        while d:
            cur= d.popleft()
            locks= [ e.lock for e in edges
                if e.thread== cur.thread and
                e.lock is not cur.lock ]
            for ck in locks:
                nexts= [ e for e in edges
                    if ck is e.lock and
                    e.thread!= cur.thread ]
                if inpair in nexts: return True
                d.extend( nexts )
        return False

def main( func ):
    if __name__== '__main__':
        func()

@main
def fmain():
    import random
    locks= [ Trylock() for _ in range( 20 ) ]
    def th1( i ):
        while 1:
            lock= random.choice( locks )
            ret= lock.acquire()
            if ret is not Acquires:
                continue
            print( '%i th lock %s acquire\n'%
                ( i, lock ) ),
            sleep( .0001 )
            lock2= random.choice( locks )
            if lock2.acquire() is Acquires:
                print( '%i th lock2 %s acquire\n'%
                    ( i, lock ) ),
                sleep( .0001 )
                lock2.release()
            lock.release()
            print( '%i th lock %s release\n'%
                ( i, lock ) ),
    ths= [ threading.Thread( target= th1,
        args= ( i, ) ) for i in range( 6 ) ]
    [ th.start() for th in ths ]
-=-=-=-=-=-=-



More information about the Python-list mailing list