Preliminary. ThinkMeta ConcurrentTasks and this documentation are preliminary; everything is subject to change.

Suspending and resuming

Every wait in ConcurrentTasks comes down to two operations of the core: SuspendTask and ResumeTask.

Suspend

When a task has to wait — the semaphore has no permit, the event is not set, the file read is still in flight — it calls SuspendTask. The core switches the thread away from the task's fiber and lets it run another task. The suspended task keeps its fiber and stack exactly as they were.

Right after the switch, the core calls back into the waiter (ISuspendCallback::OnSuspended) with a handle to the suspended task (SuspendedTask). The waiter publishes the handle — puts it on the primitive's wait list — so a later signal can find it. Publishing after the task is off the processor is what makes the handoff safe: whoever takes the handle can resume the task at once.

There are two handle types: TaskHandle names any task (it is what GetCurrentTask returns), SuspendedTask names a task that is suspended right now. A SuspendedTask converts to a TaskHandle, but not the other way round, so only a handle that really comes from a suspension can be passed to ResumeTask — ResumeTask(GetCurrentTask()) does not compile.

Resume

Whoever makes the condition true — a Release, a Set, a completed I/O operation — takes a handle from the wait list and calls ResumeTask. The task becomes runnable again and is queued on the scheduler that suspended it; a thread of that scheduler picks it up and the task continues right after its wait. ResumeTask never blocks and may be called from any thread, including the core's I/O and timer threads.

No lost wake-ups

A signal can arrive between the moment a task decides to wait and the moment it is off the processor. The library's primitives handle this by re-checking their condition after the task is suspended (in OnSuspended) and resuming it right away if the condition has come true meanwhile. You only need to care about this when you write your own primitive — see Custom primitives.

Waiting with a timeout

SuspendTaskFor is SuspendTask with a time limit. If the time runs out first, the core asks the waiter to take the handle back from its wait list (OnTimeout) and resumes the task as timed out. The _s primitives offer this as Wait(timeout); it needs timers.

What this means for your code

  • Waiting is cheap: a fiber switch, no kernel transition, no allocation.
  • Only tasks wait. A plain OS thread cannot be suspended this way; convert it first (Threads as tasks).
  • A resumed task may run on a different thread of its scheduler. Do not hold OS locks or rely on thread-local state across a wait.
  • Blocking OS calls block the thread, not just the task. Prefer the library's asynchronous file and socket operations.

See also