When must you pass io_context to boostasiospawn? C
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
boost::asio::spawn() launches a stackful coroutine that runs on an executor. You must pass an io_context (or its executor) when the coroutine is not associated with an existing Asio object that already has an executor. If you spawn from within a handler that is already bound to an executor (like a socket's executor), you can pass that executor or the socket's strand instead. The rule is simple: spawn needs to know which executor to run the coroutine on.
When to Pass io_context
When spawn is called from main() or any non-Asio context, there is no implicit executor. You must provide io_context explicitly.
When You Can Use an Existing Executor
When spawning from within a coroutine, you can pass the yield_context directly. The new coroutine inherits the same executor.
Using a Strand
Passing a strand instead of io_context ensures the coroutine's continuation handlers are serialized with other handlers on that strand. This is critical for thread safety when multiple threads call io.run().
Multiple Threads — Why Strands Matter
Spawn Overloads
The completion token (detached) tells Asio what to do when the coroutine completes. Without it, older versions default to throwing on error.
TCP Server Example
Error Handling
yield[ec] captures the error code instead of throwing. This is the preferred pattern for performance-sensitive code.
Common Pitfalls
- Forgetting to call
io.run(): Coroutines spawned on anio_contextdo not run untilio.run()is called. The program exits immediately without it. - Spawning on a destroyed
io_context: If theio_contextgoes out of scope while coroutines are running, behavior is undefined. Keep theio_contextalive until all work is done. - No strand with multiple threads: Spawning directly on
io_contextwith multiple threads callingio.run()causes data races in coroutine continuations. Usemake_strand()when sharing state. - Mixing stackful and stackless coroutines:
boost::asio::spawncreates stackful coroutines (Boost.Context). C++20co_awaitcreates stackless coroutines. They use different mechanisms and should not be confused. - Large stack allocation: Each stackful coroutine allocates a stack (default 64KB-1MB depending on platform). Spawning thousands of coroutines can consume significant memory. Use
boost::asio::spawnwith a custom stack allocator for high-concurrency scenarios.
Summary
- Pass
io_contexttospawnwhen there is no existing executor (e.g., frommain()) - Pass a
strandfor thread-safe coroutine execution with multiple threads - Pass
yield_contextfrom a parent coroutine to inherit its executor - Use
yield[ec]for error code-based error handling instead of exceptions - Always use strands when coroutines share mutable state across threads
- Each stackful coroutine allocates its own stack — be mindful of memory with many coroutines
Related reading
- When or why should I use a Mutex over an RwLock?
- When should I need to use stdasyncstdlaunchasync, func instead of func?
- When should I use a CompletionService over an ExecutorService?
- When should I use async controllers in ASP.NET MVC?
- When should the STL algorithms be used instead of using your own?
- When should you use a class vs a struct in C++?
- When should I use ConcurrentSkipListMap?
- When should one use RxJava Observable and when simple Callback on Android?
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.