async 2.8.1 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|