What happens to a task that is started and never awaited?¶
Level: 201 · anyone who has seen "Task was destroyed but it is pending"
Stub — a question and its expected answers, not yet a lesson. No program behind this page has been written or run, so nothing on it has been through the check that backs every finished page. The table is what the finished lesson is expected to find, and every cell is unverified until a program prints it. (Not machine-checked here.)
One line: A task started and never awaited is either running with nobody to notice its failure, or waiting forever on something that will never come, and neither is reported by default — which is why structured concurrency ties every task to a scope that must await it, and why the Go library's leaked goroutine and Rust's JoinHandle warnings exist.
The question¶
Spawn a task that fails, and never await it. Where does the failure go? Then spawn a task that waits on a channel nobody will send on, and let the program end. Does anyone say anything? The page runs both in each language, records what was printed and the exit status, and then puts the same tasks inside a structured scope, where the failure surfaces and the leak is impossible.
What each language is expected to say¶
| Expected answer, to be verified by the program | |
|---|---|
| Rust | an unawaited JoinHandle's panic is silent; tokio prints nothing either; thread::scope surfaces it (link for tokio) |
| Go | a goroutine blocked forever is a leak nobody reports; goleak finds it in tests (link, not run) |
| C | a thread blocked on a pipe when main returns simply dies with the process |
| C++ | an unjoined std::thread terminates the program; a detached one is silent |
| Java | an ExecutorService task's exception is stored in the Future and lost if never get; a structured StructuredTaskScope rethrows |
| Python | asyncio logs "Task exception was never retrieved" at garbage collection, and "Task was destroyed but it is pending" |
What the programs have to show¶
- the failing unawaited task per language: what was printed, if anything, and when
- the blocked task at program exit: silent, or the warning
- both inside a structured scope: the failure reaches the caller
See also¶
- Before this: Why can't a normal function call an async one?
- After this: How does a loop await a sequence of values that arrive over time?
- What does a failure on a thread do when nobody is waiting for it?
- How is a running task told to stop, and does it?
- The Go library's A leaked goroutine never ends ↗
- The Rust library's Tasks ↗
- The Rust library's Shutdown and supervision ↗
- Concepts: Leaked tasks · Structured concurrency · Join · Safety failure · Supervision