Aaron Brady
2/29/2008 5:53:00 PM
On Feb 29, 5:52 am, castiro...@gmail.com wrote:
> On Feb 29, 12:55 am, Dennis Lee Bieber <wlfr...@ix.netcom.com> wrote:
>
> > On Thu, 28 Feb 2008 08:09:01 -0800 (PST), castiro...@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 ]
-=-=-=-=-=-=-