[threading] Add a coop backend. This paves the way to introduce cooperativeness into the runtime.
Coop requires the following set of rules:
-blocking code must be wrapped with MONO_PREPARE_BLOCKING / MONO_FINISH_BLOCKING
Must not read managed refs read/writes inside the block
Managed memory (i.e. inside a byte[]) must be previously pinned[1]
-state escaping transitions (lazy thunks and others sneaky n2m transitions) must be wrapped with MONO_PREPARE_RESET_BLOCKING / MONO_FINISH_RESET_BLOCKING
This lands the thread in runnable state, so be careful to keep it suspendable by following the target environment rules.
The set of rules for what runtime code can/can't are still in flux, but here's the tentative list:
== On Wrapping syscall/waits
- Wrap it on the callee. We can't necessarily know in which context the syscall will be called. This will reduce reentrancy issues as blocking is not reentrant.
- Don't go blocking while holding locks as this will certainly deadlock.
- Avoid allocating memory while holding locks.
- Annotate blocking restrictions in the comments in the same way we do it for locking.
== On aborting blocking
- Avoid it as much as possible as it might lead to unbound pauses.
- Make sure you can always restore back to blocking.
[1] Since we conservatively scan the native stack, acquiring the ref outside of the block is enough for now.