def complete_io(self, iocb, msg): """Called by a handler to return data to the client.""" if _debug: IOQController._debug("complete_io %r %r", iocb, msg) # check to see if it is completing the active one if iocb is not self.active_iocb: raise RuntimeError, "not the current iocb" # normal completion IOController.complete_io(self, iocb, msg) # no longer an active iocb self.active_iocb = None # check to see if we should wait a bit if self.wait_time: # change our state self.state = CTRL_WAITING # schedule a call in the future task = FunctionTask(IOQController._wait_trigger, self) task.install_task(_time() + self.wait_time) else: # change our state self.state = CTRL_IDLE # look for more to do deferred(IOQController._trigger, self)
def process_io(self, iocb): if _debug: SomethingController._debug("process_io %r", iocb) # simulate taking some time to complete this request task_delta = random.random() * 3.0 print("{}, {}, {:4.2f}s".format(iocb.args, iocb.kwargs, task_delta)) task = FunctionTask(self.complete_io, iocb, True) task.install_task(delta=task_delta) if _debug: SomethingController._debug(" - task: %r", task)
def set_timeout(self, delay, err=TimeoutError): """Called to set a transaction timer.""" if _debug: IOCB._debug("set_timeout(%d) %r err=%r", self.ioID, delay, err) # if one has already been created, cancel it if self.ioTimeout: self.ioTimeout.suspend_task() else: self.ioTimeout = FunctionTask(self.abort, err) # (re)schedule it self.ioTimeout.install_task(_time() + delay)
def test_function_task_immediate(self): if _debug: TestTimeMachine._debug("test_function_task_immediate") global sample_task_function_called # create a function task ft = FunctionTask(sample_task_function) sample_task_function_called = [] # reset the time machine, install the task, let it run reset_time_machine() ft.install_task(0.0) run_time_machine(60.0) # function called, 60 seconds have passed assert almost_equal(sample_task_function_called, [0.0]) assert time_machine.current_time == 60.0
def test_function_task_immediate(self): if _debug: TestTimeMachine._debug("test_function_task_immediate") global sample_task_function_called # create a function task ft = FunctionTask(sample_task_function) sample_task_function_called = 0 # reset the time machine, install the task, let it run reset_time_machine() ft.install_task(0.0) run_time_machine(60.0) # function called, no time has passed assert sample_task_function_called == 1 assert time_machine.current_time == 0.0
def test_function_task_delay(self): if _debug: TestTimeMachine._debug("test_function_task_delay") global sample_task_function_called sample_delay = 10.0 # create a function task ft = FunctionTask(sample_task_function) sample_task_function_called = [] # reset the time machine, install the task, let it run reset_time_machine() ft.install_task(sample_delay) run_time_machine(60.0) # function called, no time has passed assert almost_equal(sample_task_function_called, [sample_delay]) assert time_machine.current_time == sample_delay
class IOCB(DebugContents): _debugContents = \ ( 'args', 'kwargs' , 'ioState', 'ioResponse-', 'ioError' , 'ioController', 'ioServerRef', 'ioControllerRef', 'ioClientID', 'ioClientAddr' , 'ioComplete', 'ioCallback+', 'ioQueue', 'ioPriority', 'ioTimeout' ) def __init__(self, *args, **kwargs): global _identNext # lock the identity sequence number _identLock.acquire() # generate a unique identity for this block ioID = _identNext _identNext += 1 # release the lock _identLock.release() # debugging postponed until ID acquired if _debug: IOCB._debug("__init__(%d) %r %r", ioID, args, kwargs) # save the ID self.ioID = ioID # save the request parameters self.args = args self.kwargs = kwargs # start with an idle request self.ioState = IDLE self.ioResponse = None self.ioError = None # blocks are bound to a controller self.ioController = None # blocks could reference a local or remote server self.ioServerRef = None self.ioControllerRef = None self.ioClientID = None self.ioClientAddr = None # each block gets a completion event self.ioComplete = threading.Event() self.ioComplete.clear() # applications can set a callback functions self.ioCallback = [] # request is not currently queued self.ioQueue = None # extract the priority if it was given self.ioPriority = kwargs.get('_priority', 0) if '_priority' in kwargs: if _debug: IOCB._debug(" - ioPriority: %r", self.ioPriority) del kwargs['_priority'] # request has no timeout self.ioTimeout = None def add_callback(self, fn, *args, **kwargs): """Pass a function to be called when IO is complete.""" if _debug: IOCB._debug("add_callback(%d) %r %r %r", self.ioID, fn, args, kwargs) # store it self.ioCallback.append((fn, args, kwargs)) # already complete? if self.ioComplete.isSet(): self.trigger() def wait(self, *args): """Wait for the completion event to be set.""" if _debug: IOCB._debug("wait(%d) %r", self.ioID, args) # waiting from a non-daemon thread could be trouble self.ioComplete.wait(*args) def trigger(self): """Set the event and make the callback.""" if _debug: IOCB._debug("trigger(%d)", self.ioID) # if it's queued, remove it from its queue if self.ioQueue: if _debug: IOCB._debug(" - dequeue") self.ioQueue.remove(self) # if there's a timer, cancel it if self.ioTimeout: if _debug: IOCB._debug(" - cancel timeout") self.ioTimeout.suspend_task() # set the completion event self.ioComplete.set() # make the callback for fn, args, kwargs in self.ioCallback: if _debug: IOCB._debug(" - callback fn: %r %r %r", fn, args, kwargs) fn(self, *args, **kwargs) def complete(self, msg): """Called to complete a transaction, usually when process_io has shipped the IOCB off to some other thread or function.""" if _debug: IOCB._debug("complete(%d) %r", self.ioID, msg) if self.ioController: # pass to controller self.ioController.complete_io(self, msg) else: # just fill in the data self.ioState = COMPLETED self.ioResponse = msg self.trigger() def abort(self, err): """Called by a client to abort a transaction.""" if _debug: IOCB._debug("abort(%d) %r", self.ioID, err) if self.ioController: # pass to controller self.ioController.abort_io(self, err) elif self.ioState < COMPLETED: # just fill in the data self.ioState = ABORTED self.ioError = err self.trigger() def set_timeout(self, delay, err=TimeoutError): """Called to set a transaction timer.""" if _debug: IOCB._debug("set_timeout(%d) %r err=%r", self.ioID, delay, err) # if one has already been created, cancel it if self.ioTimeout: self.ioTimeout.suspend_task() else: self.ioTimeout = FunctionTask(self.abort, err) # (re)schedule it self.ioTimeout.install_task(_time() + delay) def __repr__(self): xid = id(self) if (xid < 0): xid += (1L << 32) sname = self.__module__ + '.' + self.__class__.__name__ desc = "(%d)" % (self.ioID,) return '<' + sname + desc + ' instance at 0x%08x' % (xid,) + '>'
class IOCB(DebugContents): _debugContents = \ ( 'args', 'kwargs' , 'ioState', 'ioResponse-', 'ioError' , 'ioController', 'ioServerRef', 'ioControllerRef', 'ioClientID', 'ioClientAddr' , 'ioComplete', 'ioCallback+', 'ioQueue', 'ioPriority', 'ioTimeout' ) def __init__(self, *args, **kwargs): global _identNext # lock the identity sequence number _identLock.acquire() # generate a unique identity for this block ioID = _identNext _identNext += 1 # release the lock _identLock.release() # debugging postponed until ID acquired if _debug: IOCB._debug("__init__(%d) %r %r", ioID, args, kwargs) # save the ID self.ioID = ioID # save the request parameters self.args = args self.kwargs = kwargs # start with an idle request self.ioState = IDLE self.ioResponse = None self.ioError = None # blocks are bound to a controller self.ioController = None # blocks could reference a local or remote server self.ioServerRef = None self.ioControllerRef = None self.ioClientID = None self.ioClientAddr = None # each block gets a completion event self.ioComplete = threading.Event() self.ioComplete.clear() # applications can set a callback functions self.ioCallback = [] # request is not currently queued self.ioQueue = None # extract the priority if it was given self.ioPriority = kwargs.get('_priority', 0) if '_priority' in kwargs: if _debug: IOCB._debug(" - ioPriority: %r", self.ioPriority) del kwargs['_priority'] # request has no timeout self.ioTimeout = None def add_callback(self, fn, *args, **kwargs): """Pass a function to be called when IO is complete.""" if _debug: IOCB._debug("add_callback(%d) %r %r %r", self.ioID, fn, args, kwargs) # store it self.ioCallback.append((fn, args, kwargs)) # already complete? if self.ioComplete.isSet(): self.trigger() def wait(self, *args): """Wait for the completion event to be set.""" if _debug: IOCB._debug("wait(%d) %r", self.ioID, args) # waiting from a non-daemon thread could be trouble self.ioComplete.wait(*args) def trigger(self): """Set the event and make the callback.""" if _debug: IOCB._debug("trigger(%d)", self.ioID) # if it's queued, remove it from its queue if self.ioQueue: if _debug: IOCB._debug(" - dequeue") self.ioQueue.remove(self) # if there's a timer, cancel it if self.ioTimeout: if _debug: IOCB._debug(" - cancel timeout") self.ioTimeout.suspend_task() # set the completion event self.ioComplete.set() # make the callback for fn, args, kwargs in self.ioCallback: if _debug: IOCB._debug(" - callback fn: %r %r %r", fn, args, kwargs) fn(self, *args, **kwargs) def complete(self, msg): """Called to complete a transaction, usually when process_io has shipped the IOCB off to some other thread or function.""" if _debug: IOCB._debug("complete(%d) %r", self.ioID, msg) if self.ioController: # pass to controller self.ioController.complete_io(self, msg) else: # just fill in the data self.ioState = COMPLETED self.ioResponse = msg self.trigger() def abort(self, err): """Called by a client to abort a transaction.""" if _debug: IOCB._debug("abort(%d) %r", self.ioID, err) if self.ioController: # pass to controller self.ioController.abort_io(self, err) elif self.ioState < COMPLETED: # just fill in the data self.ioState = ABORTED self.ioError = err self.trigger() def set_timeout(self, delay, err=TimeoutError): """Called to set a transaction timer.""" if _debug: IOCB._debug("set_timeout(%d) %r err=%r", self.ioID, delay, err) # if one has already been created, cancel it if self.ioTimeout: self.ioTimeout.suspend_task() else: self.ioTimeout = FunctionTask(self.abort, err) # (re)schedule it self.ioTimeout.install_task(_time() + delay) def __repr__(self): xid = id(self) if (xid < 0): xid += (1L << 32) sname = self.__module__ + '.' + self.__class__.__name__ desc = "(%d)" % (self.ioID, ) return '<' + sname + desc + ' instance at 0x%08x' % (xid, ) + '>'