On Jun 6, 2016, at 08:29, Guido van Rossum email@example.com wrote:
On Sun, Jun 5, 2016 at 10:16 PM, Glyph firstname.lastname@example.org wrote:
On Jun 4, 2016, at 13:25, Ben Darnell email@example.com wrote:
If things are so sensitive to minor changes in timing, doesn't that set the bar impossibly high for interoperability?
The sensitivity is not to changes in timing - i.e. when the wall-clock runs, or ordering of non-deterministically ordered events - but rather to reentrancy - whether certain things complete synchronously while the caller is still on the stack and can depend on them having done so upon return.
The recommended way of writing tests within Twisted these days depends heavily on `.callback´ synchronously resolving a Deferred, which is what adding a call_soon breaks.
That's interesting, and also potentially worrisome (for interop, I'm not saying Twisted is wrong here).
I think asyncio depends on the opposite: that if you add a callback to a Future that's ready it does *not* immediately run. Asyncio's promise is pretty strongly that callbacks are serialized (no callbacks running inside other callbacks). IIRC we experimented with other semantics and found that it was harder to reason about. (IMO if you *know* a Future is ready why add a callback to it rather than just calling the damn thing if that's what you want?)
I don't think Twisted is necessarily right here either. You're absolutely right that it's easier to reason about reentrancy in some cases if you have a might-be-fired-might-not Future vs. the same sort of Deferred. I like the property where you can do:
def test(test_case): a = asynchronously_something() test_case.this_must_not_have_a_result(a) cause_a_to_fire() test_case.assertEqual(test_case.this_must_have_a_result(a), something)
but this is (somewhat) opposed to the fact that call_soon means you never get the nasty surprise where the callback added in the middle of a function gets run before the rest of it does. So I think there are good properties in both cases and given some thought it is probably possible to map between them, but Deferred is lower-level here in the sense that it provides a way to do this both with the event loop and without. You can always call_soon(deferred.callback) but you can't go the other way and force a Future to resolve synchronously - right?