11

Why are the awaiters (GetAwaiter - to make a class awaitable) structs and not classes. Does it harm to use a class?

public struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion:

http://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/TaskAwaiter.cs#b8626cdb2f1cbe65

public struct YieldAwaiter : ICriticalNotifyCompletion:

http://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/YieldAwaitable.cs#1e1219f924e9e3b7

public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion

http://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/TaskAwaiter.cs#2c48fb3bdfc69022

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Thomas Maierhofer
  • 2,665
  • 18
  • 33
  • 3
    I can only *assume* here, but my guess is that there is no strong *need* for them to be classes, and that in most scenarios they work just fine as immutable structs, avoiding allocations to minimize the GC impact of tasks (although I suspect the awaiters are the tiny tiny tip of that iceberg) – Marc Gravell May 12 '14 at 12:32
  • Does it harm to be a struct? – Ash Burlaczenko May 12 '14 at 12:35
  • if you use struct, it will be copied over and over again. you can't maintain any state information in it and must propagate the continuation back to some object. In case of TaskAwaiter it is propagated back to the Task object, which must take care of synchronization and many other stuff. That makes sense for Task but a new awaiter object is single instanced on GetAwaiter and encapsulates exactly one continuation. So it is perfectly thread safe if you maintain the state in the awaiter itself. – Thomas Maierhofer May 12 '14 at 13:40
  • 1
    Marcs assumption and the accepted answer are correct. Its to avoid memory pressure. Some enumerators are structs for the same reason. – Eric Lippert May 12 '14 at 14:21

1 Answers1

10

The reason behind making awaitables a struct is to avoid unnecessary heap allocations and minimize the memory footprint when the compiler creates the state machine behind the scenes.

This is an implementation detail. It is not necessary for an awaitable to be of type struct and not a class. To strengthen this statement, try compiling an async method in Roslyn in Debug mode, and you'll see the state-machine is a class, where-as compiling in Release will result in a struct. More on that in Why are async state machines classes (and not structs) in Roslyn?

Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 2
    It can be an `interface` as well, as long as it has the `GetAwaiter` method and the return type satisfies the requirements. Actually, because the `GetAwaiter` method is allowed to be an extension method, you can even make "crazy" types like enums _awaitable_, by writing an extension method called `GetAwaiter` with a correct return type. This is just a curiosity, not something I recommend using in practice. – Jeppe Stig Nielsen May 12 '14 at 14:03