[Twisted-Python] deferred TCP writes

Hi, I have a server that just continously pushes blocks of data to clients after they connect. I'd like to defer transport.write(), and then send another block of data when it completes. All the samples and documentation I hae seen show synchronous writes. Is there an async. version? -- Doug Fort, Consulting Programmer http://www.dougfort.com

I have run into this issue as well when trying to order different writes and make code run _after_ a write was completed. I ended up calling my code using deferToThread and putting in time.sleep calls to make sure things got ordered correctly. Ugly hack though. The ITransport interface docs seem to imply that returning a deferred from a write is not supported: I make no representations about whether calls to me will happen immediately or require returning to a control loop, or whether they will happen in the same or another thread. Consider methods of this class (aside from getPeer) to be 'thrown over the wall', to happen at some indeterminate time. I echo the question: why doesn't write return a deferred? Surely there are times you want to trigger events on the completion of a write. Brian Brian Granger Santa Clara University ellisonbg@gmail.com

On 4/18/06, Brian Granger <bgranger@scu.edu> wrote:
The way you do this is to hook up a producer to the transport. Hopefully there are some references to this in the API docs: search for "producer" and "consumer". -- Christopher Armstrong International Man of Twistery http://radix.twistedmatrix.com/ http://twistedmatrix.com/ http://canonical.com/

On Tue, 2006-04-18 at 10:44 -0700, Brian Granger wrote:
Eeeeew. Don't do that.
This is (more or less) what the producer API is for. Register a IConsumer with transport.registerProducer and you will get callbacks telling you when the transport buffer becomes empty or full. Returning Deferred from every transport.write would make everything rather slow... a wrapper that does this could be probably be written using the producer/consumer API.

On 4/18/06, Itamar Shtull-Trauring <itamar@itamarst.org> wrote:
I though my hack would get a good response :)
Fantastic, I wasn't aware of this API. I will defintely use this approach from now on. Thanks for the pointer. Brian -- Brian Granger Santa Clara University ellisonbg@gmail.com

On Tue, 18 Apr 2006 10:44:22 -0700, Brian Granger <bgranger@scu.edu> wrote:
Some folks have already answered with the primary reason, which is to say, efficiency. While Twisted definitely trades performance for flexibility in a number of places where reasonable, 'write()' is one of those things that just absolutely has to be as fast as possible, because many things which are highly performance-critical call it. Creating Deferreds could be 100x faster and it would still be too slow for this case. There is anothe reason, though: returning a deferred from "write()" implies to the user that when the deferred fires, the bytes have been "written", e.g. the other end has received them. In cases where .write() blocks, most naive programmers -- and yes, I say this having once been a "naive programmer" myself -- assume that the other end has not only received the bytes, it has processed them. This is wrong, and can (and often does) have unfortunate consequences. It sounds like you want control over excessive in-memory buffering, which is a pretty reasonable thing to want, and the IConsumer/IProducer interfaces you have already been directed to should provide that to you quite satisfactorily. The reality is that when a 'write' completes and returns to userland, your bytes are somewhere between your kernel, your router, your cable modem, your ISP's cable modem, your ISP's internal routers, the backbone (...) your destination's kernel, and your destination's application. You have no idea where they are, really, and neither does Twisted. It just hides one more distinction from you: Twisted's buffer to your OS's buffer.

I have run into this issue as well when trying to order different writes and make code run _after_ a write was completed. I ended up calling my code using deferToThread and putting in time.sleep calls to make sure things got ordered correctly. Ugly hack though. The ITransport interface docs seem to imply that returning a deferred from a write is not supported: I make no representations about whether calls to me will happen immediately or require returning to a control loop, or whether they will happen in the same or another thread. Consider methods of this class (aside from getPeer) to be 'thrown over the wall', to happen at some indeterminate time. I echo the question: why doesn't write return a deferred? Surely there are times you want to trigger events on the completion of a write. Brian Brian Granger Santa Clara University ellisonbg@gmail.com

On 4/18/06, Brian Granger <bgranger@scu.edu> wrote:
The way you do this is to hook up a producer to the transport. Hopefully there are some references to this in the API docs: search for "producer" and "consumer". -- Christopher Armstrong International Man of Twistery http://radix.twistedmatrix.com/ http://twistedmatrix.com/ http://canonical.com/

On Tue, 2006-04-18 at 10:44 -0700, Brian Granger wrote:
Eeeeew. Don't do that.
This is (more or less) what the producer API is for. Register a IConsumer with transport.registerProducer and you will get callbacks telling you when the transport buffer becomes empty or full. Returning Deferred from every transport.write would make everything rather slow... a wrapper that does this could be probably be written using the producer/consumer API.

On 4/18/06, Itamar Shtull-Trauring <itamar@itamarst.org> wrote:
I though my hack would get a good response :)
Fantastic, I wasn't aware of this API. I will defintely use this approach from now on. Thanks for the pointer. Brian -- Brian Granger Santa Clara University ellisonbg@gmail.com

On Tue, 18 Apr 2006 10:44:22 -0700, Brian Granger <bgranger@scu.edu> wrote:
Some folks have already answered with the primary reason, which is to say, efficiency. While Twisted definitely trades performance for flexibility in a number of places where reasonable, 'write()' is one of those things that just absolutely has to be as fast as possible, because many things which are highly performance-critical call it. Creating Deferreds could be 100x faster and it would still be too slow for this case. There is anothe reason, though: returning a deferred from "write()" implies to the user that when the deferred fires, the bytes have been "written", e.g. the other end has received them. In cases where .write() blocks, most naive programmers -- and yes, I say this having once been a "naive programmer" myself -- assume that the other end has not only received the bytes, it has processed them. This is wrong, and can (and often does) have unfortunate consequences. It sounds like you want control over excessive in-memory buffering, which is a pretty reasonable thing to want, and the IConsumer/IProducer interfaces you have already been directed to should provide that to you quite satisfactorily. The reality is that when a 'write' completes and returns to userland, your bytes are somewhere between your kernel, your router, your cable modem, your ISP's cable modem, your ISP's internal routers, the backbone (...) your destination's kernel, and your destination's application. You have no idea where they are, really, and neither does Twisted. It just hides one more distinction from you: Twisted's buffer to your OS's buffer.
participants (5)
-
Brian Granger
-
Christopher Armstrong
-
Doug Fort, Consulting Programmer
-
glyph@divmod.com
-
Itamar Shtull-Trauring