is there enough information?

castironpi at gmail.com castironpi at gmail.com
Tue Feb 26 08:07:48 EST 2008


> Create a class which will ensure
> turn-taking of events, using a get method with and integer index, by
> waiting for the prior index to complete before starting the next.

from thread import start_new_thread as launch
from threading import Lock
import time
from functools import partial

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 CustThread:
    count= 0
    def __init__( self ):
        CustThread.count+= 1
        self.id= CustThread.count
        self.step= Step()
        self.cont= True
        self.cmd= None
        self.ret= None
    def __repr__( self ):
        return '<CustThread %i>'% self.id

def thloop( thd ):
    while thd.cont:
        with thd.step[1]:
            if not thd.cont: break
            print( 'step 1', end= ' ' )
            thd.ret= thd.cmd+ 1
        with thd.step[3]:
            print( 'step 3' )
            thd.ret= None
        thd.step.complete()

def op100( thd ):
    with thd.step[0]:
        print( 'step 0', end= ' ' )
        thd.cmd= 100
    with thd.step[2]:
        print( 'step 2', end= ' ' )
        ret1= thd.ret
    assert ret1== 101

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

@main
def fmain():
    class Case:
        def __init__( self ):
            self.th1= CustThread()

    while 1:
        print( '===============================' )
        class Case1:
            case= Case()
            launch( thloop, ( case.th1, ) )
            for _ in range( 10 ):
                case.th1.cmd= None
                case.th1.ret= None
                op100( case.th1 )
            case.th1.cont= False
            case.th1.step.open()
        print( 'case complete' )

    while 1: time.sleep( 1 )

'''
Kind of trivial, interesting technique, and general.  Enter with
thd.step[n]: to indicate step n in a process.  with thd.step[m] for m!
= n will block until it exits, and step[n+1] gets the go, or step[0]
with step.complete().  step.open() grants every step, such as in the
case of completion; set termination conditions prior to its call, and
check them in any sensitive loops.

One way to replace magic sequence numbers with meaning is the "Step1
Step2 Step3" style used in namedtuple, but they're only shown bare
here.

Sequence numbers are looked up in a hash; special applications can use
an array, and account for the possibility that with step[10]: may be
called before even step[0].  0- and 1-based options are customizable.
Sequence length may be known at creation time; if so, the Step
instance can be initialized with it; contraindicating cases may
include with step[n] ... with step[n+2], that don't mean the right
thing in a separate instance.

__getitem__ returns a WithObj, calling __enter__ and __exit__ on which
routes to the Step instance, paired with the specified index.  You
could probably cache those.

The example is a 1-SetOp, 2-DoOp, 1-GetReturn triplet.
acquire( False ) / release() pairs release a thread, whether it is
acquired already or not.  _oplock synchronizes _index and lock lookup
operations across an instance.

Kind of cool.
'''



More information about the Python-list mailing list