c# - How do yield and await implement flow of control in .NET? -


as understand yield keyword, if used inside iterator block, returns flow of control calling code, , when iterator called again, picks left off.

also, await not waits callee, returns control caller, pick left off when caller awaits method.

in other words-- there no thread, , "concurrency" of async , await illusion caused clever flow of control, details of concealed syntax.

now, i'm former assembly programmer , i'm familiar instruction pointers, stacks, etc. , how normal flows of control (subroutine, recursion, loops, branches) work. these new constructs-- don't them.

when await reached, how runtime know piece of code should execute next? how know when can resume left off, , how remember where? happens current call stack, saved somehow? if calling method makes other method calls before awaits-- why doesn't stack overwritten? , how on earth runtime work way through in case of exception , stack unwind?

when yield reached, how runtime keep track of point things should picked up? how iterator state preserved?

i'll answer specific questions below, read extensive articles on how designed yield , await.

https://blogs.msdn.microsoft.com/ericlippert/tag/continuation-passing-style/

https://blogs.msdn.microsoft.com/ericlippert/tag/iterators/

https://blogs.msdn.microsoft.com/ericlippert/tag/async/

some of these articles out of date now; code generated different in lot of ways. these give idea of how works.

also, if not understand how lambdas generated closure classes, understand first. won't make heads or tails of async if don't have lambdas down.

when await reached, how runtime know piece of code should execute next?

await generated as:

if (the task not completed)   assign delegate executes remainder of method continuation of task   return caller else   execute remainder of method 

that's it. await fancy return.

how know when can resume left off, , how remember where?

well, how do without await? when method foo calls method bar, somehow remember how middle of foo, locals of activation of foo intact, no matter bar does.

you know how that's done in assembler. activation record foo pushed onto stack; contains values of locals. @ point of call return address in foo pushed onto stack. when bar done, stack pointer , instruction pointer reset need , foo keeps going left off.

the continuation of await same, except record put onto heap obvious reason the sequence of activations not form stack.

the delegate await gives continuation task contains (1) number input lookup table gives instruction pointer need execute next, , (2) values of locals , temporaries.

there additional gear in there; instance, in .net illegal branch middle of try block, can't stick address of code inside try block table. these bookkeeping details. conceptually, activation record moved onto heap.

what happens current call stack, saved somehow?

the relevant information in current activation record never put on stack in first place; allocated off heap get-go. (well, formal parameters passed on stack or in registers , copied heap location when method begins.)

the activation records of callers not stored; await going return them, remember, they'll dealt normally.

note germane difference between simplified continuation passing style of await, , true call-with-current-continuation structures see in languages scheme. in languages entire continuation including continuation callers captured call-cc.

what if calling method makes other method calls before awaits-- why doesn't stack overwritten?

those method calls return, , activation records no longer on stack @ point of await.

and how on earth runtime work way through in case of exception , stack unwind?

in event of uncaught exception, exception caught, stored inside task, , re-thrown when task's result fetched.

remember bookkeeping mentioned before? getting exception semantics right huge pain, let me tell you.

when yield reached, how runtime keep track of point things should picked up? how iterator state preserved?

same way. state of locals moved onto heap, , number representing instruction @ movenext should resume next time called stored along locals.

and again, there's bunch of gear in iterator block make sure exceptions handled correctly.


Comments

Popular posts from this blog

python - Operations inside variables -

Generic Map Parameter java -

arrays - What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it? -