'Process the event queue and execute handlers.
The number of event runner threads is controlled by HANDLER_THREADS.
Stream event handlers will all execute in this thread. Custom event
handlers may be spawned in individual threads.'

def _event_runner(self):

    log.debug(u'Loading    event    runner')
    try:
        while (not self.stop.is_set()):
            event = self.event_queue.get()
            if (event is None):
                continue
            (etype, handler) = event[0:2]
            args = event[2:]
            orig = copy.copy(args[0])
            if (etype == u'stanza'):
                try:
                    handler.run(args[0])
                except Exception as e:
                    error_msg = u'Error    processing    stream    handler:    %s'
                    log.exception(error_msg, handler.name)
                    orig.exception(e)
            elif (etype == u'schedule'):
                name = args[2]
                try:
                    log.debug(u'Scheduled    event:    %s:    %s', name, args[0])
                    handler(*args[0], **args[1])
                except Exception as e:
                    log.exception(u'Error    processing    scheduled    task')
                    self.exception(e)
            elif (etype == u'event'):
                (func, threaded, disposable) = handler
                try:
                    if threaded:
                        x = threading.Thread(name=(u'Event_%s' % str(func)), target=self._threaded_event_wrapper, args=(func, args))
                        x.daemon = self._use_daemons
                        x.start()
                    else:
                        func(*args)
                except Exception as e:
                    error_msg = u'Error    processing    event    handler:    %s'
                    log.exception(error_msg, str(func))
                    if hasattr(orig, u'exception'):
                        orig.exception(e)
                    else:
                        self.exception(e)
            elif (etype == u'quit'):
                log.debug(u'Quitting    event    runner    thread')
                break
    except KeyboardInterrupt:
        log.debug(u'Keyboard    Escape    Detected    in    _event_runner')
        self.event(u'killed', direct=True)
        self.disconnect()
    except SystemExit:
        self.disconnect()
        self.event_queue.put((u'quit', None, None))
    self._end_thread(u'event    runner')
