Class: Yobi::Cancellation
- Inherits:
-
Object
- Object
- Yobi::Cancellation
- Defined in:
- lib/yobi/cancellation.rb,
sig/yobi.rbs
Overview
A cancellation token for a long-running Restic run.
Pass one to Yobi::Repository#with_cancellation, then call #cancel! from another thread to stop whichever long-running method (#backup, #restore, #check, #prune, #forget, #copy) is running on the repository it returns:
token = Yobi::Cancellation.new
Thread.new { token.cancel! if user_clicked_cancel }
repo.with_cancellation(token).backup(source: "/data")
# => raises Yobi::Cancelled
#cancel! sends the Restic process SIGINT rather than SIGKILL, so Restic
removes its own repository lock on the way out and no restic unlock is
needed afterwards. An interrupted backup writes no snapshot; data it had
already uploaded is generally left unreferenced until the next prune
rather than being reused, so cancelling does discard in-progress work.
Safe to call #cancel! before the run starts (the run then raises without spawning Restic at all), after it has finished (a no-op), and from any thread. A token tracks one run at a time and is not reusable once cancelled.
Constant Summary collapse
- DEFAULT_SIGNAL =
The signal sent to Restic by #cancel! unless overridden.
"INT"
Instance Method Summary collapse
-
#attach(pid) ⇒ void
Attaches a freshly spawned Restic process.
-
#cancel!(signal: DEFAULT_SIGNAL) ⇒ Boolean
Requests cancellation, signalling the attached Restic process if one is currently running.
-
#cancelled? ⇒ Boolean
True once #cancel! has been called.
-
#detach ⇒ void
Detaches the current process once it has been reaped, so a later #cancel! can't signal a recycled PID.
-
#initialize ⇒ Cancellation
constructor
A new instance of Cancellation.
- #inspect ⇒ String
Constructor Details
#initialize ⇒ Cancellation
Returns a new instance of Cancellation.
30 31 32 33 34 35 |
# File 'lib/yobi/cancellation.rb', line 30 def initialize @mutex = Mutex.new @cancelled = false @signal = DEFAULT_SIGNAL @pid = nil end |
Instance Method Details
#attach(pid) ⇒ void
This method returns an undefined value.
Attaches a freshly spawned Restic process. If #cancel! already landed before the spawn, the process is signalled immediately. Called by Yobi::Restic; not part of the public API.
64 65 66 67 68 69 70 |
# File 'lib/yobi/cancellation.rb', line 64 def attach(pid) # :nodoc: cancelled, signal = @mutex.synchronize do @pid = pid [@cancelled, @signal] end signal_pid(pid, signal) if cancelled end |
#cancel!(signal: DEFAULT_SIGNAL) ⇒ Boolean
Requests cancellation, signalling the attached Restic process if one is currently running. Returns true the first time, false if this token was already cancelled. Idempotent and thread-safe.
signal: overrides the signal sent; the SIGINT default is what lets
Restic clean up its lock, so override it only when you have a reason to.
48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/yobi/cancellation.rb', line 48 def cancel!(signal: DEFAULT_SIGNAL) pid = nil @mutex.synchronize do return false if @cancelled @cancelled = true @signal = signal pid = @pid end signal_pid(pid, signal) true end |
#cancelled? ⇒ Boolean
True once #cancel! has been called.
38 39 40 |
# File 'lib/yobi/cancellation.rb', line 38 def cancelled? @mutex.synchronize { @cancelled } end |
#detach ⇒ void
This method returns an undefined value.
Detaches the current process once it has been reaped, so a later #cancel! can't signal a recycled PID. Called by Yobi::Restic; not part of the public API.
75 76 77 |
# File 'lib/yobi/cancellation.rb', line 75 def detach # :nodoc: @mutex.synchronize { @pid = nil } end |
#inspect ⇒ String
79 80 81 |
# File 'lib/yobi/cancellation.rb', line 79 def inspect "#<#{self.class} cancelled=#{cancelled?}>" end |