0
0
Rubyprogramming~15 mins

Fiber for cooperative concurrency in Ruby - Deep Dive

Choose your learning style9 modes available
Overview - Fiber for cooperative concurrency
What is it?
A Fiber in Ruby is a special kind of lightweight thread that allows a program to pause and resume execution at specific points. Unlike regular threads, Fibers do not run in parallel but cooperate by yielding control back and forth. This lets you write code that can manage multiple tasks without the complexity of full threading.
Why it matters
Fibers exist to help programs handle multiple tasks smoothly without the overhead and complexity of real threads. Without Fibers, managing tasks like waiting for input or processing data in chunks would require complicated code or slow down the program. Fibers make it easier to write efficient, readable code that handles many things seemingly at once.
Where it fits
Before learning Fibers, you should understand basic Ruby programming, methods, and blocks. Knowing about threads and how concurrency works in general helps. After Fibers, you can explore more advanced concurrency tools like Threads, Async gems, or EventMachine for real parallelism or event-driven programming.
Mental Model
Core Idea
Fibers let your program pause and resume tasks cooperatively, handing control back and forth like passing a baton in a relay race.
Think of it like...
Imagine two friends playing catch with a ball. One throws the ball (runs some code), then waits for the other to catch it and throw it back (yield control). They take turns throwing and catching, cooperating to keep the game going smoothly without rushing or interrupting each other.
┌─────────────┐       ┌─────────────┐
│ Fiber A     │       │ Fiber B     │
│ Running     │       │ Paused      │
│ (has ball)  │──────▶│ (waiting)   │
│             │       │             │
│  yield      │       │ resume      │
└─────────────┘       └─────────────┘
       ▲                     │
       │                     ▼
┌─────────────┐       ┌─────────────┐
│ Fiber B     │       │ Fiber A     │
│ Running     │       │ Paused      │
│ (has ball)  │──────▶│ (waiting)   │
│             │       │             │
│  yield      │       │ resume      │
└─────────────┘       └─────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding basic Ruby methods
🤔
Concept: Learn how Ruby methods work and how code executes step-by-step.
Ruby methods are blocks of code you can call by name. When you call a method, Ruby runs its code from top to bottom, then returns a result. This linear flow is the foundation before adding pauses or switching tasks.
Result
You can write and call simple methods that run completely before moving on.
Knowing how Ruby runs code normally helps you see how Fibers change this flow by pausing and resuming.
2
FoundationIntroduction to Ruby blocks and yield
🤔
Concept: Blocks let you pass chunks of code to methods, and yield lets methods run those blocks.
In Ruby, you can pass a block to a method and call it inside with yield. This lets methods run extra code you provide, making your programs more flexible.
Result
You can write methods that run other code inside them, controlling when and how it runs.
Understanding yield is key because Fibers use a similar idea to pause and resume execution.
3
IntermediateCreating and running a Fiber
🤔Before reading on: do you think a Fiber runs immediately when created or only when resumed? Commit to your answer.
Concept: Fibers are created with a block but only start running when you call resume on them.
You create a Fiber by giving it a block of code. This code doesn't run right away. Instead, you call resume to start or continue the Fiber. Inside the Fiber, you can call Fiber.yield to pause and return control.
Result
The Fiber runs until it hits yield, then pauses. Calling resume again continues from where it left off.
Knowing that Fibers start paused and run only when resumed helps you control task switching explicitly.
4
IntermediateSwitching control between Fibers
🤔Before reading on: do you think Fibers switch automatically or only when you tell them to? Commit to your answer.
Concept: Fibers switch control only when you explicitly call resume or yield, making concurrency cooperative.
Fibers do not run in parallel. Instead, they cooperate by yielding control back to the caller or another Fiber. You decide exactly when to switch, so the program stays predictable and easy to follow.
Result
You can write code that switches between tasks smoothly without race conditions or surprises.
Understanding cooperative switching prevents confusion about Fibers running at the same time like threads.
5
IntermediatePassing data between Fiber and caller
🤔Before reading on: do you think Fibers can send and receive data when switching? Commit to your answer.
Concept: Fibers can exchange values with the code that resumes or yields them, enabling communication between tasks.
When you call resume, you can pass a value into the Fiber. When the Fiber yields, it can send a value back. This two-way data flow lets Fibers work together and share information.
Result
Fibers become more powerful by not just pausing but also exchanging data with the rest of the program.
Knowing Fibers communicate data helps you design cooperative tasks that depend on each other.
6
AdvancedUsing Fibers for cooperative concurrency
🤔Before reading on: do you think Fibers can replace threads for all concurrency needs? Commit to your answer.
Concept: Fibers let you manage multiple tasks by switching between them manually, useful for IO or event-driven programs without real parallelism.
By creating multiple Fibers and resuming them in turn, you can simulate multitasking. This is great for programs that wait on input or network calls, letting other tasks run while waiting.
Result
Your program feels responsive and efficient without the complexity of threads or locks.
Understanding Fibers as a tool for cooperative multitasking clarifies when and why to use them instead of threads.
7
ExpertInternal Fiber state and stack switching
🤔Before reading on: do you think Fibers share the same call stack or have separate stacks? Commit to your answer.
Concept: Fibers have their own call stack and execution context, allowing them to pause and resume independently within the same thread.
Under the hood, Ruby Fibers save their current position and local variables on their own stack. When resumed, the Fiber restores this state and continues. This lightweight stack switching is faster than full threads but limited to cooperative control.
Result
Fibers provide efficient context switching without OS thread overhead but require explicit yielding.
Knowing Fibers have separate stacks explains why they can pause and resume without losing state, unlike simple method calls.
Under the Hood
Ruby Fibers work by saving the current execution context, including the call stack and local variables, when Fiber.yield is called. This context is stored separately from the main thread's stack. When Fiber.resume is called, Ruby restores this saved context and continues execution from the paused point. This switching happens entirely in user space without kernel involvement, making it very fast but cooperative.
Why designed this way?
Fibers were designed to provide lightweight concurrency without the complexity and overhead of OS threads. By requiring explicit yielding, Fibers avoid race conditions and locking issues common in preemptive threading. This design fits Ruby's focus on simplicity and developer control, allowing efficient multitasking especially for IO-bound tasks.
Main Thread
┌───────────────────────────────┐
│                               │
│  Calls Fiber.resume           │
│       │                       │
│       ▼                       │
│  ┌───────────────┐            │
│  │ Fiber Stack   │            │
│  │ (saved state) │◀───────────┤
│  └───────────────┘            │
│       ▲                       │
│       │                       │
│  Fiber.yield saves state      │
│  and returns control          │
└───────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do Fibers run in parallel like threads? Commit to yes or no.
Common Belief:Fibers are just like threads and run code at the same time in parallel.
Tap to reveal reality
Reality:Fibers do not run in parallel; they run cooperatively by explicitly yielding control back and forth.
Why it matters:Believing Fibers run in parallel can lead to expecting thread-like behavior, causing bugs when Fibers don't switch automatically.
Quick: Can Fibers be preempted by the system without your code's help? Commit to yes or no.
Common Belief:Fibers can be interrupted and switched by the system anytime, like threads.
Tap to reveal reality
Reality:Fibers only switch when your code calls yield or resume; the system does not preempt them.
Why it matters:Expecting preemption can cause missed task switches and unresponsive programs if yield is not used properly.
Quick: Do Fibers share the same call stack? Commit to yes or no.
Common Belief:All Fibers share the same call stack as the main thread.
Tap to reveal reality
Reality:Each Fiber has its own call stack and local variables, allowing independent execution states.
Why it matters:Misunderstanding this can cause confusion about variable scope and why Fibers can pause and resume without losing data.
Quick: Can Fibers be used for CPU-bound parallel processing? Commit to yes or no.
Common Belief:Fibers can speed up CPU-heavy tasks by running them in parallel.
Tap to reveal reality
Reality:Fibers do not run in parallel and cannot speed up CPU-bound tasks; they are best for IO-bound or cooperative multitasking.
Why it matters:Using Fibers for CPU-heavy work wastes time and resources, leading to poor performance.
Expert Zone
1
Fibers can be nested, meaning a Fiber can resume another Fiber, creating complex cooperative flows that require careful management.
2
Exception handling inside Fibers behaves differently; exceptions propagate to the caller of resume, which can affect error management strategies.
3
Fibers interact subtly with Ruby's garbage collector; keeping references to Fibers alive affects memory and lifecycle, which experts must monitor.
When NOT to use
Fibers are not suitable when true parallelism is needed, such as CPU-bound tasks or multi-core processing. In those cases, use Ruby Threads or external processes. Also, Fibers require explicit yielding, so they are not ideal for libraries or code that cannot control execution flow.
Production Patterns
In production, Fibers are often used in event-driven servers, like web servers or network clients, to handle many connections efficiently without blocking. Frameworks like Async or libraries like EventMachine build on Fibers to provide cooperative concurrency with readable code.
Connections
Coroutines in other languages
Fibers are Ruby's version of coroutines, which exist in languages like Python and Lua.
Understanding Fibers helps grasp coroutines elsewhere, revealing a common pattern of cooperative multitasking across programming languages.
Operating system threads
Fibers differ from OS threads by being cooperative and lightweight, while threads are preemptive and managed by the OS.
Knowing this contrast clarifies when to choose Fibers for simplicity and efficiency versus threads for true parallelism.
Human multitasking and attention switching
Fibers mimic how humans switch attention between tasks deliberately rather than automatically.
This connection shows how cooperative concurrency models real-world task management, helping understand why explicit yielding is needed.
Common Pitfalls
#1Forgetting to call Fiber.yield inside a Fiber, causing it to run to completion without pausing.
Wrong approach:fiber = Fiber.new { puts 'Start'; puts 'End' } fiber.resume fiber.resume
Correct approach:fiber = Fiber.new { puts 'Start'; Fiber.yield; puts 'End' } fiber.resume fiber.resume
Root cause:Misunderstanding that Fibers only pause when Fiber.yield is called, so without it, the Fiber runs fully on first resume.
#2Trying to resume a Fiber that has already finished, causing an error.
Wrong approach:fiber = Fiber.new { puts 'Done' } fiber.resume fiber.resume # Error here
Correct approach:fiber = Fiber.new { puts 'Done' } fiber.resume # Do not resume again after completion
Root cause:Not tracking Fiber state leads to resuming a dead Fiber, which Ruby does not allow.
#3Expecting Fibers to run in parallel and not yielding control, causing unresponsive programs.
Wrong approach:fiber1 = Fiber.new { loop { puts 'A' } } fiber2 = Fiber.new { loop { puts 'B' } } fiber1.resume fiber2.resume
Correct approach:fiber1 = Fiber.new { loop { puts 'A'; Fiber.yield } } fiber2 = Fiber.new { loop { puts 'B'; Fiber.yield } } loop do fiber1.resume fiber2.resume end
Root cause:Expecting automatic switching without explicit yield causes one Fiber to block others.
Key Takeaways
Fibers provide a way to pause and resume code cooperatively within a single thread, enabling lightweight multitasking.
They differ from threads by requiring explicit yielding, which gives the programmer full control over task switching.
Each Fiber has its own call stack and execution context, allowing independent progress without losing state.
Fibers are best suited for IO-bound or event-driven programs, not for CPU-bound parallel processing.
Understanding Fibers helps write efficient, readable concurrent Ruby code without the complexity of full threading.