You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# @returns [Array(Thread::Backtrace::Location)] The backtrace of the caller.
16
+
defself.backtrace
17
+
caller_locations(2..-1)
18
+
end
19
+
else
20
+
# @returns [Array(String)] The backtrace of the caller.
21
+
defself.backtrace
22
+
caller(2..-1)
23
+
end
24
+
end
25
+
26
+
# Create a new cause of the stop operation, with the given message.
27
+
#
28
+
# @parameter message [String] The error message.
29
+
# @returns [Cause] The cause of the stop operation.
30
+
defself.for(message="Task was stopped")
31
+
instance=self.new(message)
32
+
instance.set_backtrace(self.backtrace)
33
+
returninstance
34
+
end
35
+
end
36
+
37
+
ifRUBY_VERSION < "3.5"
38
+
# Create a new stop operation.
39
+
#
40
+
# This is a compatibility method for Ruby versions before 3.5 where cause is not propagated correctly when using {Fiber#raise}
41
+
#
42
+
# @parameter message [String | Hash] The error message or a hash containing the cause.
43
+
definitialize(message="Task was stopped")
44
+
ifmessage.is_a?(Hash)
45
+
@cause=message[:cause]
46
+
message="Task was stopped"
47
+
end
48
+
49
+
super(message)
50
+
end
51
+
52
+
# @returns [Exception] The cause of the stop operation.
53
+
#
54
+
# This is a compatibility method for Ruby versions before 3.5 where cause is not propagated correctly when using {Fiber#raise}, we explicitly capture the cause here.
55
+
defcause
56
+
super || @cause
57
+
end
58
+
end
59
+
60
+
# Used to defer stopping the current task until later.
61
+
classLater
62
+
# Create a new stop later operation.
63
+
#
64
+
# @parameter task [Task] The task to stop later.
65
+
# @parameter cause [Exception] The cause of the stop operation.
66
+
definitialize(task,cause=nil)
67
+
@task=task
68
+
@cause=cause
69
+
end
70
+
71
+
# @returns [Boolean] Whether the task is alive.
72
+
defalive?
73
+
true
74
+
end
75
+
76
+
# Transfer control to the operation - this will stop the task.
Copy file name to clipboardExpand all lines: lib/async/task.rb
+15-31Lines changed: 15 additions & 31 deletions
Original file line number
Diff line number
Diff line change
@@ -13,33 +13,11 @@
13
13
14
14
require_relative"node"
15
15
require_relative"condition"
16
+
require_relative"stop"
16
17
17
18
Fiber.attr_accessor:async_task
18
19
19
20
moduleAsync
20
-
# Raised when a task is explicitly stopped.
21
-
classStop < Exception
22
-
# Used to defer stopping the current task until later.
23
-
classLater
24
-
# Create a new stop later operation.
25
-
#
26
-
# @parameter task [Task] The task to stop later.
27
-
definitialize(task)
28
-
@task=task
29
-
end
30
-
31
-
# @returns [Boolean] Whether the task is alive.
32
-
defalive?
33
-
true
34
-
end
35
-
36
-
# Transfer control to the operation - this will stop the task.
37
-
deftransfer
38
-
@task.stop
39
-
end
40
-
end
41
-
end
42
-
43
21
# Raised if a timeout occurs on a specific Fiber. Handled gracefully by `Task`.
44
22
# @public Since *Async v1*.
45
23
classTimeoutError < StandardError
@@ -271,7 +249,13 @@ def wait
271
249
# If `later` is false, it means that `stop` has been invoked directly. When `later` is true, it means that `stop` is invoked by `stop_children` or some other indirect mechanism. In that case, if we encounter the "current" fiber, we can't stop it right away, as it's currently performing `#stop`. Stopping it immediately would interrupt the current stop traversal, so we need to schedule the stop to occur later.
272
250
#
273
251
# @parameter later [Boolean] Whether to stop the task later, or immediately.
274
-
defstop(later=false)
252
+
# @parameter cause [Exception] The cause of the stop operation.
253
+
defstop(later=false,cause: $!)
254
+
# If no cause is given, we generate one from the current call stack:
255
+
unlesscause
256
+
cause=Stop::Cause.for("Stopping task!")
257
+
end
258
+
275
259
ifself.stopped?
276
260
# 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.
277
261
returnstopped!
@@ -285,27 +269,27 @@ def stop(later = false)
285
269
# If we are deferring stop...
286
270
if@defer_stop == false
287
271
# Don't stop now... but update the state so we know we need to stop later.
288
-
@defer_stop=true
272
+
@defer_stop=cause
289
273
returnfalse
290
274
end
291
275
292
276
ifself.current?
293
277
# If the fiber is current, and later is `true`, we need to schedule the fiber to be stopped later, as it's currently invoking `stop`:
294
278
iflater
295
279
# If the fiber is the current fiber and we want to stop it later, schedule it:
296
-
Fiber.scheduler.push(Stop::Later.new(self))
280
+
Fiber.scheduler.push(Stop::Later.new(self,cause))
297
281
else
298
282
# Otherwise, raise the exception directly:
299
-
raiseStop,"Stopping current task!"
283
+
raiseStop,"Stopping current task!",cause: cause
300
284
end
301
285
else
302
286
# If the fiber is not curent, we can raise the exception directly:
303
287
begin
304
288
# There is a chance that this will stop the fiber that originally called stop. If that happens, the exception handling in `#stopped` will rescue the exception and re-raise it later.
305
-
Fiber.scheduler.raise(@fiber,Stop)
289
+
Fiber.scheduler.raise(@fiber,Stop,cause: cause)
306
290
rescueFiberError
307
291
# In some cases, this can cause a FiberError (it might be resumed already), so we schedule it to be stopped later:
308
-
Fiber.scheduler.push(Stop::Later.new(self))
292
+
Fiber.scheduler.push(Stop::Later.new(self,cause))
309
293
end
310
294
end
311
295
else
@@ -345,7 +329,7 @@ def defer_stop
345
329
346
330
# If we were asked to stop, we should do so now:
347
331
ifdefer_stop
348
-
raiseStop,"Stopping current task (was deferred)!"
332
+
raiseStop,"Stopping current task (was deferred)!",cause: defer_stop
349
333
end
350
334
end
351
335
else
@@ -356,7 +340,7 @@ def defer_stop
356
340
357
341
# @returns [Boolean] Whether stop has been deferred.
358
342
defstop_deferred?
359
-
@defer_stop
343
+
!!@defer_stop
360
344
end
361
345
362
346
# Lookup the {Task} for the current fiber. Raise `RuntimeError` if none is available.
0 commit comments