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
Copy file name to clipboardExpand all lines: lib/async/task.rb
+42-10Lines changed: 42 additions & 10 deletions
Original file line number
Diff line number
Diff line change
@@ -18,13 +18,39 @@
18
18
moduleAsync
19
19
# Raised when a task is explicitly stopped.
20
20
classStop < Exception
21
+
# Represents the source of the stop operation.
22
+
classCause < Exception
23
+
ifRUBY_VERSION >= "3.2"
24
+
defself.backtrace
25
+
caller_locations(2..-1)
26
+
end
27
+
else
28
+
defself.backtrace
29
+
caller(2..-1)
30
+
end
31
+
end
32
+
33
+
defself.for(message="Task was stopped")
34
+
instance=self.new(message)
35
+
instance.set_backtrace(self.backtrace)
36
+
returninstance
37
+
end
38
+
end
39
+
40
+
# Create a new stop operation.
41
+
definitialize(message="Task was stopped")
42
+
super(message)
43
+
end
44
+
21
45
# Used to defer stopping the current task until later.
22
46
classLater
23
47
# Create a new stop later operation.
24
48
#
25
49
# @parameter task [Task] The task to stop later.
26
-
definitialize(task)
50
+
# @parameter cause [Exception] The cause of the stop operation.
51
+
definitialize(task,cause=nil)
27
52
@task=task
53
+
@cause=cause
28
54
end
29
55
30
56
# @returns [Boolean] Whether the task is alive.
@@ -34,7 +60,7 @@ def alive?
34
60
35
61
# Transfer control to the operation - this will stop the task.
36
62
deftransfer
37
-
@task.stop
63
+
@task.stop(false,cause: @cause)
38
64
end
39
65
end
40
66
end
@@ -266,7 +292,13 @@ def wait
266
292
# 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.
267
293
#
268
294
# @parameter later [Boolean] Whether to stop the task later, or immediately.
269
-
defstop(later=false)
295
+
# @parameter cause [Exception] The cause of the stop operation.
296
+
defstop(later=false,cause: $!)
297
+
# If no cause is given, we generate one from the current call stack:
298
+
unlesscause
299
+
cause=Stop::Cause.for("Stopping task!")
300
+
end
301
+
270
302
ifself.stopped?
271
303
# 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.
272
304
returnstopped!
@@ -280,27 +312,27 @@ def stop(later = false)
280
312
# If we are deferring stop...
281
313
if@defer_stop == false
282
314
# Don't stop now... but update the state so we know we need to stop later.
283
-
@defer_stop=true
315
+
@defer_stop=cause
284
316
returnfalse
285
317
end
286
318
287
319
ifself.current?
288
320
# 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`:
289
321
iflater
290
322
# If the fiber is the current fiber and we want to stop it later, schedule it:
291
-
Fiber.scheduler.push(Stop::Later.new(self))
323
+
Fiber.scheduler.push(Stop::Later.new(self,cause))
292
324
else
293
325
# Otherwise, raise the exception directly:
294
-
raiseStop,"Stopping current task!"
326
+
raiseStop,"Stopping current task!",cause: cause
295
327
end
296
328
else
297
329
# If the fiber is not curent, we can raise the exception directly:
298
330
begin
299
331
# 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.
300
-
Fiber.scheduler.raise(@fiber,Stop)
332
+
Fiber.scheduler.raise(@fiber,Stop,cause: cause)
301
333
rescueFiberError
302
334
# In some cases, this can cause a FiberError (it might be resumed already), so we schedule it to be stopped later:
303
-
Fiber.scheduler.push(Stop::Later.new(self))
335
+
Fiber.scheduler.push(Stop::Later.new(self,cause))
304
336
end
305
337
end
306
338
else
@@ -340,7 +372,7 @@ def defer_stop
340
372
341
373
# If we were asked to stop, we should do so now:
342
374
ifdefer_stop
343
-
raiseStop,"Stopping current task (was deferred)!"
375
+
raiseStop,"Stopping current task (was deferred)!",cause: defer_stop
344
376
end
345
377
end
346
378
else
@@ -351,7 +383,7 @@ def defer_stop
351
383
352
384
# @returns [Boolean] Whether stop has been deferred.
353
385
defstop_deferred?
354
-
@defer_stop
386
+
!!@defer_stop
355
387
end
356
388
357
389
# Lookup the {Task} for the current fiber. Raise `RuntimeError` if none is available.
0 commit comments