async 2.4.2 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d64eb10a6e308731e79afd4591092d17ef5d4ffca9e40d9ee128a1d14b30669
4
- data.tar.gz: 8c5fda19ac8eb203bdd6a27100f90b47295d1ff882811d78da257342ae240258
3
+ metadata.gz: c732b7d676e5d1248189f0e3b9fbce25c44026633bd2094c30a94c7a82ee40a3
4
+ data.tar.gz: a668963f5721f56883ca4fecd7fda0b198f9b8ec2e4276fb126d30d547d18589
5
5
  SHA512:
6
- metadata.gz: b1b8f7644eda45639f9a53e314b195ad2955b10358148edf6e698be89cb7232c22be15d3a0214f14a2053bc4a6e3e78683f1ef22d1da02a1fc7cd83e1a71b2fc
7
- data.tar.gz: 109375f5cd3c217341316c4b5f390d7a2a57bda847bbaa8d98ec13e93fa9dbc6905978bad00641182b7d4daf4e87c0ac7286b43ca5a6522f0610b37e8206d7df
6
+ metadata.gz: 2480c08bd9dcd8ebfefcabb02d347f3b1aa3f4485202f101b351ac3b585390c2f5471f21fa34c959483ad2b976d6491b49c8dd63b869776f40157011e25a3404
7
+ data.tar.gz: 68b04041436ba8080614878d6903b7658ffe8a6c092b6ba38efa41e0fa9e9bdb1cffbcff7ed299f206c431b56ef8bbb089b1a07689c60c0eda3e34cd32ed9337
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/async/task.rb CHANGED
@@ -38,6 +38,25 @@ module Async
38
38
  end
39
39
 
40
40
  # Encapsulates the state of a running task and it's result.
41
+ #
42
+ # ```mermaid
43
+ # stateDiagram-v2
44
+ # [*] --> Initialized
45
+ # Initialized --> Running : Run
46
+ #
47
+ # Running --> Completed : Return Value
48
+ # Running --> Failed : Exception
49
+ #
50
+ # Completed --> [*]
51
+ # Failed --> [*]
52
+ #
53
+ # Running --> Stopped : Stop
54
+ # Stopped --> [*]
55
+ # Completed --> Stopped : Stop
56
+ # Failed --> Stopped : Stop
57
+ # Initialized --> Stopped : Stop
58
+ # ```
59
+ #
41
60
  # @public Since `stable-v1`.
42
61
  class Task < Node
43
62
  # @deprecated With no replacement.
@@ -51,22 +70,24 @@ module Async
51
70
  def initialize(parent = Task.current?, finished: nil, **options, &block)
52
71
  super(parent, **options)
53
72
 
73
+ # These instance variables are critical to the state of the task.
74
+ # In the initialized state, the @block should be set, but the @fiber should be nil.
75
+ # In the running state, the @fiber should be set.
76
+ # In a finished state, the @block should be nil, and the @fiber should be nil.
77
+ @block = block
78
+ @fiber = nil
79
+
54
80
  @status = :initialized
55
81
  @result = nil
56
82
  @finished = finished
57
-
58
- @block = block
59
- @fiber = nil
60
83
  end
61
84
 
62
85
  def reactor
63
86
  self.root
64
87
  end
65
88
 
66
- if Fiber.current.respond_to?(:backtrace)
67
- def backtrace(*arguments)
68
- @fiber&.backtrace(*arguments)
69
- end
89
+ def backtrace(*arguments)
90
+ @fiber&.backtrace(*arguments)
70
91
  end
71
92
 
72
93
  def to_s
@@ -91,10 +112,40 @@ module Async
91
112
  # @attr fiber [Fiber] The fiber which is being used for the execution of this task.
92
113
  attr :fiber
93
114
 
115
+ # Whether the internal fiber is alive, i.e. it
94
116
  def alive?
95
117
  @fiber&.alive?
96
118
  end
97
119
 
120
+ # Whether we can remove this node from the reactor graph.
121
+ # @returns [Boolean]
122
+ def finished?
123
+ # If the block is nil and the fiber is nil, it means the task has finished execution. This becomes true after `finish!` is called.
124
+ super && @block.nil? && @fiber.nil?
125
+ end
126
+
127
+ # Whether the task is running.
128
+ # @returns [Boolean]
129
+ def running?
130
+ @status == :running
131
+ end
132
+
133
+ def failed?
134
+ @status == :failed
135
+ end
136
+
137
+ # The task has been stopped
138
+ def stopped?
139
+ @status == :stopped
140
+ end
141
+
142
+ # The task has completed execution and generated a result.
143
+ def completed?
144
+ @status == :completed
145
+ end
146
+
147
+ alias complete? completed?
148
+
98
149
  # @attr status [Symbol] The status of the execution of the fiber, one of `:initialized`, `:running`, `:complete`, `:stopped` or `:failed`.
99
150
  attr :status
100
151
 
@@ -131,7 +182,8 @@ module Async
131
182
  def wait
132
183
  raise "Cannot wait on own fiber!" if Fiber.current.equal?(@fiber)
133
184
 
134
- if running?
185
+ # `finish!` will set both of these to nil before signaling the condition:
186
+ if @block || @fiber
135
187
  @finished ||= Condition.new
136
188
  @finished.wait
137
189
  end
@@ -153,17 +205,22 @@ module Async
153
205
  return
154
206
  end
155
207
 
156
- if self.running?
208
+ # If the fiber is alive, we need to stop it:
209
+ if @fiber&.alive?
157
210
  if self.current?
158
211
  if later
212
+ # If the fiber is the current fiber and we want to stop it later, schedule it:
159
213
  Fiber.scheduler.push(Stop::Later.new(self))
160
214
  else
215
+ # Otherwise, raise the exception directly:
161
216
  raise Stop, "Stopping current task!"
162
217
  end
163
- elsif @fiber&.alive?
218
+ else
219
+ # If the fiber is not curent, we can raise the exception directly:
164
220
  begin
165
221
  Fiber.scheduler.raise(@fiber, Stop)
166
222
  rescue FiberError
223
+ # In some cases, this can cause a FiberError (it might be resumed already), so we schedule it to be stopped later:
167
224
  Fiber.scheduler.push(Stop::Later.new(self))
168
225
  end
169
226
  end
@@ -190,36 +247,34 @@ module Async
190
247
  self.equal?(Thread.current[:async_task])
191
248
  end
192
249
 
193
- # Check if the task is running.
194
- # @returns [Boolean]
195
- def running?
196
- @status == :running
197
- end
198
-
199
- # Whether we can remove this node from the reactor graph.
200
- # @returns [Boolean]
201
- def finished?
202
- super && @fiber.nil?
203
- end
204
-
205
- def failed?
206
- @status == :failed
207
- end
250
+ private
208
251
 
209
- def stopped?
210
- @status == :stopped
252
+ # Finish the current task, moving any children to the parent.
253
+ def finish!
254
+ # Don't hold references to the fiber or block after the task has finished:
255
+ @fiber = nil
256
+ @block = nil # If some how we went directly from initialized to finished.
257
+
258
+ # Attempt to remove this node from the task tree.
259
+ consume
260
+
261
+ # If this task was being used as a future, signal completion here:
262
+ if @finished
263
+ @finished.signal(self)
264
+ @finished = nil
265
+ end
211
266
  end
212
267
 
213
- def complete?
214
- @status == :complete
268
+ # State transition into the completed state.
269
+ def completed!(result)
270
+ @result = result
271
+ @status = :completed
215
272
  end
216
273
 
217
- private
218
-
219
274
  # This is a very tricky aspect of tasks to get right. I've modelled it after `Thread` but it's slightly different in that the exception can propagate back up through the reactor. If the user writes code which raises an exception, that exception should always be visible, i.e. cause a failure. If it's not visible, such code fails silently and can be very difficult to debug.
220
- def fail!(exception = false, propagate = true)
221
- @status = :failed
275
+ def failed!(exception = false, propagate = true)
222
276
  @result = exception
277
+ @status = :failed
223
278
 
224
279
  if exception
225
280
  if propagate
@@ -233,27 +288,33 @@ module Async
233
288
  end
234
289
  end
235
290
 
236
- def stop!
291
+ def stopped!
237
292
  # Console.logger.info(self, self.annotation) {"Task was stopped with #{@children&.size.inspect} children!"}
238
293
  @status = :stopped
239
294
 
295
+ # We are not running, but children might be so we should stop them:
240
296
  stop_children(true)
241
297
  end
242
298
 
299
+ def stop!
300
+ stopped!
301
+
302
+ finish!
303
+ end
304
+
243
305
  def schedule(&block)
244
306
  @fiber = Fiber.new do
245
307
  set!
246
308
 
247
309
  begin
248
- @result = yield
249
- @status = :complete
310
+ completed!(yield)
250
311
  # Console.logger.debug(self) {"Task was completed with #{@children.size} children!"}
251
312
  rescue Stop
252
- stop!
313
+ stopped!
253
314
  rescue StandardError => error
254
- fail!(error, false)
315
+ failed!(error, false)
255
316
  rescue Exception => exception
256
- fail!(exception, true)
317
+ failed!(exception, true)
257
318
  ensure
258
319
  # Console.logger.info(self) {"Task ensure $! = #{$!} with #{@children&.size.inspect} children!"}
259
320
  finish!
@@ -263,20 +324,6 @@ module Async
263
324
  self.root.resume(@fiber)
264
325
  end
265
326
 
266
- # Finish the current task, moving any children to the parent.
267
- def finish!
268
- # Allow the fiber to be recycled.
269
- @fiber = nil
270
-
271
- # Attempt to remove this node from the task tree.
272
- consume
273
-
274
- # If this task was being used as a future, signal completion here:
275
- if @finished
276
- @finished.signal(self)
277
- end
278
- end
279
-
280
327
  # Set the current fiber's `:async_task` to this task.
281
328
  def set!
282
329
  # This is actually fiber-local:
data/lib/async/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2017-2022, by Samuel Williams.
5
5
 
6
6
  module Async
7
- VERSION = "2.4.2"
7
+ VERSION = "2.5.0"
8
8
  end
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.2
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -56,7 +56,7 @@ cert_chain:
56
56
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
57
57
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
58
58
  -----END CERTIFICATE-----
59
- date: 2023-03-11 00:00:00.000000000 Z
59
+ date: 2023-03-19 00:00:00.000000000 Z
60
60
  dependencies:
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: console
@@ -100,20 +100,6 @@ dependencies:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
102
  version: '4.1'
103
- - !ruby/object:Gem::Dependency
104
- name: async-rspec
105
- requirement: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '1.1'
110
- type: :development
111
- prerelease: false
112
- version_requirements: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - "~>"
115
- - !ruby/object:Gem::Version
116
- version: '1.1'
117
103
  - !ruby/object:Gem::Dependency
118
104
  name: bake-test
119
105
  requirement: !ruby/object:Gem::Requirement
@@ -261,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
247
  - !ruby/object:Gem::Version
262
248
  version: '0'
263
249
  requirements: []
264
- rubygems_version: 3.4.7
250
+ rubygems_version: 3.4.6
265
251
  signing_key:
266
252
  specification_version: 4
267
253
  summary: A concurrency framework for Ruby.
metadata.gz.sig CHANGED
Binary file