ractor-pool 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/lib/ractor-pool/version.rb +1 -1
- data/lib/ractor-pool.rb +34 -8
- data/sig/generated/ractor-pool.rbs +21 -5
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2d8741738f0de76db49cbbbaccc701b687af3df13ce9802205c7661a10519126
|
|
4
|
+
data.tar.gz: 337ce4d26b7735ec2517c2b88e76f2b11bef87124e3581a74772e68fbd3b6e57
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 663d3b28aeab7e0341bbfd5d78540c670fbb74e3cfc89679572093067fc72396028d3b1637c224676d4c52d86aaafd688143e28499217f0b513c7d5035ba42c4
|
|
7
|
+
data.tar.gz: 6a9fc3ec933ece355f9bba48ac1de2980a3c105f0baaf2a13684ab74a54028daec5f2695dee25c4d970d3dc71f7bd066d207977424f8f199c44f8f61e9e6ae85
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
A thread-safe, lock-free pool of Ractor workers with
|
|
6
|
+
A thread-safe, lock-free pool of Ractor workers with coordinator or round-robin dispatch for distributing work.
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
data/lib/ractor-pool/version.rb
CHANGED
data/lib/ractor-pool.rb
CHANGED
|
@@ -6,11 +6,16 @@ require "atomic-ruby/atom"
|
|
|
6
6
|
|
|
7
7
|
Warning.ignore(/Ractor API is experimental/, __FILE__)
|
|
8
8
|
|
|
9
|
-
# A thread-safe, lock-free pool of Ractor workers with
|
|
9
|
+
# A thread-safe, lock-free pool of Ractor workers with coordinator or round-robin dispatch for distributing work.
|
|
10
10
|
#
|
|
11
11
|
# RactorPool manages a fixed number of worker ractors that process work items in parallel.
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
# The +:coordinator+ strategy (the default) routes each work item to whichever worker is
|
|
13
|
+
# currently idle via a dedicated coordinator Ractor, so a slow item on one worker does not
|
|
14
|
+
# block faster items from being picked up by other workers. Use it when work items have
|
|
15
|
+
# variable cost. The +:round_robin+ strategy dispatches work to workers in turn, so a slow
|
|
16
|
+
# item on one worker queues the next item destined for that worker behind it, even if other
|
|
17
|
+
# workers are idle. Use it when work items have uniform cost. Results are collected and
|
|
18
|
+
# passed to a result handler running in a separate thread.
|
|
14
19
|
#
|
|
15
20
|
# @example Basic usage
|
|
16
21
|
# results = []
|
|
@@ -48,16 +53,21 @@ class RactorPool
|
|
|
48
53
|
def message = "cannot queue work after shutdown"
|
|
49
54
|
end
|
|
50
55
|
|
|
56
|
+
STRATEGIES = %i[coordinator round_robin].freeze
|
|
57
|
+
private_constant :STRATEGIES
|
|
58
|
+
|
|
51
59
|
SHUTDOWN = :shutdown
|
|
52
60
|
private_constant :SHUTDOWN
|
|
53
61
|
|
|
54
62
|
# @rbs @size: Integer
|
|
55
63
|
# @rbs @worker: ^(untyped) -> untyped
|
|
64
|
+
# @rbs @strategy: Symbol
|
|
56
65
|
# @rbs @name: String?
|
|
57
66
|
# @rbs @on_error: (^(Exception) -> void | nil)
|
|
58
67
|
# @rbs @result_handler: (^(untyped) -> void | nil)
|
|
59
68
|
# @rbs @in_flight: Atom[Integer]
|
|
60
69
|
# @rbs @shutdown: Atom[bool]
|
|
70
|
+
# @rbs @next_worker_index: Atom[Integer]?
|
|
61
71
|
# @rbs @result_port: Ractor::Port?
|
|
62
72
|
# @rbs @error_port: Ractor::Port?
|
|
63
73
|
# @rbs @coordinator: Ractor?
|
|
@@ -69,12 +79,14 @@ class RactorPool
|
|
|
69
79
|
#
|
|
70
80
|
# @param size [Integer] number of worker ractors to create
|
|
71
81
|
# @param worker [Proc] a shareable proc that processes each work item
|
|
82
|
+
# @param strategy [Symbol] dispatch strategy, either +:coordinator+ (the default) or +:round_robin+
|
|
72
83
|
# @param name [String, nil] optional name for the pool, used in thread/ractor names
|
|
73
84
|
# @param on_error [Proc, nil] optional shareable proc called with the raised exception when a worker raises
|
|
74
85
|
# @yieldparam result [Object] the result returned by the worker proc
|
|
75
86
|
# @return [void]
|
|
76
87
|
# @raise [ArgumentError] if size is not a positive integer
|
|
77
88
|
# @raise [ArgumentError] if worker is not a proc
|
|
89
|
+
# @raise [ArgumentError] if strategy is not +:coordinator+ or +:round_robin+
|
|
78
90
|
# @raise [ArgumentError] if on_error is given but is not a proc
|
|
79
91
|
#
|
|
80
92
|
# @example With result handler
|
|
@@ -83,29 +95,35 @@ class RactorPool
|
|
|
83
95
|
# @example Without result handler
|
|
84
96
|
# pool = RactorPool.new(size: 4, worker: proc { it })
|
|
85
97
|
#
|
|
98
|
+
# @example With round-robin strategy
|
|
99
|
+
# pool = RactorPool.new(size: 4, strategy: :round_robin, worker: proc { it })
|
|
100
|
+
#
|
|
86
101
|
# @example With error handler
|
|
87
102
|
# error_count = Atom.new(0)
|
|
88
103
|
# on_error = proc { error_count.swap { |count| count + 1 } }
|
|
89
104
|
# pool = RactorPool.new(size: 4, worker: proc { raise }, on_error: on_error)
|
|
90
105
|
#
|
|
91
|
-
# @rbs (?size: Integer, worker: ^(untyped) -> untyped, ?name: String?, ?on_error: (^(Exception) -> void | nil)) ?{ (untyped) -> void } -> void
|
|
92
|
-
def initialize(size: Etc.nprocessors, worker:, name: nil, on_error: nil, &result_handler)
|
|
106
|
+
# @rbs (?size: Integer, worker: ^(untyped) -> untyped, ?strategy: Symbol, ?name: String?, ?on_error: (^(Exception) -> void | nil)) ?{ (untyped) -> void } -> void
|
|
107
|
+
def initialize(size: Etc.nprocessors, worker:, strategy: :coordinator, name: nil, on_error: nil, &result_handler)
|
|
93
108
|
raise ArgumentError, "size must be a positive Integer" unless size.is_a?(Integer) && size > 0
|
|
94
109
|
raise ArgumentError, "worker must be a Proc" unless worker.is_a?(Proc)
|
|
110
|
+
raise ArgumentError, "strategy must be one of #{STRATEGIES.inspect}" unless STRATEGIES.include?(strategy)
|
|
95
111
|
raise ArgumentError, "on_error must be a Proc" if on_error && !on_error.is_a?(Proc)
|
|
96
112
|
|
|
97
113
|
@size = size
|
|
98
114
|
@worker = Ractor.shareable_proc(&worker)
|
|
115
|
+
@strategy = strategy
|
|
99
116
|
@name = name
|
|
100
117
|
@on_error = Ractor.shareable_proc(&on_error) if on_error
|
|
101
118
|
@result_handler = result_handler
|
|
102
119
|
|
|
103
120
|
@in_flight = Atom.new(0)
|
|
104
121
|
@shutdown = Atom.new(false)
|
|
122
|
+
@next_worker_index = Atom.new(-1) if size > 1 && strategy == :round_robin
|
|
105
123
|
|
|
106
124
|
@result_port = Ractor::Port.new if result_handler
|
|
107
125
|
@error_port = Ractor::Port.new unless on_error
|
|
108
|
-
@coordinator = start_coordinator if size > 1
|
|
126
|
+
@coordinator = start_coordinator if size > 1 && strategy == :coordinator
|
|
109
127
|
@workers = start_workers
|
|
110
128
|
@collector = start_collector
|
|
111
129
|
@error_collector = start_error_collector
|
|
@@ -132,8 +150,16 @@ class RactorPool
|
|
|
132
150
|
raise EnqueuedWorkAfterShutdownError
|
|
133
151
|
end
|
|
134
152
|
|
|
153
|
+
target = if @coordinator
|
|
154
|
+
@coordinator
|
|
155
|
+
elsif @next_worker_index
|
|
156
|
+
@workers[@next_worker_index.swap { |index| (index + 1) % @size }]
|
|
157
|
+
else
|
|
158
|
+
@workers.first
|
|
159
|
+
end
|
|
160
|
+
|
|
135
161
|
begin
|
|
136
|
-
|
|
162
|
+
target.send(work, move: true)
|
|
137
163
|
ensure
|
|
138
164
|
@in_flight.swap { |count| count - 1 }
|
|
139
165
|
end
|
|
@@ -175,7 +201,7 @@ class RactorPool
|
|
|
175
201
|
@workers.each(&:join)
|
|
176
202
|
@coordinator.join
|
|
177
203
|
else
|
|
178
|
-
@workers.
|
|
204
|
+
@workers.each { |worker| worker.send(SHUTDOWN, move: true) }
|
|
179
205
|
@workers.each(&:join)
|
|
180
206
|
@result_port&.send(SHUTDOWN, move: true)
|
|
181
207
|
end
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# Generated from lib/ractor-pool.rb with RBS::Inline
|
|
2
2
|
|
|
3
|
-
# A thread-safe, lock-free pool of Ractor workers with
|
|
3
|
+
# A thread-safe, lock-free pool of Ractor workers with coordinator or round-robin dispatch for distributing work.
|
|
4
4
|
#
|
|
5
5
|
# RactorPool manages a fixed number of worker ractors that process work items in parallel.
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# The +:coordinator+ strategy (the default) routes each work item to whichever worker is
|
|
7
|
+
# currently idle via a dedicated coordinator Ractor, so a slow item on one worker does not
|
|
8
|
+
# block faster items from being picked up by other workers. Use it when work items have
|
|
9
|
+
# variable cost. The +:round_robin+ strategy dispatches work to workers in turn, so a slow
|
|
10
|
+
# item on one worker queues the next item destined for that worker behind it, even if other
|
|
11
|
+
# workers are idle. Use it when work items have uniform cost. Results are collected and
|
|
12
|
+
# passed to a result handler running in a separate thread.
|
|
8
13
|
#
|
|
9
14
|
# @example Basic usage
|
|
10
15
|
# results = []
|
|
@@ -42,6 +47,8 @@ class RactorPool
|
|
|
42
47
|
def message: () -> String
|
|
43
48
|
end
|
|
44
49
|
|
|
50
|
+
STRATEGIES: untyped
|
|
51
|
+
|
|
45
52
|
SHUTDOWN: ::Symbol
|
|
46
53
|
|
|
47
54
|
@error_collector: Thread?
|
|
@@ -56,6 +63,8 @@ class RactorPool
|
|
|
56
63
|
|
|
57
64
|
@result_port: Ractor::Port?
|
|
58
65
|
|
|
66
|
+
@next_worker_index: Atom[Integer]?
|
|
67
|
+
|
|
59
68
|
@shutdown: Atom[bool]
|
|
60
69
|
|
|
61
70
|
@in_flight: Atom[Integer]
|
|
@@ -66,6 +75,8 @@ class RactorPool
|
|
|
66
75
|
|
|
67
76
|
@name: String?
|
|
68
77
|
|
|
78
|
+
@strategy: Symbol
|
|
79
|
+
|
|
69
80
|
@worker: ^(untyped) -> untyped
|
|
70
81
|
|
|
71
82
|
@size: Integer
|
|
@@ -74,12 +85,14 @@ class RactorPool
|
|
|
74
85
|
#
|
|
75
86
|
# @param size [Integer] number of worker ractors to create
|
|
76
87
|
# @param worker [Proc] a shareable proc that processes each work item
|
|
88
|
+
# @param strategy [Symbol] dispatch strategy, either +:coordinator+ (the default) or +:round_robin+
|
|
77
89
|
# @param name [String, nil] optional name for the pool, used in thread/ractor names
|
|
78
90
|
# @param on_error [Proc, nil] optional shareable proc called with the raised exception when a worker raises
|
|
79
91
|
# @yieldparam result [Object] the result returned by the worker proc
|
|
80
92
|
# @return [void]
|
|
81
93
|
# @raise [ArgumentError] if size is not a positive integer
|
|
82
94
|
# @raise [ArgumentError] if worker is not a proc
|
|
95
|
+
# @raise [ArgumentError] if strategy is not +:coordinator+ or +:round_robin+
|
|
83
96
|
# @raise [ArgumentError] if on_error is given but is not a proc
|
|
84
97
|
#
|
|
85
98
|
# @example With result handler
|
|
@@ -88,13 +101,16 @@ class RactorPool
|
|
|
88
101
|
# @example Without result handler
|
|
89
102
|
# pool = RactorPool.new(size: 4, worker: proc { it })
|
|
90
103
|
#
|
|
104
|
+
# @example With round-robin strategy
|
|
105
|
+
# pool = RactorPool.new(size: 4, strategy: :round_robin, worker: proc { it })
|
|
106
|
+
#
|
|
91
107
|
# @example With error handler
|
|
92
108
|
# error_count = Atom.new(0)
|
|
93
109
|
# on_error = proc { error_count.swap { |count| count + 1 } }
|
|
94
110
|
# pool = RactorPool.new(size: 4, worker: proc { raise }, on_error: on_error)
|
|
95
111
|
#
|
|
96
|
-
# @rbs (?size: Integer, worker: ^(untyped) -> untyped, ?name: String?, ?on_error: (^(Exception) -> void | nil)) ?{ (untyped) -> void } -> void
|
|
97
|
-
def initialize: (worker: ^(untyped) -> untyped, ?size: Integer, ?name: String?, ?on_error: ^(Exception) -> void | nil) ?{ (untyped) -> void } -> void
|
|
112
|
+
# @rbs (?size: Integer, worker: ^(untyped) -> untyped, ?strategy: Symbol, ?name: String?, ?on_error: (^(Exception) -> void | nil)) ?{ (untyped) -> void } -> void
|
|
113
|
+
def initialize: (worker: ^(untyped) -> untyped, ?size: Integer, ?strategy: Symbol, ?name: String?, ?on_error: ^(Exception) -> void | nil) ?{ (untyped) -> void } -> void
|
|
98
114
|
|
|
99
115
|
# Queues a work item to be processed by an available worker.
|
|
100
116
|
#
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ractor-pool
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua Young
|
|
@@ -75,6 +75,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
75
|
requirements: []
|
|
76
76
|
rubygems_version: 4.0.10
|
|
77
77
|
specification_version: 4
|
|
78
|
-
summary: A thread-safe, lock-free pool of Ractor workers with
|
|
79
|
-
for distributing work
|
|
78
|
+
summary: A thread-safe, lock-free pool of Ractor workers with coordinator or round-robin
|
|
79
|
+
dispatch for distributing work
|
|
80
80
|
test_files: []
|