#!/usr/bin/env python ############################################################################# # Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997, 1998, 1999 # All Rights Reserved. # # The software contained on this media is the property of the DSTC Pty # Ltd. Use of this software is strictly in accordance with the # license agreement in the accompanying LICENSE.HTML file. If your # distribution of this software does not contain a LICENSE.HTML file # then you have no rights to use this software in any manner and # should contact DSTC at the address below to determine an appropriate # licensing arrangement. # # DSTC Pty Ltd # Level 7, GP South # Staff House Road # University of Queensland # St Lucia, 4072 # Australia # Tel: +61 7 3365 4310 # Fax: +61 7 3365 4311 # Email: enquiries@dstc.edu.au # # This software is being provided "AS IS" without warranty of any # kind. In no event shall DSTC Pty Ltd be liable for damage of any # kind arising out of or in connection with the use or performance of # this software. # # Project: Fnorb # File: $Source: /cvsroot/fnorb/fnorb/orb/SelectReactor.py,v $ # Version: @(#)$RCSfile: SelectReactor.py,v $ $Revision: 1.18 $ # ############################################################################# """ A simple 'select' Reactor. """ # Standard/built-in modules. import select # Fnorb modules. import Reactor def SelectReactor_init(): """ Initialise the SelectReactor. There can only be one instance of any concrete reactor class per process. """ try: reactor = SelectReactor() except Reactor.Reactor, reactor: pass return reactor class SelectReactor(Reactor.Reactor): """ The SelectReactor. This reactor uses a standard 'select' event loop. """ def __init__(self): """ Constructor. """ # There can be at most one instance of any concrete reactor class per # process if Reactor.Reactor._instance is not None: # A reactor already exists. raise Reactor.Reactor._instance Reactor.Reactor._instance = self # A dictionary of all registered event handlers. All handles in the # SelectReactor are actually just file descriptors. self.__handlers = {} # {Handle: (EventHandler, Mask)} return ######################################################################### # Reactor interface. ######################################################################### def register_handler(self, handler, mask): """ Register an event handler. """ # Get the event handler's underlying I/O handle. handle = handler.handle() # If the handler is already registered then update the mask. try: # Find the event handler associated with the handle. (handler, handler_mask) = self.__handlers[handle] # Update the mask. handler_mask = handler_mask | mask # Otherwise, add the new handler. except KeyError: handler_mask = mask # Do the add/update. self.__handlers[handle] = (handler, handler_mask) return def unregister_handler(self, handler, mask): """ Withdraw a handler's registration. """ # Get the event handler's underlying I/O handle. handle = handler.handle() try: # Find the event handler associated with the handle. (handler, handler_mask) = self.__handlers[handle] # Update the mask. handler_mask = handler_mask & ~mask # If the mask is now zero (ie. the handler is no longer interested # in *any* events), then delete the handler. if handler_mask == 0: del self.__handlers[handle] # Otherwise, just update the handler's mask. else: self.__handlers[handle] = (handler, handler_mask) # Ignore any attempts to un-register a handler that wasn't registerd! except KeyError: pass return def start_event_loop(self, timeout=None): """ Start the event loop. """ self.__is_alive = 1 while self.__is_alive: # Wait for and process a single event. self.do_one_event(timeout) # Close all registered handlers. self.__close_all_handlers() return def stop_event_loop(self): """ Stop the event loop. """ # This variable is checked before each call to 'do_one_event'. self.__is_alive = 0 return def do_one_event(self, timeout=None): """ Dispatch a single event. """ # Get the read/write/exception handles for all registered handlers. (iwtd, owtd, ewtd) = self.__get_handles() # Blocking select (with timeout if specified). if timeout is None: (iwtd, owtd, ewtd) = select.select(iwtd, owtd, ewtd) else: (iwtd, owtd, ewtd) = select.select(iwtd, owtd, ewtd, timeout) # Set up a 'try' block to catch 'KeyError' exceptions (sometimes # handlers may remove themselves before we get to process them e.g # if a handler has both a read and a write event then the processing # of the read event could call 'unregister_handler' before the # write event is processed). try: # Read events. if len(iwtd) > 0: for handle in iwtd: # Find the event handler associated with the handle. (handler, mask) = self.__handlers[handle] # Handler callback. handler.handle_event(Reactor.READ) # Write events. if len(owtd) > 0: for handle in owtd: # Find the event handler associated with the handle. (handler, mask) = self.__handlers[handle] # Handler callback. handler.handle_event(Reactor.WRITE) # Exceptional events. if len(ewtd) > 0: for handle in ewtd: # Find the event handler associated with the handle. (handler, mask) = self.__handlers[handle] # Handler callback. handler.handle_event(Reactor.EXCEPTION) except KeyError: pass return # The following two methods are provided to allow Fnorb events to be # handled in other event loops. def handles(self): """ Return the read/write/exception handles for registered handlers. The return value is a tuple in the form:- ([ReadHandles], [WriteHandles], [ExceptionHandles]) """ return self.__get_handles() def handle_one_event(self, handle, mask): """ Handle a single event. """ # Read event. if mask & Reactor.READ: # Find the event handler associated with the handle. (handler, handler_mask) = self.__handlers[handle] # Handler callback. handler.handle_event(Reactor.READ) # Write event. if mask & Reactor.WRITE: # Find the event handler associated with the handle. (handler, handler_mask) = self.__handlers[handle] # Handler callback. handler.handle_event(Reactor.WRITE) # Exception event. if mask & Reactor.EXCEPTION: # Find the event handler associated with the handle. (handler, handler_mask) = self.__handlers[handle] # Handler callback. handler.handle_event(Reactor.EXCEPTION) return ######################################################################### # Private interface. ######################################################################### def __get_handles(self): """ Return the read/write/exception handles for registered handlers. The return value is a tuple in the form:- ([ReadHandles], [WriteHandles], [ExceptionHandles]) """ iwtd = {} owtd = {} ewtd = {} for handle in self.__handlers.keys(): # Find the event handler associated with the handle. (handler, mask) = self.__handlers[handle] if mask & Reactor.READ: iwtd[handle] = None if mask & Reactor.WRITE: owtd[handle] = None # Listen for errors on all handles. ewtd[handle] = None return (iwtd.keys(), owtd.keys(), ewtd.keys()) def __close_all_handlers(self): """ Close all registered handlers. """ # We work on a copy of the list, 'cos the handlers will most likely # call 'unregister_handler' in their 'close' method which can cause # them to be deleted from the handler dictionary, 'self.__handlers'. for (handler, mask) in self.__handlers.values(): # Handler callback. handler.handle_close() return #############################################################################