> On Oct 18, 2016, at 5:50 AM, Itamar Turner-Trauring <ita...@itamarst.org> > wrote: > > Not been doing much Twisted lately, but have been doing async stuff > elsewhere, and I've learned some useful things.
Thanks for writing these up, Itamar! This sort of reflection is rare and it's always helpful :). > 1. Callbacks should be sync or async, but never > sometimes-one-sometimes-the-other. For details go read > http://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/. > For example, Deferred.addCallback(f) really should never run f() > immediately. This has come up a lot in a compare-and-contrast of Twisted vs. asyncio. I agree that the problems with synchronous callbacks are not insignificant (reentrancy is a degenerate form of preemption, and as we all know preemption is the corrupt wellspring of all bugs). However, the benefit, i.e. consistency of behavior with respect to reentrancy, comes with a cost: tight coupling to an event loop. In asyncio, Future's tight coupling to call_soon is a source of problems; it makes it hard to write a test without setting up an elaborate scheduling trampoline, whereas successResultOf/failureResultOf are quite simple to work with. I think Deferred as it is today is a pretty good compromise between the two positions. On the one hand it is decoupled from the event loop. On the other - and this is important - no Deferred-returning API will ever call your callbacks synchronously. Deferred.addCallback will, of course, but savvy Twisted programmers can (and should) do this, if they have dependent state changes: self.manipulateSomeStateForSetup() d = doSomethingPotentiallySynchronous() self.manipulateSomeStateForProcessing() d.addCallback(completeOperation) As a caller, you can always decide whether you can safely be re-entered or not. In most cases, simply moving the 'addCallback' to the end of the function (a-la Go's "defer", oddly enough) is fine. In more complex cases where you really need to unwind reentrancy completely, you can do your own callLater(0) or callFromThread() from an object with a reference to a reactor. > 3. What happened to '2'? :) > By instrumenting all callbacks it manages, which may or may not > require item #1, Twisted can have a context that automatically follows > callbacks. Node has this and it is extremely useful. > http://fredkschott.com/post/2014/02/conquering-asynchronous-context-with-cls/ > is best summary I've found with a bit of searching. This was _always_ supposed to be the way that Twisted worked, but frankly I just wasn't smart enough to figure it out. This is why twisted.python.context came to exist in the first place; I always wanted to attach it to Deferred somehow. I will watch this talk intently; if #1 really is required to address this, my opinion might change. A PR would be intensely appreciated. -glyph
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python