✅ How do async methods handle references
You can't manually declare references (out and ref arguments) in async methods because of how async works (method returns before any work is done).
It got me questioning the logic of it, because as far as i know, most of the argument passing in c# uses references, and by default passing an instance of a class uses a reference (except for records)
Are all arguments in async methods treated as records and copied into a separate instance to later be used? Or it only affects the manual declaration of pointers? Why?
15 Replies
Arguments are always passed by value, including references. Physically, there's no such thing as a reference. There are pointers, references are a conceptual thing.
Async methods are converted into state machines, with all their context as fields. Ref parameters aren't allowed because ref fields in classes aren't allowed (the state machines are always classes)
And references in classes aren't allowed because
1. the GC can't deal with pointers on the GC heap that don't point exactly at other objects on the GC heap
2. they might point to stack memory, so it's to prevent stack references fro escaping onto objects with a longer lifetime
Well aren't most objects passed use a pointer? I'm not asking why ref parameters aren't allowed in async methods, it's just if i do:
I expect to get a result of 10 (am i wrong here?)
So if i do
Will the output be any different?
No, it won't be different, it'll be 10
a pointer is stored in the state machine
pointing to the heap allocated object
there's no copying involved
outside of the pointer itself
Just a small note, difference between a reference and a pointer is that, a reference is implemented using a pointer, The GC can move the actual location in memory and the reference would point to the new location.
Well, that's nice to know, still, why managed pointers are not allowed while native pointers work just as fine?
because you control the memory of native pointers
Well, let's say you pass a valuetype like
int
by ref
into an async method
What happens now after we return from the method that called the async method?
We'd be pointing to stack memory that shouldn't be usedThe GC only knows how to move the pointers that point to the start of an object. If you had a pointer to the middle of an object (a field reference for example) it wouldn't get moved with the object and you got yourself an invalid pointer
Also, the GC can't tell pointers at the middle of an object count as references to the object
so it's just never allowed
even in cases where it provably would be safe
method can return before the underlying async method is completed? and the memory for the pointer will be freed up/disposed before the operation on it is completed
not disposed or freed up, it's stack memory, it will be used for something else
and you int reference will end up looking at completely different stuff
unpredictably different
UB
Yes, it'll return
When calling Bar, we'll 100% return before BarAsync finishes execution
Ok, i think i understand, because of concurrent execution there may be pointer corruption
so no pointers pointing to the values inside the object are allowed, only pointers to objects themselves
Yes pretty much
Thanks everyone, i learned a lot of new stuff today :)
I guess my question is answered