|
2 | 2 | require "shifty/taggable" |
3 | 3 |
|
4 | 4 | module Shifty |
| 5 | + # Shifty::Worker uses Ruby Fibers for cooperative multitasking. |
| 6 | + # This results in a single-threaded execution model where workers |
| 7 | + # explicitly yield control to one another. This model is chosen for its |
| 8 | + # simplicity and suitability for creating chainable data processing |
| 9 | + # pipelines, where each worker performs a specific task and passes |
| 10 | + # its output to the next worker in the chain. |
5 | 11 | class Worker |
6 | 12 | attr_reader :supply, :tags |
7 | 13 |
|
@@ -50,6 +56,9 @@ def ensure_ready_to_work! |
50 | 56 | end |
51 | 57 |
|
52 | 58 | def workflow |
| 59 | + # This is the core of the worker's execution, managed by a Fiber. |
| 60 | + # The Fiber allows the worker to pause its execution (yield) and |
| 61 | + # be resumed later, enabling cooperative multitasking. |
53 | 62 | @my_little_machine ||= Fiber.new { |
54 | 63 | loop do |
55 | 64 | value = supply&.shift |
@@ -77,6 +86,48 @@ def task_method_exists? |
77 | 86 | def task_method_accepts_a_value? |
78 | 87 | method(:task).arity > 0 |
79 | 88 | end |
| 89 | + |
| 90 | + # ## Concurrency and Thread Safety |
| 91 | + # |
| 92 | + # ### Concurrency Model |
| 93 | + # Shifty utilizes Ruby Fibers to achieve cooperative multitasking. This means |
| 94 | + # that workers voluntarily yield control, allowing other workers to execute. |
| 95 | + # The entire processing pipeline runs within a single thread, which removes |
| 96 | + # an entire class of *preemptive* concurrency hazards (races on shared |
| 97 | + # objects, the need for mutexes around Shifty's own state). |
| 98 | + # |
| 99 | + # Note that single-threading does NOT make the data flowing between workers |
| 100 | + # safe on its own: because each value passes through every worker before the |
| 101 | + # next value begins, a worker that mutates a handed-off value can silently |
| 102 | + # corrupt what downstream workers observe. That hazard is orthogonal to |
| 103 | + # threading and is addressed separately by handoff immutability policies |
| 104 | + # (see docs/planning/handoff-immutability-policies.md). |
| 105 | + # |
| 106 | + # ### Alternatives (Threads/Ractors) |
| 107 | + # Threads and Ractors are not used for parallelism *yet*. Shifty's current |
| 108 | + # goal is an easy-to-use framework for sequential data pipelines, and a |
| 109 | + # single-threaded Fiber model serves that directly without the overhead of |
| 110 | + # mutexes (Threads) or the sharing restrictions of Ractors. This is a |
| 111 | + # scoping decision, not a rejection: the planned move to deeply frozen, |
| 112 | + # shareable handoff values is deliberately Ractor-compatible and lays the |
| 113 | + # groundwork for a future Ractor-backed worker type. (Fibers cannot cross |
| 114 | + # Ractor boundaries, so such a worker would be a distinct type, not a |
| 115 | + # retrofit of this one.) |
| 116 | + # |
| 117 | + # ### Thread Safety |
| 118 | + # `Shifty::Worker` instances, and by extension `Shifty::Gang` or |
| 119 | + # `Shifty::Roster` instances that manage these workers, are **not** |
| 120 | + # inherently thread-safe if they are shared and modified across multiple |
| 121 | + # user-created native threads. If users choose to integrate Shifty components |
| 122 | + # into a multi-threaded application (e.g., processing multiple independent |
| 123 | + # Shifty pipelines in parallel using separate Threads), they are responsible |
| 124 | + # for implementing appropriate synchronization mechanisms (like mutexes) |
| 125 | + # to protect shared Shifty objects from concurrent access and modification. |
| 126 | + # (Handoff immutability policies govern only the *values* passed between |
| 127 | + # workers, not the worker/pipeline objects themselves or their closure |
| 128 | + # state — those remain the user's responsibility across threads.) |
| 129 | + # For typical use cases where a Shifty pipeline is built and run, no |
| 130 | + # external threading is usually involved by the user. |
80 | 131 | end |
81 | 132 |
|
82 | 133 | class WorkerError < StandardError; end |
|
0 commit comments