async function fragments scheduling around awaits

84 views
Skip to first unread message

DoHyung Kim

unread,
Jun 25, 2015, 10:15:48 AM6/25/15
to mi...@dartlang.org
I remember I read a post in this group that fragments from async functions are scheduled using 'dart:async' scheduleMicrotask function. But having searched through the language spec and API doc of 'dart:async', never found which API or an equivalent of which is used to schedule fragments of an async function.

I think such a detail should NOT remain as an implementation detail, and be clearly specified in the language spec. If I am wrong, please correct me.

I plan to override scheduleMicrotask to affect async function execution for some reasons. I hope to know it is legitmate rather than being an ugly hack relying on an implementation detail.

Thanks.

- DH

Lasse R.H. Nielsen

unread,
Jun 25, 2015, 10:33:15 AM6/25/15
to mi...@dartlang.org
On Thu, Jun 25, 2015 at 4:15 PM, DoHyung Kim <dyn...@gmail.com> wrote:
I remember I read a post in this group that fragments from async functions are scheduled using 'dart:async' scheduleMicrotask function.

The fragments, as separated by "await" gaps, are not scheduled in any particular way - they wait for the future they are waiting for to complete, and let that schedule the call that maes them continue when they have a result to continue with.
 
But having searched through the language spec and API doc of 'dart:async', never found which API or an equivalent of which is used to schedule fragments of an async function.

Basically, it's "Future.then" that does the scheduling.
 
I think such a detail should NOT remain as an implementation detail, and be clearly specified in the language spec. If I am wrong, please correct me.

I think you are wrong. :)
Specifying how the async function transformation is implemented in too much detail will prevent other possible implementations.
For example, it's possible to implement an async function with await using a separate stack, and simply block at the await until the future is complete. If you start requiring specific functions to be used, then it may prevent that implementation. 

Overspecifying is sometimes worse than underspecifying in the long run - it forces you to repeat a bad design even if you now know how to do it better (q.v. JavaScript RegExps).

I plan to override scheduleMicrotask to affect async function execution for some reasons. I hope to know it is legitmate rather than being an ugly hack relying on an implementation detail.

You should be fine as long as all the code you use is well-behaved. The dart:async futures do use scheduleMicrotask in the future's zone when they need to postpone a callback. If you run all your code in your own zone, the scheduleMicrotask should get called when necessary.

If you have futures implementations that are not using scheduleMicrotask, or that are avoiding your zone, then it may not work, but that's not an async/await syntax related problem.
It is always possible to exit your zone by doing Zone.ROOT.run(() { some code; });. There is nothing you can do about that. That's usually not the case for well-behaved code.

/L
-- 
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

DoHyung Kim

unread,
Jun 25, 2015, 10:49:18 AM6/25/15
to mi...@dartlang.org
Thanks for enlightening me. I totally misunderstood how async/await works.

But considering it is not specified how async functions are scheduled, am I still safe to override Zone's scheduleMicrotask to affect how async functions are scheduled? As you said, assume I properly run those async functions in a Zone I configure.

2015년 6월 25일 목요일 오후 11시 33분 15초 UTC+9, Lasse Reichstein Holst Nielsen 님의 말:

DoHyung Kim

unread,
Jun 25, 2015, 11:10:13 AM6/25/15
to mi...@dartlang.org
It seems that I need more clarification on my question below.

To my understanding, async functions require creation of Future, however it create one (but no fragment scheduling, I got it). And I got that you (@lrn) said it is ok to override scheduleMicrotask in a Zone to affect scheduling of async functions.

I plan to proceed with what @lrn suggests. But I'm still not sure if I'm safe to do so without any authorative statements somewhere in a spec.

2015년 6월 25일 목요일 오후 11시 49분 18초 UTC+9, DoHyung Kim 님의 말:

Lasse R.H. Nielsen

unread,
Jun 25, 2015, 2:15:01 PM6/25/15
to mi...@dartlang.org
On Thu, Jun 25, 2015 at 5:10 PM, DoHyung Kim <dyn...@gmail.com> wrote:
It seems that I need more clarification on my question below.

To my understanding, async functions require creation of Future, however it create one (but no fragment scheduling, I got it). And I got that you (@lrn) said it is ok to override scheduleMicrotask in a Zone to affect scheduling of async functions.

I plan to proceed with what @lrn suggests. But I'm still not sure if I'm safe to do so without any authorative statements somewhere in a spec.

I'm still not entirely sure what you are trying to achieve.
You can override scheduleMicrotask in a zone, and then any scheduling of microtasks by code running in that zone will be affected. That should be safe - and not in the spec because the spec does not mention zones at all.
It may not affect all futures - some futures are completed by timer events, some by other events. If you want to intercept all resumes after an "await", there is no simple way to do that because the resume can be caused by any event that completes a future.

/L
 

2015년 6월 25일 목요일 오후 11시 49분 18초 UTC+9, DoHyung Kim 님의 말:
Thanks for enlightening me. I totally misunderstood how async/await works.

But considering it is not specified how async functions are scheduled, am I still safe to override Zone's scheduleMicrotask to affect how async functions are scheduled? As you said, assume I properly run those async functions in a Zone I configure.


2015년 6월 25일 목요일 오후 11시 33분 15초 UTC+9, Lasse Reichstein Holst Nielsen 님의 말:


On Thu, Jun 25, 2015 at 4:15 PM, DoHyung Kim <dyn...@gmail.com> wrote:
I remember I read a post in this group that fragments from async functions are scheduled using 'dart:async' scheduleMicrotask function.

The fragments, as separated by "await" gaps, are not scheduled in any particular way - they wait for the future they are waiting for to complete, and let that schedule the call that maes them continue when they have a result to continue with.
 
But having searched through the language spec and API doc of 'dart:async', never found which API or an equivalent of which is used to schedule fragments of an async function.

Basically, it's "Future.then" that does the scheduling.
 
I think such a detail should NOT remain as an implementation detail, and be clearly specified in the language spec. If I am wrong, please correct me.

I think you are wrong. :)
Specifying how the async function transformation is implemented in too much detail will prevent other possible implementations.
For example, it's possible to implement an async function with await using a separate stack, and simply block at the await until the future is complete. If you start requiring specific functions to be used, then it may prevent that implementation. 

Overspecifying is sometimes worse than underspecifying in the long run - it forces you to repeat a bad design even if you now know how to do it better (q.v. JavaScript RegExps).

I plan to override scheduleMicrotask to affect async function execution for some reasons. I hope to know it is legitmate rather than being an ugly hack relying on an implementation detail.

You should be fine as long as all the code you use is well-behaved. The dart:async futures do use scheduleMicrotask in the future's zone when they need to postpone a callback. If you run all your code in your own zone, the scheduleMicrotask should get called when necessary.

If you have futures implementations that are not using scheduleMicrotask, or that are avoiding your zone, then it may not work, but that's not an async/await syntax related problem.
It is always possible to exit your zone by doing Zone.ROOT.run(() { some code; });. There is nothing you can do about that. That's usually not the case for well-behaved code.

/L
-- 
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

DoHyung Kim

unread,
Jun 25, 2015, 9:17:02 PM6/25/15
to mi...@dartlang.org
What I want to do is to control how an async function is scheduled.

https://dartpad.dartlang.org/199592c01dbd8e7d6427

Please see the above snippet. The 'asyncFunction()` has no await inside, but still an async function returning a Future and I have no control over how the body of the asyncFunction() is scheduled. What I want to make sure is to force async functions run after some other tasks already queued for execution.

In the DartPad above, the asyncFunction() is run after print("Out") is executed (actually after main() exited). That means the current async/await implementation does some hidden scheduling for the execution of the function body, and it is certain that the body can't run in parallel with some other codes in the same Isolate. That's engraved in somewhere in the spec.

Actually having await in an async function doesn't change the fact that anyhow the current implementation of async/await do schedule at least the initial fragment of an async function in somehow implementation specific means. See below:

https://dartpad.dartlang.org/3f9e73addf2d81b13add

print("Running") is run after the main exists.

According to what Lasse told me and what I read from the language spec, the current behavior is only single legitmate way of implementing the current spec, but other implementations may do it differently and make the order of executions in the above samples different.

If this is what the spec intends, it is ok. And I can go away from async function for my specific use case and use Future explicitly.


2015년 6월 26일 금요일 오전 3시 15분 1초 UTC+9, Lasse Reichstein Holst Nielsen 님의 말:

Lasse R.H. Nielsen

unread,
Jun 26, 2015, 2:07:53 AM6/26/15
to mi...@dartlang.org
On Fri, Jun 26, 2015 at 3:17 AM, DoHyung Kim <dyn...@gmail.com> wrote:
What I want to do is to control how an async function is scheduled.

The easiest way to do that is to not use an async function, and do the async scheduling manually:

    Future foo() {
       var completer = new Completer();
       scheduleMicrotask(() {
         print("Hello world!");
         completer.complete();
       });
       return completer.future;
     }

That gives total control.
If you want to control someone else's async function, you are already assuming that the function is async, which is an implementation detail that may change in the future.


https://dartpad.dartlang.org/199592c01dbd8e7d6427

Please see the above snippet. The 'asyncFunction()` has no await inside, but still an async function returning a Future and I have no control over how the body of the asyncFunction() is scheduled. What I want to make sure is to force async functions run after some other tasks already queued for execution.

The *earliest* something can be scheduled is as a microtask at the end of the microtask queue [1].
If that's not good enough, you can postpone the function call to a later timer task:
    
   Future asyncResult = new Future(asyncFunction);  

(The "new Future(...)" call will schedule asyncFunction to be called with a new Timer with duration zero, so it will only be executed after all microtasks have been handled - and possibly some other timers or top-level events).

You say the call must wait for something else to complete. If you have a way to see when that something else is done, you could wait for that:

   Future somethingElseResult = ...;
   Future asyncResult = somethingElseResult.then((_) => sayncFunction());

I'm guessing that option isn't available since you are trying more complicated things, but I just want to list it for completeness :)

 

In the DartPad above, the asyncFunction() is run after print("Out") is executed (actually after main() exited). That means the current async/await implementation does some hidden scheduling for the execution of the function body, and it is certain that the body can't run in parallel with some other codes in the same Isolate. That's engraved in somewhere in the spec.

Dart is single-threaded, which is why it can get away with not having any synchronization primitives or complex memory model. So, nothing happens in parallel, except isolates which are isolated from each other's memory.

The body of an async function is executed ... later. The spec just says: "The body of f is scheduled for execution at some future time". All current implementations take that as being scheduled as a microtask, but I agree that the spec is silent. All you can know for certain is that it is *after* the current microtask queue.

(There is an open feature request for making the inital part of the async function synchronous, but it probably won't happen).

Actually having await in an async function doesn't change the fact that anyhow the current implementation of async/await do schedule at least the initial fragment of an async function in somehow implementation specific means. See below:

https://dartpad.dartlang.org/3f9e73addf2d81b13add

print("Running") is run after the main exists.

As currently written, that much is guaranteed. The example linked here will not change its visible behavior no matter how the current spec is implemented. The async functions' body will be executed after main has ended, the only question is how much later.

(It will if the feature request above gets accepted, then "Running" will be printed before "Out", but again, I don't think that'll happen - sadly, it's my request :).
 

According to what Lasse told me and what I read from the language spec, the current behavior is only single legitmate way of implementing the current spec, but other implementations may do it differently and make the order of executions in the above samples different.

The *order* of the prints in the samples above can not be different for an implementation of the specification - the "later", while vague, is not going to be before the current microtask is done. That's one thing we try to be consistent about - async callbacks must not interrupt straight-line code because then it becomes impossible to reason about the code.
 
If this is what the spec intends, it is ok. And I can go away from async function for my specific use case and use Future explicitly.

That will give you the maximal control. 

/L

[1] Well, technically, that's not entirely correct - you can attach a callback to a future which is already scheduled to be completed before the last microtask, and the callback will get called when the future completes, which may be as soon as the next microtask. For this particular case, that is not a problem since the new async function invocation will not depend on an existing future.

Reply all
Reply to author
Forward
0 new messages