Notes from the second Tulip/Twisted meet-up =========================================== Rackspace, 12/11/2012 Glyph, Brian Warner, David Reid, Duncan McGreggor, others Flow control ------------ - Pause/resume on transport manages data_received. - There's also an API to tell the transport whom to pause when the write calls are overwhelming it: IConsumer.registerProducer(). - There's also something called pipes but it's built on top of the old interface. - Twisted has variations on the basic flow control that I should ignore. Half_close ---------- - This sends an EOF after writing some stuff. - Can't write any more. - Problem with TLS is known (the RFC sadly specifies this behavior). - It must be dynamimcally discoverable whether the transport supports half_close, since the protocol may have to do something different to make up for its missing (e.g. use chunked encoding). Twisted uses an interface check for this and also hasattr(trans, 'halfClose') but a flag (or flag method) is fine too. Constructing transport and protocol ----------------------------------- - There are good reasons for passing a function to the transport construction helper that creates the protocol. (You need these anyway for server-side protocols.) The sequence of events is something like . open socket . create transport (pass it a socket?) . create protocol (pass it nothing) . proto.make_connection(transport); this does: . self.transport = transport . self.connection_made(transport) But it seems okay to skip make_connection and setting .transport. Note that make_connection() is a concrete method on the Protocol implementation base class, while connection_made() is an abstract method on IProtocol. Event Loop ---------- - We discussed the sequence of actions in the event loop. I think in the end we're fine with what Tulip currently does. There are two choices: Tulip: . run ready callbacks until there aren't any left . poll, adding more callbacks to the ready list . add now-ready delayed callbacks to the ready list . go to top Tornado: . run all currently ready callbacks (but not new ones added during this) . (the rest is the same) The difference is that in the Tulip version, CPU bound callbacks that keep adding more to the queue will starve I/O (and yielding to other tasks won't actually cause I/O to happen unless you do e.g. sleep(0.001)). OTOH this may be good because it means there's less overhead if you frequently split operations in two. - I think Twisted does it Tornado style (in a convoluted way :-), but it may not matter, and it's important to leave this vague so implementations can do what's best for their platform. (E.g. if the event loop is built into the OS there are different trade-offs.) System call cost ---------------- - System calls on MacOS are expensive, on Linux they are cheap. - Optimal buffer size ~16K. - Try joining small buffer pieces together, but expect to be tuning this later. Futures ------- - Futures are the most robust API for async stuff, you can check errors etc. So let's do this. - Just don't implement wait(). - For the basics, however, (recv/send, mostly), don't use Futures but use basic callbacks, transport/protocol style. - make_connection() (by any name) can return a Future, it makes it easier to check for errors. - This means revisiting the Tulip proactor branch (IOCP). - The semantics of add_done_callback() are fuzzy about in which thread the callback will be called. (It may be the current thread or another one.) We don't like that. But always inserting a call_soon() indirection may be expensive? Glyph suggested changing the add_done_callback() method name to something else to indicate the changed promise. - Separately, I've been thinking about having two versions of call_soon() -- a more heavy-weight one to be called from other threads that also writes a byte to the self-pipe. Signals ------- - There was a side conversation about signals. A signal handler is similar to another thread, so probably should use (the heavy-weight version of) call_soon() to schedule the real callback and not do anything else. - Glyph vaguely recalled some trickiness with the self-pipe. We should be able to fix this afterwards if necessary, it shouldn't affect the API design.