Here. Run it. Download Python 3.0a2.
I'm unlikely to download an alpha release when I haven't even
upgraded to 2.5 (maybe the next three day weekend I'll have time to
track down new versions of all the third party modules I have installed
and then download 2.5)
But I'll meet you half-way
said:
What results do you get on your computer?
Since "with" is a convenience being used to "hide" explicit entry
and exit code, I've hacked in said explicit calls...
-=-=-=-=-=-=-
import threading
##import time
####class WithObj:
#### def __init__( self, enter, exit ):
#### self.__enter__, self.__exit__= enter, exit
##
####class Step:
#### def __init__( self ):
#### self._index= 0
#### self._locks= {}
#### self._opened= False
#### self._oplock= Lock()
#### def __getitem__( self, index ):
#### with self._oplock:
#### lock= self._locks.get( index, None )
#### if None is lock:
#### lock= self._locks[ index ]= Lock()
#### if index!= self._index:
#### assert lock.acquire( False )
#### return WithObj(
#### partial( self.ienter, index ),
#### partial( self.iexit, index ) )
#### def ienter( self, index ):
#### with self._oplock:
#### if self._opened:
#### return self
#### lock= self._locks.get( index )
#### assert lock.acquire()
#### with self._oplock:
#### return self
#### def iexit( self, index, *a ):
#### with self._oplock:
#### self._index+= 1
#### lock= self._locks.get( self._index )
#### if None is not lock:
#### lock.acquire( False )
#### lock.release()
#### def complete( self ):
#### with self._oplock:
#### self._index= 0
#### lock= self._locks.get( 0 )
#### if None is not lock:
#### lock.acquire( False )
#### lock.release()
#### def open( self ):
#### with self._oplock:
#### self._opened= True
#### for lock in self._locks.values():
#### lock.acquire( False )
#### lock.release()
class StepLock(object): #cannot inherit from threading.Condition
#as Condition is a function returning an
#instance of _Condition
def __init__(self, lock=None):
self.condition = threading.Condition(lock)
self.reset()
def reset(self, step=0): #can be used as a "set"
self._step = step
## print "\tRESET: step %s" % self._step,
def nextstep(self, increment=1): #used for delta changes
self._step += increment
## print "\tNEXTSTEP: step %s" % self._step,
def acquire(self, *args):
## tid = int(time.clock() * 100000)
## print "\tACQUIRE %s..." % tid,
self.condition.acquire(*args)
## print "\t%s taken" % tid,
def wait(self, timeout=None):
## tid = int(time.clock() * 100000)
## print "\tWAITING %s..." % tid,
self.condition.wait(timeout)
## print "\t%s done" % tid,
def acquirestep(self, step):
self.acquire()
## print "\tWANT %s, at %s" % (step, self._step),
while self._step != step:
self.notify() #not at our step, so notify some other
self.wait() #waiting thread that it can check
def notify(self):
## print "\tNOTIFY",
self.condition.notify()
def notifyAll(self):
## print "\tNOTIFYALL",
self.condition.notifyAll()
def release(self):
## print "\tRELEASE",
self.condition.release()
def releasestep(self):
## print "\tRELEASESTEP",
self.nextstep()
self.notify()
self.release()
class Worker(object):
count= 0
def __init__( self ):
Worker.count+= 1
self.id= Worker.count
#### self.step= Step()
self.slock = StepLock()
self.running = True
self.cmd = None
self.ret = None
def __repr__( self ):
return '<Worker %s>' % self.id
def threadbody( worker ):
## print "\tworker entered",
while worker.running:
#### with worker.step[1]:
#### if not worker.running: break
#### print( 'step 1', end= ' ' )
#### worker.ret= worker.cmd+ 1
worker.slock.acquirestep(1)
print "\n\n%s step 1\n" % worker
worker.ret = worker.cmd + 1
worker.slock.releasestep()
#### with worker.step[3]:
#### print( 'step 3' )
#### worker.ret= None
worker.slock.acquirestep(3)
print "\n\n%s step 3\n" % worker
worker.ret = None
#### worker.step.complete()
worker.slock.reset()
worker.slock.release() #note -- not a notify
def op100( worker ):
#### with worker.step[0]:
#### print( 'step 0', end= ' ' )
#### worker.cmd= 100
## print "\top100 entered",
worker.slock.acquirestep(0)
print "\n\n%s step 0\n" % worker
worker.cmd = 100
worker.slock.releasestep()
#### with worker.step[2]:
#### print( 'step 2', end= ' ' )
#### ret1= worker.ret
worker.slock.acquirestep(2)
print "\n\n%s step 2\n" % worker
result = worker.ret
worker.slock.releasestep()
print "\n\nOP100 RESULT: %s\n" % result
assert result == 101
####def main( func ):
#### if __name__== '__main__':
#### func()
####
### you defined a decorator just to hide a two-line bit of code?
####@main
def fmain():
#### class Case:
#### def __init__( self ):
#### self.th1= CustThread()
### A class that just adds one more level of attribute
### indirection?
# while True:
# For this test I don't want an infinite loop
#### print( '===============================' )
worktask = Worker()
#### class Case1:
### I smell Java burning... A class containing no methods, just
### a static body?
#### case= Case()
#### launch( thloop, ( worktask, ) )
### better not to mix features of threading and thread (especially as
### threading itself imports thread
thrd = threading.Thread(target = threadbody,
args = (worktask,) )
thrd.setDaemon(True) #kill the thread if main body dies
thrd.start()
for _ in range( 5 ): #only five cycles for proof
print "=" * 40
worktask.cmd = None
worktask.ret = None
worktask.slock.acquire()
worktask.slock.notify() #signal that prepwork is done
worktask.slock.release() #let other threads check
op100( worktask )
worktask.running= False
## thrd.join() #daemon thread should die when we exit
#otherwise deadlock as thread is blocked
#on .acquirestep(0)
#### worktask.step.open()
print( '\n\ncase complete' )
#### while True: time.sleep( 1 )
### the previous "while" loop would never exit, so a second
### "while" loop doing 1 second sleeps after the first will
### never be executed (and would never exit itself)
if __name__ == "__main__":
fmain()
-=-=-=-=-=-=-=-=-
Output, clean, looks like:
-=-=-=-=-=-
========================================
<Worker 1> step 0
<Worker 1> step 1
<Worker 1> step 2
OP100 RESULT: 101
========================================
<Worker 1> step 3
<Worker 1> step 0
<Worker 1> step 1
<Worker 1> step 2
OP100 RESULT: 101
========================================
<Worker 1> step 3
<Worker 1> step 0
<Worker 1> step 1
<Worker 1> step 2
OP100 RESULT: 101
========================================
<Worker 1> step 3
<Worker 1> step 0
<Worker 1> step 1
<Worker 1> step 2
OP100 RESULT: 101
========================================
<Worker 1> step 3
<Worker 1> step 0
<Worker 1> step 1
<Worker 1> step 2
OP100 RESULT: 101
case complete
<Worker 1> step 3
-=-=-=-=-=-
Lots of blank lines as I had forced new-lines to put the core output
stand-alone when running with the commented out wolf-fences (debug
output) as shown next.
===============================
RESET: step 0 ACQUIRE 0... 0 taken NOTIFY RELEASE op100
entered ACQUIRE 5... worker entered ACQUIRE 8... 8 taken WANT
1, at 0 NOTIFY WAITING 12... 5 taken WANT 0, at 0
<Worker 1> step 0
RELEASESTEP NEXTSTEP: step 1 NOTIFY RELEASE ACQUIRE
17... 17 taken WANT 2, at 1 NOTIFY WAITING 21... 12 done
<Worker 1> step 1
RELEASESTEP NEXTSTEP: step 2 NOTIFY RELEASE ACQUIRE
26... 21 done
<Worker 1> step 2
RELEASESTEP NEXTSTEP: step 3 NOTIFY RELEASE
OP100 RESULT: 101
ACQUIRE 31... 26 taken WANT 3, at 3
<Worker 1> step 3
RESET: step 0 RELEASE ACQUIRE 35... 31 taken NOTIFY
RELEASE op100 entered ACQUIRE 38... 35 taken WANT 1, at 0
NOTIFY WAITING 40... 38 taken WANT 0, at 0
<Worker 1> step 0
RELEASESTEP NEXTSTEP: step 1 NOTIFY RELEASE ACQUIRE
45... 45 taken WANT 2, at 1 NOTIFY WAITING 48... 40 done
<Worker 1> step 1
RELEASESTEP NEXTSTEP: step 2 NOTIFY RELEASE ACQUIRE
55... 48 done
<Worker 1> step 2
RELEASESTEP NEXTSTEP: step 3 NOTIFY RELEASE
OP100 RESULT: 101
ACQUIRE 60... 55 taken WANT 3, at 3
<Worker 1> step 3
RESET: step 0 RELEASE ACQUIRE 63... 60 taken NOTIFY
RELEASE op100 entered ACQUIRE 66... 63 taken WANT 1, at 0
NOTIFY WAITING 68... 66 taken WANT 0, at 0
<Worker 1> step 0
RELEASESTEP NEXTSTEP: step 1 NOTIFY RELEASE ACQUIRE
73... 68 done
<Worker 1> step 1
RELEASESTEP NEXTSTEP: step 2 NOTIFY RELEASE ACQUIRE
78... 73 taken WANT 2, at 2
<Worker 1> step 2
RELEASESTEP NEXTSTEP: step 3 NOTIFY RELEASE
OP100 RESULT: 101
ACQUIRE 82... 78 taken WANT 3, at 3
<Worker 1> step 3
RESET: step 0 RELEASE ACQUIRE 86... 82 taken NOTIFY
RELEASE op100 entered ACQUIRE 89... 86 taken WANT 1, at 0
NOTIFY WAITING 91... 89 taken WANT 0, at 0
<Worker 1> step 0
RELEASESTEP NEXTSTEP: step 1 NOTIFY RELEASE ACQUIRE
97... 91 done
<Worker 1> step 1
RELEASESTEP NEXTSTEP: step 2 NOTIFY RELEASE ACQUIRE
101... 97 taken WANT 2, at 2
<Worker 1> step 2
RELEASESTEP NEXTSTEP: step 3 NOTIFY RELEASE
OP100 RESULT: 101
ACQUIRE 105... 101 taken WANT 3, at 3
<Worker 1> step 3
RESET: step 0 RELEASE ACQUIRE 108... 105 taken NOTIFY
RELEASE op100 entered ACQUIRE 111... 108 taken WANT 1, at 0
NOTIFY WAITING 113... 111 taken WANT 0, at 0
<Worker 1> step 0
RELEASESTEP NEXTSTEP: step 1 NOTIFY RELEASE ACQUIRE
119... 113 done
<Worker 1> step 1
RELEASESTEP NEXTSTEP: step 2 NOTIFY RELEASE ACQUIRE
123... 119 taken WANT 2, at 2
<Worker 1> step 2
RELEASESTEP NEXTSTEP: step 3 NOTIFY RELEASE
OP100 RESULT: 101
case complete
123 taken WANT 3, at 3
<Worker 1> step 3
RESET: step 0 RELEASE
-=-=-=-=-=-=-
The WAIT/DONE and ACQUIRESTEP/TAKEN outputs show the clock time
(multiplied by 100000 to get nice integers) so you can see which acquire
or wait actually succeeded. As with:
RESET: step 0 ACQUIRE 0... 0 taken NOTIFY RELEASE op100
entered ACQUIRE 5... worker entered ACQUIRE 8... 8 taken WANT
1, at 0 NOTIFY WAITING 12... 5 taken WANT 0, at 0
<Worker 1> step 0
The RESET is the one executed as part of initializing the StepLock
member of Worker. Even though thrd.start() is invoked, it is likely the
thread does not gain control at that point. That would mean the ACQUIRE
0 is the one in the range(5) loop, along with the NOTIFY and RELEASE,
followed by calling op100. ACQUIRE 5 is a result of the acquirestep(0).
NOW the thread actually gained control (worker entered) and ACQUIRE 8 8
are part of its acquirestep(1). The thread actually gets the condition
lock immediately, tests the current step level against the step is want
and finds a mismatch. The thread then signals for any other thread
waiting on the condition via NOTIFY, and then waits for another thread
to trigger a notify on it (WAIT 12). Control returns to op100 and the
acquirestep(0) ACQUIRE 5 is answered with 5 taken; the step level
matches the desired step, and the code for step 0 is executed...
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/