Overview
Integrating Twisted in to the current Chandler architecture will involve the following steps:
1. Add Twisted and its dependencies to the Chandler build process
2. Create a thread wrapper around the Twisted Reactor
3. Define a base class for Chandler code that wishes to be Twisted
4. Start the Twisted thread wrapper on Chandler startup and shut it down on Chandler exit
Chandler Build Integration
Markie and I have begun discussions on how best to accomplish this and a bug has been filed in Bugzilla. However,
we are still in the beginning stages.
Running Twisted in a thread
By placing the Twisted Reactor in a thread, Chandler can continue to run the WXWindows event loop on the main thread.
Classes wishing to execute methods in the Reactor use reactor.callFromThread(method, *args, **kw).
The callFromThread method executes code in the Reactor thread (which is a requirement for proper asynch event handling).
Reactor Thread Source Code:
from twisted.internet import defer, reactor
import threading
from twisted.python import threadable
__author__ ="Brian Kirsch <bkirsch@osafoundation.org>"
#required for using threads with the Reactor
threadable.init()
class ReactorException(Exception):
def __init__(self, *args):
Exception.__init__(self, *args)
class ReactorThread(threading.Thread):
"""Run the Reactor in a Thread to prevent blocking the
Main Thread once reactor.run is called"""
def __init__(self):
threading.Thread.__init__(self)
self._reactorRunning = False
def run(self):
if self._reactorRunning:
raise ReactorException("Reactor Already Running")
self._reactorRunning = True
#call run passing a False flag indicating to the
#reactor not to install sig handlers since sig handlers
#only work on the main thread
reactor.run(False)
def callInReactor(self, callable, *args, **kw):
if self._reactorRunning:
reactor.callFromThread(callable, *args, **kw)
else:
callable(*args, **kw)
def isReactorRunning(self):
return self._reactorRunning
def startReactor(self):
if self._reactorRunning:
raise ReactorException("Reactor Already Running")
threading.Thread.start(self)
reactor.addSystemEventTrigger('after', 'shutdown', self.__reactorShutDown)
def stopReactor(self):
"""may want a way to force thread to join if reactor does not shutdown
properly. The reactor can get in to a recursive loop condition if reactor.stop
placed in the threads join method. This will require further investigation.
"""
if not self._reactorRunning:
raise ReactorException("Reactor Not Running")
reactor.callFromThread(reactor.stop)
def addReactorEventTrigger(self, phase, eventType, callable):
if self._reactorRunning:
reactor.callFromThread(reactor.addSystemEventTrigger, phase, eventType, callable)
else:
reactor.addSystemEventTrigger(phase, eventType, callable)
def __reactorShuttingDown(self):
pass
def __reactorShutDown(self):
"""This method called when the reactor is stopped"""
self._reactorRunning = False
Chandler objects extend TwistedBase? to leverage Twisted
Twisted itself is a complicated enough framework. Developers who understand Twisted will not
want another layer of abstraction over Twisted. So the goal was to put the thinest wrapper possible around Twisted code.
The one special condition that must be captured and handled in Twisted code is Chandler application shutdown. If
code is in the middle of a series of callbacks it must stop the callback chain and perform any clean up required.
The
TwistedBase? class captures the reactor shutdown event and calls its child classes onShutdown method. The
onShutdown method will be called when the Chandler
--
BrianKirsch - 01 Jun 2004