async 2.8.1 → 2.9.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/idler.rb +39 -0
- data/lib/async/scheduler.rb +47 -2
- data/lib/async/task.rb +3 -3
- data/lib/async/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +11 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e93ed3ea9094d074f430f9518bf0087f57abf732bf196bed5cf8249315aac33d
|
4
|
+
data.tar.gz: 233dffa50c8bb88cd514545280e3bef088b998a1ddb58e6db68d1a7c8a268c32
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bede7ace58ac0e5f6bbefd05a89855a3f80ddf3d02a441aaa73b0a64adeb82b83d02d064121ae267b719519a6fc03f86b7b586f8af3901ae12fcfc6db2d5612c
|
7
|
+
data.tar.gz: a093ddf356f924089fc8500bee1570cac0e7a4a2e131127eeae0b65813905ae853f44e448f18ecda3d55e39d33f2c7e96c41a34f53cc02259ac8b5bef02e857a
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/idler.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
5
|
+
|
6
|
+
module Async
|
7
|
+
class Idler
|
8
|
+
def initialize(maximum_load = 0.8, backoff: 0.01, parent: nil)
|
9
|
+
@maximum_load = maximum_load
|
10
|
+
@backoff = backoff
|
11
|
+
@parent = parent
|
12
|
+
end
|
13
|
+
|
14
|
+
def async(*arguments, parent: (@parent or Task.current), **options, &block)
|
15
|
+
wait
|
16
|
+
|
17
|
+
# It is crucial that we optimistically execute the child task, so that we prevent a tight loop invoking this method from consuming all available resources.
|
18
|
+
parent.async(*arguments, **options, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def wait
|
22
|
+
scheduler = Fiber.scheduler
|
23
|
+
backoff = nil
|
24
|
+
|
25
|
+
while true
|
26
|
+
load = scheduler.load
|
27
|
+
break if load < @maximum_load
|
28
|
+
|
29
|
+
if backoff
|
30
|
+
sleep(backoff)
|
31
|
+
backoff *= 2.0
|
32
|
+
else
|
33
|
+
scheduler.yield
|
34
|
+
backoff = @backoff
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/async/scheduler.rb
CHANGED
@@ -37,9 +37,33 @@ module Async
|
|
37
37
|
|
38
38
|
@blocked = 0
|
39
39
|
|
40
|
+
@busy_time = 0.0
|
41
|
+
@idle_time = 0.0
|
42
|
+
|
40
43
|
@timers = ::Timers::Group.new
|
41
44
|
end
|
42
45
|
|
46
|
+
# Compute the scheduler load according to the busy and idle times that are updated by the run loop.
|
47
|
+
# @returns [Float] The load of the scheduler. 0.0 means no load, 1.0 means fully loaded or over-loaded.
|
48
|
+
def load
|
49
|
+
total_time = @busy_time + @idle_time
|
50
|
+
|
51
|
+
# If the total time is zero, then the load is zero:
|
52
|
+
return 0.0 if total_time.zero?
|
53
|
+
|
54
|
+
# We normalize to a 1 second window:
|
55
|
+
if total_time > 1.0
|
56
|
+
ratio = 1.0 / total_time
|
57
|
+
@busy_time *= ratio
|
58
|
+
@idle_time *= ratio
|
59
|
+
|
60
|
+
# We don't need to divide here as we've already normalised it to a 1s window:
|
61
|
+
return @busy_time
|
62
|
+
else
|
63
|
+
return @busy_time / total_time
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
43
67
|
def scheduler_close
|
44
68
|
# If the execution context (thread) was handling an exception, we want to exit as quickly as possible:
|
45
69
|
unless $!
|
@@ -49,6 +73,13 @@ module Async
|
|
49
73
|
self.close
|
50
74
|
end
|
51
75
|
|
76
|
+
# Terminate the scheduler. We deliberately ignore interrupts here, as this code can be called from an interrupt, and we don't want to be interrupted while cleaning up.
|
77
|
+
def terminate
|
78
|
+
Thread.handle_interrupt(::Interrupt => :never) do
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
52
83
|
# @public Since `stable-v1`.
|
53
84
|
def close
|
54
85
|
# It's critical to stop all tasks. Otherwise they might be holding on to resources which are never closed/released correctly.
|
@@ -260,6 +291,8 @@ module Async
|
|
260
291
|
# @parameter timeout [Float | Nil] The maximum timeout, or if nil, indefinite.
|
261
292
|
# @returns [Boolean] Whether there is more work to do.
|
262
293
|
private def run_once!(timeout = 0)
|
294
|
+
start_time = Async::Clock.now
|
295
|
+
|
263
296
|
interval = @timers.wait_interval
|
264
297
|
|
265
298
|
# If there is no interval to wait (thus no timers), and no tasks, we could be done:
|
@@ -281,6 +314,15 @@ module Async
|
|
281
314
|
|
282
315
|
@timers.fire
|
283
316
|
|
317
|
+
# Compute load:
|
318
|
+
end_time = Async::Clock.now
|
319
|
+
total_duration = end_time - start_time
|
320
|
+
idle_duration = @selector.idle_duration
|
321
|
+
busy_duration = total_duration - idle_duration
|
322
|
+
|
323
|
+
@busy_time += busy_duration
|
324
|
+
@idle_time += idle_duration
|
325
|
+
|
284
326
|
# The reactor still has work to do:
|
285
327
|
return true
|
286
328
|
end
|
@@ -308,7 +350,7 @@ module Async
|
|
308
350
|
|
309
351
|
begin
|
310
352
|
# In theory, we could use Exception here to be a little bit safer, but we've only shown the case for SignalException to be a problem, so let's not over-engineer this.
|
311
|
-
Thread.handle_interrupt(SignalException => :never) do
|
353
|
+
Thread.handle_interrupt(::SignalException => :never) do
|
312
354
|
while true
|
313
355
|
# If we are interrupted, we need to exit:
|
314
356
|
break if self.interrupted?
|
@@ -318,7 +360,10 @@ module Async
|
|
318
360
|
end
|
319
361
|
end
|
320
362
|
rescue Interrupt
|
321
|
-
|
363
|
+
Thread.handle_interrupt(::SignalException => :never) do
|
364
|
+
self.stop
|
365
|
+
end
|
366
|
+
|
322
367
|
retry
|
323
368
|
end
|
324
369
|
|
data/lib/async/task.rb
CHANGED
@@ -208,8 +208,8 @@ module Async
|
|
208
208
|
# @parameter later [Boolean] Whether to stop the task later, or immediately.
|
209
209
|
def stop(later = false)
|
210
210
|
if self.stopped?
|
211
|
-
# If
|
212
|
-
return
|
211
|
+
# If the task is already stopped, a `stop` state transition re-enters the same state which is a no-op. However, we will also attempt to stop any running children too. This can happen if the children did not stop correctly the first time around. Doing this should probably be considered a bug, but it's better to be safe than sorry.
|
212
|
+
return stopped!
|
213
213
|
end
|
214
214
|
|
215
215
|
# If the fiber is alive, we need to stop it:
|
@@ -304,7 +304,7 @@ module Async
|
|
304
304
|
stopped = false
|
305
305
|
|
306
306
|
begin
|
307
|
-
# We are
|
307
|
+
# We are not running, but children might be so we should stop them:
|
308
308
|
stop_children(true)
|
309
309
|
rescue Stop
|
310
310
|
stopped = true
|
data/lib/async/version.rb
CHANGED
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.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -61,7 +61,7 @@ cert_chain:
|
|
61
61
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
62
62
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
63
63
|
-----END CERTIFICATE-----
|
64
|
-
date: 2024-
|
64
|
+
date: 2024-03-05 00:00:00.000000000 Z
|
65
65
|
dependencies:
|
66
66
|
- !ruby/object:Gem::Dependency
|
67
67
|
name: console
|
@@ -97,14 +97,20 @@ dependencies:
|
|
97
97
|
requirements:
|
98
98
|
- - "~>"
|
99
99
|
- !ruby/object:Gem::Version
|
100
|
-
version: '1.
|
100
|
+
version: '1.5'
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.5.1
|
101
104
|
type: :runtime
|
102
105
|
prerelease: false
|
103
106
|
version_requirements: !ruby/object:Gem::Requirement
|
104
107
|
requirements:
|
105
108
|
- - "~>"
|
106
109
|
- !ruby/object:Gem::Version
|
107
|
-
version: '1.
|
110
|
+
version: '1.5'
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 1.5.1
|
108
114
|
- !ruby/object:Gem::Dependency
|
109
115
|
name: timers
|
110
116
|
requirement: !ruby/object:Gem::Requirement
|
@@ -131,6 +137,7 @@ files:
|
|
131
137
|
- lib/async/clock.rb
|
132
138
|
- lib/async/condition.md
|
133
139
|
- lib/async/condition.rb
|
140
|
+
- lib/async/idler.rb
|
134
141
|
- lib/async/list.rb
|
135
142
|
- lib/async/node.rb
|
136
143
|
- lib/async/notification.rb
|
metadata.gz.sig
CHANGED
Binary file
|