async 2.24.0 → 2.25.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
- checksums.yaml.gz.sig +0 -0
- data/lib/async/clock.rb +1 -1
- data/lib/async/limited_queue.rb +1 -1
- data/lib/async/queue.rb +2 -1
- data/lib/async/scheduler.rb +69 -6
- data/lib/async/task.rb +2 -1
- data/lib/async/variable.rb +1 -1
- data/lib/async/version.rb +2 -2
- data/lib/metrics/provider/async/task.rb +1 -1
- data/lib/traces/provider/async/barrier.rb +1 -1
- data/lib/traces/provider/async/task.rb +1 -1
- data/license.md +6 -1
- data/readme.md +6 -0
- data/releases.md +27 -0
- data.tar.gz.sig +0 -0
- metadata +17 -13
- metadata.gz.sig +0 -0
- data/lib/async/worker_pool.rb +0 -182
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44c11aa8f74922b9bc2ccce792fd3c419af4a3a261f97f2f2202e3d75a5bf8f5
|
4
|
+
data.tar.gz: e2327b997b32a42e2ebfe9a4e4950d26a12ce698ce34a89d9fe1be94b0241b1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20961f535e5368753dffdb760d9bddf0fcf55e5910e5f6c71dba28ebb0405221d9328a8bfe5ef4e688ead71b4b5c4bde3949ce1edfa013b06af7db57adfe6d7f
|
7
|
+
data.tar.gz: d58c654852cd650978fa14fef7eaba00f4784137075960e5ad5b0b6b20ae3523912578b32b660f7d107df318cdd34945124e83231c4e574e336c5c4862a6c87c
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/clock.rb
CHANGED
data/lib/async/limited_queue.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright,
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
5
|
|
6
6
|
# The implementation lives in `queue.rb` but later we may move it here for better autoload/inference.
|
7
7
|
require_relative "queue"
|
data/lib/async/queue.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
4
|
+
# Copyright, 2018-2025, by Samuel Williams.
|
5
5
|
# Copyright, 2019, by Ryan Musgrave.
|
6
6
|
# Copyright, 2020-2022, by Bruno Sutic.
|
7
|
+
# Copyright, 2025, by Jahfer Husain.
|
7
8
|
|
8
9
|
require_relative "notification"
|
9
10
|
|
data/lib/async/scheduler.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
# Copyright, 2020, by Jun Jiang.
|
6
6
|
# Copyright, 2021, by Julien Portalier.
|
7
|
+
# Copyright, 2025, by Shopify Inc.
|
7
8
|
|
8
9
|
require_relative "clock"
|
9
10
|
require_relative "task"
|
10
11
|
require_relative "timeout"
|
11
|
-
require_relative "worker_pool"
|
12
12
|
|
13
13
|
require "io/event"
|
14
14
|
|
@@ -45,7 +45,29 @@ module Async
|
|
45
45
|
def self.supported?
|
46
46
|
true
|
47
47
|
end
|
48
|
+
|
49
|
+
# Used to augment the scheduler to add support for blocking operations.
|
50
|
+
module BlockingOperationWait
|
51
|
+
# Wait for the given work to be executed.
|
52
|
+
#
|
53
|
+
# @public Since *Async v2.21* and *Ruby v3.4*.
|
54
|
+
# @asynchronous May be non-blocking.
|
55
|
+
#
|
56
|
+
# @parameter work [Proc] The work to execute on a background thread.
|
57
|
+
# @returns [Object] The result of the work.
|
58
|
+
def blocking_operation_wait(work)
|
59
|
+
@worker_pool.call(work)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private_constant :BlockingOperationWait
|
48
64
|
|
65
|
+
if ::IO::Event.const_defined?(:WorkerPool)
|
66
|
+
WorkerPool = ::IO::Event::WorkerPool
|
67
|
+
else
|
68
|
+
WorkerPool = nil
|
69
|
+
end
|
70
|
+
|
49
71
|
# Create a new scheduler.
|
50
72
|
#
|
51
73
|
# @public Since *Async v1*.
|
@@ -65,14 +87,15 @@ module Async
|
|
65
87
|
@idle_time = 0.0
|
66
88
|
|
67
89
|
@timers = ::IO::Event::Timers.new
|
90
|
+
|
68
91
|
if worker_pool == true
|
69
|
-
@worker_pool = WorkerPool
|
92
|
+
@worker_pool = WorkerPool&.new
|
70
93
|
else
|
71
94
|
@worker_pool = worker_pool
|
72
95
|
end
|
73
|
-
|
96
|
+
|
74
97
|
if @worker_pool
|
75
|
-
self.singleton_class.prepend(
|
98
|
+
self.singleton_class.prepend(BlockingOperationWait)
|
76
99
|
end
|
77
100
|
end
|
78
101
|
|
@@ -234,7 +257,7 @@ module Async
|
|
234
257
|
# @parameter blocker [Object] The object that was blocking the fiber.
|
235
258
|
# @parameter fiber [Fiber] The fiber to unblock.
|
236
259
|
def unblock(blocker, fiber)
|
237
|
-
# $stderr.puts "unblock(#{blocker}, #{fiber})"
|
260
|
+
# Fiber.blocking{$stderr.puts "unblock(#{blocker}, #{fiber})"}
|
238
261
|
|
239
262
|
# This operation is protected by the GVL:
|
240
263
|
if selector = @selector
|
@@ -250,6 +273,8 @@ module Async
|
|
250
273
|
#
|
251
274
|
# @parameter duration [Numeric | Nil] The time in seconds to sleep, or if nil, indefinitely.
|
252
275
|
def kernel_sleep(duration = nil)
|
276
|
+
# Fiber.blocking{$stderr.puts "kernel_sleep(#{duration}, #{Fiber.current})"}
|
277
|
+
|
253
278
|
if duration
|
254
279
|
self.block(nil, duration)
|
255
280
|
else
|
@@ -348,6 +373,34 @@ module Async
|
|
348
373
|
end
|
349
374
|
end
|
350
375
|
|
376
|
+
# Used to defer stopping the current task until later.
|
377
|
+
class FiberInterrupt
|
378
|
+
# Create a new stop later operation.
|
379
|
+
#
|
380
|
+
# @parameter task [Task] The task to stop later.
|
381
|
+
def initialize(fiber, exception)
|
382
|
+
@fiber = fiber
|
383
|
+
@exception = exception
|
384
|
+
end
|
385
|
+
|
386
|
+
# @returns [Boolean] Whether the task is alive.
|
387
|
+
def alive?
|
388
|
+
@fiber.alive?
|
389
|
+
end
|
390
|
+
|
391
|
+
# Transfer control to the operation - this will stop the task.
|
392
|
+
def transfer
|
393
|
+
# Fiber.blocking{$stderr.puts "FiberInterrupt#transfer(#{@fiber}, #{@exception})"}
|
394
|
+
@fiber.raise(@exception)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
# Raise an exception on the specified fiber, waking up the event loop if necessary.
|
399
|
+
def fiber_interrupt(fiber, exception)
|
400
|
+
# Fiber.blocking{$stderr.puts "fiber_interrupt(#{fiber}, #{exception})"}
|
401
|
+
unblock(nil, FiberInterrupt.new(fiber, exception))
|
402
|
+
end
|
403
|
+
|
351
404
|
# Wait for the specified process ID to exit.
|
352
405
|
#
|
353
406
|
# @public Since *Async v2*.
|
@@ -361,6 +414,16 @@ module Async
|
|
361
414
|
return @selector.process_wait(Fiber.current, pid, flags)
|
362
415
|
end
|
363
416
|
|
417
|
+
# Wait for the specified IOs to become ready for the specified events.
|
418
|
+
#
|
419
|
+
# @public Since *Async v2.25*.
|
420
|
+
# @asynchronous May be non-blocking.
|
421
|
+
def io_select(...)
|
422
|
+
Thread.new do
|
423
|
+
::IO.select(...)
|
424
|
+
end.value
|
425
|
+
end
|
426
|
+
|
364
427
|
# Run one iteration of the event loop.
|
365
428
|
#
|
366
429
|
# When terminating the event loop, we already know we are finished. So we don't need to check the task tree. This is a logical requirement because `run_once` ignores transient tasks. For example, a single top level transient task is not enough to keep the reactor running, but during termination we must still process it in order to terminate child tasks.
|
data/lib/async/task.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2017-
|
4
|
+
# Copyright, 2017-2025, by Samuel Williams.
|
5
5
|
# Copyright, 2017, by Kent Gruber.
|
6
6
|
# Copyright, 2017, by Devin Christensen.
|
7
7
|
# Copyright, 2020, by Patrik Wenger.
|
8
8
|
# Copyright, 2023, by Math Ieu.
|
9
|
+
# Copyright, 2025, by Shigeru Nakajima.
|
9
10
|
|
10
11
|
require "fiber"
|
11
12
|
require "console"
|
data/lib/async/variable.rb
CHANGED
data/lib/async/version.rb
CHANGED
data/license.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# MIT License
|
2
2
|
|
3
|
-
Copyright, 2017-
|
3
|
+
Copyright, 2017-2025, by Samuel Williams.
|
4
4
|
Copyright, 2017, by Kent Gruber.
|
5
5
|
Copyright, 2017, by Devin Christensen.
|
6
6
|
Copyright, 2018, by Sokolov Yura.
|
@@ -27,6 +27,11 @@ Copyright, 2023, by Emil Tin.
|
|
27
27
|
Copyright, 2023, by Gert Goet.
|
28
28
|
Copyright, 2024, by Dimitar Peychinov.
|
29
29
|
Copyright, 2024, by Jamie McCarthy.
|
30
|
+
Copyright, 2025, by Jahfer Husain.
|
31
|
+
Copyright, 2025, by Mark Montroy.
|
32
|
+
Copyright, 2025, by Shigeru Nakajima.
|
33
|
+
Copyright, 2025, by Alan Wu.
|
34
|
+
Copyright, 2025, by Shopify Inc.
|
30
35
|
|
31
36
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
32
37
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
@@ -35,6 +35,12 @@ Please see the [project documentation](https://socketry.github.io/async/) for mo
|
|
35
35
|
|
36
36
|
Please see the [project releases](https://socketry.github.io/async/releases/index) for all releases.
|
37
37
|
|
38
|
+
### v2.25.0
|
39
|
+
|
40
|
+
- Added support for `io_select` hook in the fiber scheduler, allowing non-blocking `IO.select` operations. This enables better integration with code that uses `IO.select` for multiplexing IO operations.
|
41
|
+
- [Use `IO::Event::WorkerPool` for Blocking Operations](https://socketry.github.io/async/releases/index#use-io::event::workerpool-for-blocking-operations)
|
42
|
+
- [Better handling of `IO#close` using `fiber_interrupt`](https://socketry.github.io/async/releases/index#better-handling-of-io#close-using-fiber_interrupt)
|
43
|
+
|
38
44
|
### v2.24.0
|
39
45
|
|
40
46
|
- Ruby v3.1 support is dropped.
|
data/releases.md
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v2.25.0
|
4
|
+
|
5
|
+
- Added support for `io_select` hook in the fiber scheduler, allowing non-blocking `IO.select` operations. This enables better integration with code that uses `IO.select` for multiplexing IO operations.
|
6
|
+
|
7
|
+
### Use `IO::Event::WorkerPool` for Blocking Operations
|
8
|
+
|
9
|
+
The `Async::WorkerPool` implementation has been removed in favor of using `IO::Event::WorkerPool` directly. This change simplifies the codebase by delegating worker pool functionality to the `io-event` gem, which provides a more efficient and well-tested implementation.
|
10
|
+
|
11
|
+
To enable the worker pool, you can set the `ASYNC_SCHEDULER_WORKER_POOL` environment variable to `true`. This will allow the scheduler to use a worker pool for blocking operations, which can help improve performance in applications that perform a lot of CPU-bound operations (e.g. `rb_nogvl`).
|
12
|
+
|
13
|
+
### Better handling of `IO#close` using `fiber_interrupt`
|
14
|
+
|
15
|
+
`IO#close` interrupts fibers that are waiting on the IO using the new `fiber_interrupt` hook introduced in Ruby 3.5/4.0. This means that if you close an IO while a fiber is waiting on it, the fiber will be interrupted and will raise an `IOError`. This is a change from previous versions of Ruby, where closing an IO would not interrupt fibers waiting on it, and would instead interrupt the entire event loop (essentially a bug).
|
16
|
+
|
17
|
+
``` ruby
|
18
|
+
r, w = IO.pipe
|
19
|
+
|
20
|
+
Async do
|
21
|
+
child = Async do
|
22
|
+
r.gets
|
23
|
+
end
|
24
|
+
|
25
|
+
r.close # This will interrupt the child fiber.
|
26
|
+
child.wait # This will raise an `IOError` because the IO was closed.
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
3
30
|
## v2.24.0
|
4
31
|
|
5
32
|
- Ruby v3.1 support is dropped.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.25.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -13,21 +13,26 @@ authors:
|
|
13
13
|
- Emil Tin
|
14
14
|
- Jamie McCarthy
|
15
15
|
- Kent Gruber
|
16
|
+
- Alan Wu
|
16
17
|
- Brian Morearty
|
17
18
|
- Colin Kelley
|
18
19
|
- Dimitar Peychinov
|
19
20
|
- Gert Goet
|
21
|
+
- Jahfer Husain
|
20
22
|
- Jiang Jinyang
|
21
23
|
- Julien Portalier
|
22
24
|
- Jun Jiang
|
23
25
|
- Ken Muryoi
|
24
26
|
- Leon Löchner
|
27
|
+
- Mark Montroy
|
25
28
|
- Masafumi Okura
|
26
29
|
- Masayuki Yamamoto
|
27
30
|
- Math Ieu
|
28
31
|
- Ryan Musgrave
|
29
32
|
- Salim Semaoune
|
30
33
|
- Shannon Skipper
|
34
|
+
- Shigeru Nakajima
|
35
|
+
- Shopify Inc.
|
31
36
|
- Sokolov Yura
|
32
37
|
- Stefan Wrobel
|
33
38
|
- Trevor Turk
|
@@ -62,7 +67,7 @@ cert_chain:
|
|
62
67
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
63
68
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
64
69
|
-----END CERTIFICATE-----
|
65
|
-
date:
|
70
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
66
71
|
dependencies:
|
67
72
|
- !ruby/object:Gem::Dependency
|
68
73
|
name: console
|
@@ -98,42 +103,42 @@ dependencies:
|
|
98
103
|
requirements:
|
99
104
|
- - "~>"
|
100
105
|
- !ruby/object:Gem::Version
|
101
|
-
version: '1.
|
106
|
+
version: '1.11'
|
102
107
|
type: :runtime
|
103
108
|
prerelease: false
|
104
109
|
version_requirements: !ruby/object:Gem::Requirement
|
105
110
|
requirements:
|
106
111
|
- - "~>"
|
107
112
|
- !ruby/object:Gem::Version
|
108
|
-
version: '1.
|
113
|
+
version: '1.11'
|
109
114
|
- !ruby/object:Gem::Dependency
|
110
|
-
name:
|
115
|
+
name: metrics
|
111
116
|
requirement: !ruby/object:Gem::Requirement
|
112
117
|
requirements:
|
113
118
|
- - "~>"
|
114
119
|
- !ruby/object:Gem::Version
|
115
|
-
version: '0.
|
120
|
+
version: '0.12'
|
116
121
|
type: :runtime
|
117
122
|
prerelease: false
|
118
123
|
version_requirements: !ruby/object:Gem::Requirement
|
119
124
|
requirements:
|
120
125
|
- - "~>"
|
121
126
|
- !ruby/object:Gem::Version
|
122
|
-
version: '0.
|
127
|
+
version: '0.12'
|
123
128
|
- !ruby/object:Gem::Dependency
|
124
|
-
name:
|
129
|
+
name: traces
|
125
130
|
requirement: !ruby/object:Gem::Requirement
|
126
131
|
requirements:
|
127
132
|
- - "~>"
|
128
133
|
- !ruby/object:Gem::Version
|
129
|
-
version: '0.
|
134
|
+
version: '0.15'
|
130
135
|
type: :runtime
|
131
136
|
prerelease: false
|
132
137
|
version_requirements: !ruby/object:Gem::Requirement
|
133
138
|
requirements:
|
134
139
|
- - "~>"
|
135
140
|
- !ruby/object:Gem::Version
|
136
|
-
version: '0.
|
141
|
+
version: '0.15'
|
137
142
|
executables: []
|
138
143
|
extensions: []
|
139
144
|
extra_rdoc_files: []
|
@@ -162,7 +167,6 @@ files:
|
|
162
167
|
- lib/async/version.rb
|
163
168
|
- lib/async/waiter.md
|
164
169
|
- lib/async/waiter.rb
|
165
|
-
- lib/async/worker_pool.rb
|
166
170
|
- lib/kernel/async.rb
|
167
171
|
- lib/kernel/sync.rb
|
168
172
|
- lib/metrics/provider/async.rb
|
@@ -187,14 +191,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
187
191
|
requirements:
|
188
192
|
- - ">="
|
189
193
|
- !ruby/object:Gem::Version
|
190
|
-
version: '3.
|
194
|
+
version: '3.2'
|
191
195
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
196
|
requirements:
|
193
197
|
- - ">="
|
194
198
|
- !ruby/object:Gem::Version
|
195
199
|
version: '0'
|
196
200
|
requirements: []
|
197
|
-
rubygems_version: 3.6.
|
201
|
+
rubygems_version: 3.6.7
|
198
202
|
specification_version: 4
|
199
203
|
summary: A concurrency framework for Ruby.
|
200
204
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|
data/lib/async/worker_pool.rb
DELETED
@@ -1,182 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
5
|
-
|
6
|
-
require "etc"
|
7
|
-
|
8
|
-
module Async
|
9
|
-
# A simple work pool that offloads work to a background thread.
|
10
|
-
#
|
11
|
-
# @private
|
12
|
-
class WorkerPool
|
13
|
-
# Used to augment the scheduler to add support for blocking operations.
|
14
|
-
module BlockingOperationWait
|
15
|
-
# Wait for the given work to be executed.
|
16
|
-
#
|
17
|
-
# @public Since *Async v2.21* and *Ruby v3.4*.
|
18
|
-
# @asynchronous May be non-blocking.
|
19
|
-
#
|
20
|
-
# @parameter work [Proc] The work to execute on a background thread.
|
21
|
-
# @returns [Object] The result of the work.
|
22
|
-
def blocking_operation_wait(work)
|
23
|
-
@worker_pool.call(work)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Execute the given work in a background thread.
|
28
|
-
class Promise
|
29
|
-
# Create a new promise.
|
30
|
-
#
|
31
|
-
# @parameter work [Proc] The work to be done.
|
32
|
-
def initialize(work)
|
33
|
-
@work = work
|
34
|
-
@state = :pending
|
35
|
-
@value = nil
|
36
|
-
@guard = ::Mutex.new
|
37
|
-
@condition = ::ConditionVariable.new
|
38
|
-
@thread = nil
|
39
|
-
end
|
40
|
-
|
41
|
-
# Execute the work and resolve the promise.
|
42
|
-
def call
|
43
|
-
work = nil
|
44
|
-
|
45
|
-
@guard.synchronize do
|
46
|
-
@thread = ::Thread.current
|
47
|
-
|
48
|
-
return unless work = @work
|
49
|
-
end
|
50
|
-
|
51
|
-
resolve(work.call)
|
52
|
-
rescue Exception => error
|
53
|
-
reject(error)
|
54
|
-
end
|
55
|
-
|
56
|
-
private def resolve(value)
|
57
|
-
@guard.synchronize do
|
58
|
-
@work = nil
|
59
|
-
@thread = nil
|
60
|
-
@value = value
|
61
|
-
@state = :resolved
|
62
|
-
@condition.broadcast
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
private def reject(error)
|
67
|
-
@guard.synchronize do
|
68
|
-
@work = nil
|
69
|
-
@thread = nil
|
70
|
-
@value = error
|
71
|
-
@state = :failed
|
72
|
-
@condition.broadcast
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Cancel the work and raise an exception in the background thread.
|
77
|
-
def cancel
|
78
|
-
return unless @work
|
79
|
-
|
80
|
-
@guard.synchronize do
|
81
|
-
@work = nil
|
82
|
-
@state = :cancelled
|
83
|
-
@thread&.raise(Interrupt)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
# Wait for the work to be done.
|
88
|
-
#
|
89
|
-
# @returns [Object] The result of the work.
|
90
|
-
def wait
|
91
|
-
@guard.synchronize do
|
92
|
-
while @state == :pending
|
93
|
-
@condition.wait(@guard)
|
94
|
-
end
|
95
|
-
|
96
|
-
if @state == :failed
|
97
|
-
raise @value
|
98
|
-
else
|
99
|
-
return @value
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# A background worker thread.
|
106
|
-
class Worker
|
107
|
-
# Create a new worker.
|
108
|
-
def initialize
|
109
|
-
@work = ::Thread::Queue.new
|
110
|
-
@thread = ::Thread.new(&method(:run))
|
111
|
-
end
|
112
|
-
|
113
|
-
# Execute work until the queue is closed.
|
114
|
-
def run
|
115
|
-
while work = @work.pop
|
116
|
-
work.call
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
# Close the worker thread.
|
121
|
-
def close
|
122
|
-
if thread = @thread
|
123
|
-
@thread = nil
|
124
|
-
thread.kill
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# Call the work and notify the scheduler when it is done.
|
129
|
-
def call(work)
|
130
|
-
promise = Promise.new(work)
|
131
|
-
|
132
|
-
@work.push(promise)
|
133
|
-
|
134
|
-
begin
|
135
|
-
return promise.wait
|
136
|
-
ensure
|
137
|
-
promise.cancel
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
# Create a new work pool.
|
143
|
-
#
|
144
|
-
# @parameter size [Integer] The number of threads to use.
|
145
|
-
def initialize(size: Etc.nprocessors)
|
146
|
-
@ready = ::Thread::Queue.new
|
147
|
-
|
148
|
-
size.times do
|
149
|
-
@ready.push(Worker.new)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
# Close the work pool. Kills all outstanding work.
|
154
|
-
def close
|
155
|
-
if ready = @ready
|
156
|
-
@ready = nil
|
157
|
-
ready.close
|
158
|
-
|
159
|
-
while worker = ready.pop
|
160
|
-
worker.close
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# Offload work to a thread.
|
166
|
-
#
|
167
|
-
# @parameter work [Proc] The work to be done.
|
168
|
-
def call(work)
|
169
|
-
if ready = @ready
|
170
|
-
worker = ready.pop
|
171
|
-
|
172
|
-
begin
|
173
|
-
worker.call(work)
|
174
|
-
ensure
|
175
|
-
ready.push(worker)
|
176
|
-
end
|
177
|
-
else
|
178
|
-
raise RuntimeError, "No worker available!"
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|