timemachine 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/executor.rb +8 -0
- data/lib/thread_executor.rb +5 -0
- data/lib/timemachine.rb +88 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 412ae253c1cf57715d748a498fe09daf146813c9ec7e9592ca6eccfd6e905b8c
|
|
4
|
+
data.tar.gz: 767286f421b3706e1ae7e9ad7c69c5c26ddf2808494e03f67bf7d1beba29523c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 521d3ec7fd877e8b42532fc371044b7bc6d5bcf45ffd5905be5a708d00579973be49e57937af99a2e85208b8d448839bea76b07a12475713c33d480184b472fc
|
|
7
|
+
data.tar.gz: dc7e27cc48e54c8d4a3ded22d3e4a5e11da96183389f2864bdb71e028340555d9deb52c9bd4aa2461c2e81c1ef5c790c382977a23859f6a5c139e2995e77bdaf
|
data/lib/executor.rb
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module TimeMachine
|
|
4
|
+
#
|
|
5
|
+
# Executors contains executor interface and implementations.
|
|
6
|
+
# An executor is where a timed-up task is actually executed,
|
|
7
|
+
# thus decoupled with the scheduler.
|
|
8
|
+
#
|
|
4
9
|
module Executors
|
|
10
|
+
#
|
|
11
|
+
# The base class (interface) for a executor.
|
|
12
|
+
#
|
|
5
13
|
class Executor
|
|
6
14
|
def execute(&block)
|
|
7
15
|
raise NotImplementedError
|
data/lib/thread_executor.rb
CHANGED
|
@@ -4,6 +4,11 @@ require 'executor'
|
|
|
4
4
|
|
|
5
5
|
module TimeMachine
|
|
6
6
|
module Executors
|
|
7
|
+
#
|
|
8
|
+
# ThreadExecutor is currently the only implementation of Executor.
|
|
9
|
+
# It executes the task in a background thread, so slow tasks will not
|
|
10
|
+
# block the scheduler.
|
|
11
|
+
#
|
|
7
12
|
class ThreadExecutor < Executor
|
|
8
13
|
def execute(&block)
|
|
9
14
|
Thread.new(&block)
|
data/lib/timemachine.rb
CHANGED
|
@@ -2,18 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
require 'thread_executor'
|
|
4
4
|
|
|
5
|
+
#
|
|
6
|
+
# Namespace TimeMachine contains all the magics of this gem.
|
|
7
|
+
#
|
|
5
8
|
module TimeMachine
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# The internal struct to store a task.
|
|
12
|
+
#
|
|
6
13
|
TimedTask = Struct.new(:handle, :timeup, :block)
|
|
14
|
+
|
|
15
|
+
#
|
|
16
|
+
# The struct to store the result of a task.
|
|
17
|
+
#
|
|
7
18
|
TaskResult = Struct.new(:handle, :status, :record_result, :result)
|
|
8
19
|
|
|
20
|
+
#
|
|
21
|
+
# `DuplicateHandleError` is thrown when the handle (name) for a new task collides with a existing one.
|
|
22
|
+
#
|
|
9
23
|
class DuplicateHandleError < RuntimeError; end
|
|
10
24
|
|
|
25
|
+
#
|
|
26
|
+
# The core of this gem, which is responsible for scheduling tasks.
|
|
27
|
+
#
|
|
11
28
|
class TimeMachine
|
|
29
|
+
# Default timeout of the scheduler, used when the scheduler is idle.
|
|
12
30
|
SCHEDULER_DEFAULT_TIMEOUT = 3
|
|
31
|
+
|
|
32
|
+
# Length of the handle generated by `TimeMachine`.
|
|
13
33
|
RANDOM_HANDLE_LENGTH = 16
|
|
34
|
+
|
|
14
35
|
private_constant :SCHEDULER_DEFAULT_TIMEOUT
|
|
15
36
|
|
|
16
37
|
public
|
|
38
|
+
#
|
|
39
|
+
# Create a new `TimeMachine`.
|
|
40
|
+
#
|
|
41
|
+
# @param [Executors::Executor] executor The task executor to be used. Defaults to a `Executors::ThreadExecutor`.
|
|
42
|
+
# @param [Boolean] more_timely Use a lock in scheduler. This avoids a border case that a task is submitted while
|
|
43
|
+
# the scheduler is scheduling, thus not asleep. If `more_timely` is set to `false` in such case, the task
|
|
44
|
+
# submission will not effectively preempt the scheduler, and will have to wait one more round before it is scheduled,
|
|
45
|
+
# and therefore may cause a slight delay.
|
|
46
|
+
#
|
|
17
47
|
def initialize(executor= Executors::ThreadExecutor.new, more_timely=false)
|
|
18
48
|
@running = false
|
|
19
49
|
@timeout_queue = []
|
|
@@ -28,6 +58,18 @@ module TimeMachine
|
|
|
28
58
|
@user_mut = Mutex.new
|
|
29
59
|
end
|
|
30
60
|
|
|
61
|
+
#
|
|
62
|
+
# Execute the given block at the given timeup.
|
|
63
|
+
#
|
|
64
|
+
# @param [Time] timeup The time to invoke the given block
|
|
65
|
+
# @param [String] name A custom name (handle) for the task. If `nil`, one will be generated. If specified,
|
|
66
|
+
# should not be duplicate with an existing one.
|
|
67
|
+
# @param [Boolean] record_result Whether to save the result after completion.
|
|
68
|
+
# @param [Proc] &block The block to execute.
|
|
69
|
+
#
|
|
70
|
+
# @return [String] A handle to the task, either the name passed in (if given) or generated by `TimeMachine`.
|
|
71
|
+
# Can be used to cancel the task or query task status and result (if `record_result` is set).
|
|
72
|
+
#
|
|
31
73
|
def at(timeup, name: nil, record_result: false, &block)
|
|
32
74
|
handle = name || _generate_handle
|
|
33
75
|
|
|
@@ -44,16 +86,46 @@ module TimeMachine
|
|
|
44
86
|
handle
|
|
45
87
|
end
|
|
46
88
|
|
|
89
|
+
#
|
|
90
|
+
# Execute the given block after given timeout.
|
|
91
|
+
#
|
|
92
|
+
# @param [Float] timeout Timeout in seconds.
|
|
93
|
+
# @param [String] name A custom name (handle) for the task. If `nil`, one will be generated. If specified,
|
|
94
|
+
# should not be duplicate with an existing one.
|
|
95
|
+
# @param [Boolean] record_result Whether to save the result after completion.
|
|
96
|
+
# @param [Proc] &block The block to execute.
|
|
97
|
+
#
|
|
98
|
+
# @return [String] A handle to the task, either the name passed in (if given) or generated by `TimeMachine`.
|
|
99
|
+
# Can be used to cancel the task or query task status and result (if `record_result` is set).
|
|
100
|
+
#
|
|
47
101
|
def after(timeout, name: nil, record_result: false, &block)
|
|
48
102
|
at(Time.now + timeout, name: name, record_result: record_result, &block)
|
|
49
103
|
end
|
|
50
104
|
|
|
105
|
+
#
|
|
106
|
+
# Get the result of a given block.
|
|
107
|
+
#
|
|
108
|
+
#
|
|
109
|
+
# @param [String] handle The handle to the task, typically returned by previous `at` and `after` calls.
|
|
110
|
+
#
|
|
111
|
+
# @return [TaskResult] The result of the given task. If not found, or the task is finished with `record_result`
|
|
112
|
+
# unset, returns `nil`.
|
|
113
|
+
#
|
|
51
114
|
def get_result(handle)
|
|
52
115
|
@user_mut.synchronize do
|
|
53
116
|
@result_queue[handle]
|
|
54
117
|
end
|
|
55
118
|
end
|
|
56
119
|
|
|
120
|
+
#
|
|
121
|
+
# Get the result of a given block, and delete it from `TimeMachine`'s internal storage if it is finished or cancelled.
|
|
122
|
+
# *Remember to call this, or the results will pile up and eat your memory!*
|
|
123
|
+
#
|
|
124
|
+
# @param [String] handle The handle to the task, typically returned by previous `at` and `after` calls.
|
|
125
|
+
#
|
|
126
|
+
# @return [TaskResult] The result of the given task. If not found, or the task is finished with `record_result`
|
|
127
|
+
# unset, returns `nil`. If the task is finished or cancelled, it will be deleted from `TimeMachine` storage as well.
|
|
128
|
+
#
|
|
57
129
|
def pop_result(handle)
|
|
58
130
|
@user_mut.synchronize do
|
|
59
131
|
res = @result_queue[handle]
|
|
@@ -65,6 +137,13 @@ module TimeMachine
|
|
|
65
137
|
end
|
|
66
138
|
end
|
|
67
139
|
|
|
140
|
+
#
|
|
141
|
+
# Cancel a previously submitted task.
|
|
142
|
+
# Be sure to cancel it in time if you mean to cancel it.
|
|
143
|
+
# *If the task has already been scheduled or executed, cancel may not take effect.*
|
|
144
|
+
#
|
|
145
|
+
# @param [String] handle The handle to the task, typically returned by previous `at` and `after` calls.
|
|
146
|
+
#
|
|
68
147
|
def cancel(handle)
|
|
69
148
|
@user_mut.synchronize do
|
|
70
149
|
@cancel_queue << handle
|
|
@@ -72,6 +151,9 @@ module TimeMachine
|
|
|
72
151
|
_bg_wakeup
|
|
73
152
|
end
|
|
74
153
|
|
|
154
|
+
#
|
|
155
|
+
# Start the `TimeMachine`. This will create a background scheduler thread.
|
|
156
|
+
#
|
|
75
157
|
def start()
|
|
76
158
|
@user_mut.synchronize do
|
|
77
159
|
return if @running
|
|
@@ -80,6 +162,9 @@ module TimeMachine
|
|
|
80
162
|
end
|
|
81
163
|
end
|
|
82
164
|
|
|
165
|
+
#
|
|
166
|
+
# Stop the `TimeMachine`. All tasks due after this call will be cancelled.
|
|
167
|
+
#
|
|
83
168
|
def stop()
|
|
84
169
|
@user_mut.synchronize do
|
|
85
170
|
return unless @running
|
|
@@ -100,7 +185,7 @@ module TimeMachine
|
|
|
100
185
|
|
|
101
186
|
def _bg_wakeup
|
|
102
187
|
@scheduler_mut&.lock
|
|
103
|
-
@scheduler_thread
|
|
188
|
+
@scheduler_thread&.run
|
|
104
189
|
@scheduler_mut&.unlock
|
|
105
190
|
end
|
|
106
191
|
|
|
@@ -135,10 +220,11 @@ module TimeMachine
|
|
|
135
220
|
@scheduler_mut&.lock
|
|
136
221
|
end
|
|
137
222
|
@scheduler_mut&.unlock
|
|
223
|
+
@scheduler_thread = nil
|
|
138
224
|
end
|
|
139
225
|
|
|
140
226
|
def _bg_enqueueTask(task)
|
|
141
|
-
return if @result_queue[task.handle]
|
|
227
|
+
return if @result_queue[task.handle]&.status != :UNSCHEDULED
|
|
142
228
|
|
|
143
229
|
@result_queue[task.handle].status = :PENDING
|
|
144
230
|
@timeout_queue.insert(@timeout_queue.index do |t|
|