Module: Ractor::Pipeline
- Defined in:
- lib/ractor/pipeline.rb,
lib/ractor/pipeline/version.rb,
sig/ractor/pipeline.rbs
Defined Under Namespace
Modules: Stages Classes: Branch, Error, Source, Stage, TokenPool
Constant Summary collapse
- CREDIT =
ready tokens per (producer, consumer) pair on pull links
2- VERSION =
"0.2.0"
Class Method Summary collapse
- .filter_pipe(lanes: 1, &block) ⇒ Object
- .flat_pipe(lanes: 1, &block) ⇒ Object
-
.pipe(lanes: 1, &block) ⇒ Object
Source-less fragments for tee branches.
-
.stream(source, batch: 1) ⇒ Object
-- DSL entry points ------------------------------------------------.
-
.stream1(obj) ⇒ Object
A stream of exactly one element: stream1(obj) == stream([obj]).
- .token_pools(groups) ⇒ Object
-
.worker_loop(ctrl, job, kind) ⇒ Object
The main loop of a stage worker.
Class Method Details
.filter_pipe(lanes: 1, &block) ⇒ Object
457 |
# File 'lib/ractor/pipeline.rb', line 457 def filter_pipe(lanes: 1, &block) = Branch.new.filter_pipe(lanes:, &block) |
.flat_pipe(lanes: 1, &block) ⇒ Object
458 |
# File 'lib/ractor/pipeline.rb', line 458 def flat_pipe(lanes: 1, &block) = Branch.new.flat_pipe(lanes:, &block) |
.pipe(lanes: 1, &block) ⇒ Object
Source-less fragments for tee branches.
456 |
# File 'lib/ractor/pipeline.rb', line 456 def pipe(lanes: 1, &block) = Branch.new.pipe(lanes:, &block) |
.stream(source, batch: 1) ⇒ Object
-- DSL entry points ------------------------------------------------
449 |
# File 'lib/ractor/pipeline.rb', line 449 def stream(source, batch: 1) = Source.new(source, batch:) |
.stream1(obj) ⇒ Object
A stream of exactly one element: stream1(obj) == stream([obj]). Use it to flow an object (even an each-able one) as a single element.
453 |
# File 'lib/ractor/pipeline.rb', line 453 def stream1(obj) = Source.new([obj]) |
.token_pools(groups) ⇒ Object
358 359 360 |
# File 'lib/ractor/pipeline.rb', line 358 def token_pools(groups) groups.map { TokenPool.new } end |
.worker_loop(ctrl, job, kind) ⇒ Object
The main loop of a stage worker. Stateless between streams except for the demand bookkeeping of its (per-run) wiring.
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/ractor/pipeline.rb', line 364 def worker_loop(ctrl, job, kind) in_port = Ractor::Port.new ctrl << in_port # wait for [:init]; queue anything that arrives earlier early = [] msg = in_port.receive until msg[0] == :init early << msg msg = in_port.receive end _, groups, ups, n_producers, batch_size = msg tokens = Pipeline.token_pools(groups) # demand tokens, per pull group pending = groups.map { [] } # batches waiting for a token port_group = {} groups.each_with_index do |(ports, pull), gi| ports.each { |port| port_group[port] = gi } if pull end eos = 0 dispatch = lambda do |batch| groups.each_with_index do |(ports, pull), gi| if !pull ports.first << [:data, batch, nil] elsif (port = tokens[gi].take) port << [:data, batch, in_port] else pending[gi] << batch end end end handle = lambda do |m| case m[0] when :data begin case kind when :pipe dispatch.call(m[1].map { |obj| job.call(obj) }) when :filter_pipe out = m[1].select { |obj| job.call(obj) } dispatch.call(out) unless out.empty? when :flat_pipe out = [] m[1].each { |obj| job.call(obj).each { |o| out << o } } out.each_slice(batch_size) { |slice| dispatch.call(slice) } end rescue Ractor::ClosedError raise rescue Exception => e groups.first.first.first << [:failure, e] rescue nil end (m[2] << [:ready, in_port] rescue nil) if m[2] # replenish credit when :ready gi = port_group[m[1]] if (batch = pending[gi].shift) m[1] << [:data, batch, in_port] else tokens[gi].add(m[1]) end when :eos eos += 1 when :failure groups.first.first.first << m rescue nil # pass through when :cancel throw :cancelled end end catch(:cancelled) do ups.each { |up| CREDIT.times { up << [:ready, in_port] rescue nil } } early.each { |m| handle.call(m) } handle.call(in_port.receive) until eos == n_producers # all producers finished: drain buffered batches, then EOS handle.call(in_port.receive) until pending.all?(&:empty?) groups.each { |ports, _| ports.each { |port| port << [:eos] rescue nil } } end rescue Ractor::ClosedError # a consumer is gone: the stream was cancelled (SIGPIPE-style) end |