[Twisted-Python] Twisted Advice
Jean-Paul Calderone
exarkun at divmod.com
Wed Apr 16 09:47:40 EDT 2008
On Wed, 16 Apr 2008 13:22:58 +0100, Paul Wilson <paulalexwilson at gmail.com> wrote:
>Hi!
>
>I am researching technologies for building an application development
>framework with Python, and have been interested in Twisted to manage
>the network communication side of things. However, I read something
>yesterday that appears to undermine the applicability of twisted for
>my work. I've done quite a bit of reading around the subject since,
>and have ended up confusing myself!
>
>A remote client accesses my server to get instructions on how to
>progress a customer "interaction." The remote client is itself a
>server driven by a telephone call and keytone input. The remote client
>could be servicing N calls (channels) simultaneously, and thus my
>server needs to support N simultaneous TCP connections. Pretty
>standard stuff.
>
>I would like to provide an environment where developers can write call
>servicing applications with no knowledge of the network. I had rested
>on a coroutine approach, whereby a developer could write something
>like this:
>
> response, events = (yield getKeyPress(SomePromptAnnouncement))
>
Just to clarify - this looks like a generator rather than a coroutine.
>With the response, the developer can do an undefined number of things,
>taking an undefined amount of time. During this time, management
>events can arrive on a separate port, to which the developer gets
>knowledge of through the yield statement.
>
>On the other side of this generator is a scheduler which takes
>instructions such as "getKeyPress" and passes it through twisted to
>the remote client, such that it can play the announcement and get a
>keypress. The scheduler gets the response and sends to back to the
>generator, along with any events that have arrived on this separate
>port.
>
>When a call/connection arrives, the client will send an opening
>line(s) to which I was specify the correct LineRecieved method. This
>will trigger some scheduler code defined elsewhere via a deferred,
>which will prompt the developer's code for some instructions, such as
>"PlayPrompt". Am I correct in thinking that while a developer's code
>is executing, all other connections are paused, and that the twisted
>server will not accept new connections until it returns?
Yes, that's correct. Twisted is single-threaded - it invokes event
handlers in the same thread it uses to monitor event sources. As long
as an event handler is running, event sources are not monitored for
events and no other handlers will be invoked.
>
>My original assumption was that Twisted would spawn a new thread
>within which the scheduler would be set to run to manage the
>communication for the duration of the customer call/interaction.
>However, the approach taken by twisted is that if the developer's code
>got itself in a muddle or infinite loop or took a very long time
>accessing a database, the rest of the application would just be
>frozen.
Generally speaking, Twisted will only create a thread when application
code asks it to.
>
>I wrote a quick test app that defers to a function that sleeps then
>returns its response line. During the 20 seconds that the test server
>took to return a line, the server would not accept any other requests
>until the sleep function ended.
The "defers to a function" language is a bit confusing. However, your
conclusion sounds right. If you block the reactor thread, nothing else
will happen.
>Clearly, I must be missing something; I have designed my application
>upon the wrong axis. If so, I have some misunderstanding as to how to
>properly structure an application like this upon Twisted. Or, twisted
>is a framework whereby the response to a network event is expect to
>arrive immediately.
It's not necessary for responses to arrive immediately. Responses just
need to be obtained without blocking. For example, if getting a response
involves talking to someone else over the network, then Twisted has some
networking APIs that don't block. ;) Likewise, twisted.enterprise lets
you talk to a rdbm (it actually uses threads, but it mostly doesn't show
that to you).
>I have seen "deferToThread" mentioned but I cannot find enough
>documentation to decide whether it's what I need or not. Or perhaps
>callInThread() is what I need?
One of these may be appropriate. You could decide that in your application
framework, user code is always run in a non-reactor thread. This is easily
accomplished: your event handler just needs to invoke user code in a thread
instead of directly. deferToThread lets you run a function in a thread and
gives you a Deferred which will be called back with the return value of the
function when it returns or which will errback if it raises an exception.
callInThread is lower level, not exposing the result of the function.
>
>Any suggestions about this would be very much appreciated. Even
>better, if anybody knows of good documentation/tutorials they can
>point me to, that would be appreciated also.
It sounds like you're looking for deferToThread or one of its friends.
Note however that just throwing user code into a thread doesn't mean
programmers can be oblivious to their environment. It simply trades the
need to pay attention to when you block and for how long for the more
complex task of paying attention to thread safety. If each task is
isolated from all others, this may be a good trade-off to make, but if
there's shared state it may not be.
Jean-Paul
More information about the Twisted-Python
mailing list