Hi Doug:
This state machine initializes a system, issues an asynchronous
command
that takes time to complete (say a motor move command) and then waits
for that command to be done before exiting. In the context of a
framework that is calling this state machine, the WAITCMD1 is
repeatedly (polling) while the asynchronous command completes. A
system
can be constructed with lots of little state machines like this and
be
made to look like it is doing many things at once even though the
execution of the program is single threaded.
I understand (pretty much) the Twisted framework is like this and
implmenting event handlers like connectionMade(), etc., is a state
machine, but I'm wondering how to implement what I've outlined above
in
one of Twisted's state event handlers, or inside something like
callLater(). For example, let's say I want to use Twisted to run a
long
running daemon process that has an XMLRPC interface. That XMLRPC
interface is an interface to a state machine inside Twisted that
allows
the caller to change state, or start new state machines, or get the
status of a running state machine. In that case I'm thinking I want a
state machine the runs continuously in the Twisted loop, structured
like
the example above; co-operatively yielding back to Twisted, but
running
non-stop. Something like callLater(1, stateMachine), but non-stop,
without even the 1 second call loop.
If I understand your correctly, I don't think you need to implement a
state machine to simulate concurrency with Twisted - Twisted does a
lot
of that for you. You can think of a Twisted application as a state
machine - the callback being the state and the completion of the
operation and the calling of the callback is the transition. These
callbacks at runtime act like a thread of execution.
def Initialize(...):
# do something
deferred = someFunctionThatReturnsADeferred()
deferred.addCallback(State2)
def State2(...):
# do something
deferred = someFunctionThatReturnsADeferred()
deferred.addCallback(State3)
def State3(someData):
# do something
if someData == 'State4':
deferred = someFunctionThatReturnsADeferred()
func = State4
elif someData == 'State5':
deferred = someOtherFunctionThatReturnsADeferred()
func = State5
...
deferred.addCallback(func)
if __name__ = "__main__":
initialize()
reactor.run()
As for the Twisted loop. Well, you don't really see the Twisted loop
since that is hidden in the reactor. Also you should distinguish
between writing a new protocol and using an existing one.
In the case of XMLRPC, creating the server isn't the problem.
http://twistedmatrix.com/projects/web/documentation/howto/xmlrpc.html
Once a XMLRPC server is created, Twisted will take responsibility for
creating new instances (or threads if you want to see it that way). If
you still need a state machine, then the only hiccup I can see is
sharing state machine (if you really need one) between XMLRPC method
invocations.
Cheers,
Andrew
[Doug Farrell] Thank you very much for your detailed response, I'm kinda
getting it and am going to try out a prototype to make sure I do. As you
mention, Twisted does take care of a lot of the issues dealing with
concurrency. Having written a few threaded applications, I didn't really
want to get into starting my state machine in a thread and then having
to deal with all the cross thread data safety issues.
One thing I'm still a little confused by in your reply is how you're
getting the deferred instance. Is there a particular reason your example
does this:
deferred = someOtherFunctionThatReturnsADeferred()
rather than this:
deferred = defer.deferred()
I just want to understand if there a reason the deferred is being
created in the other function.
Again, thanks for your help and response!
Doug