@@ -25,6 +25,7 @@ def initialize(opts = {})
25
25
@queue = PriorityQueue . new ( order : :min )
26
26
@executor = get_executor_from ( opts )
27
27
@thread = nil
28
+ @condition = Condition . new
28
29
init_executor
29
30
end
30
31
@@ -43,10 +44,21 @@ def initialize(opts = {})
43
44
# @raise [ArgumentError] if no block is given
44
45
def post ( intended_time , &task )
45
46
time = TimerSet . calculate_schedule_time ( intended_time ) . to_f
46
- if super ( time , &task )
47
- check_processing_thread!
47
+ raise ArgumentError . new ( 'no block given' ) unless block_given?
48
+
49
+ mutex . synchronize do
50
+ return false unless running?
51
+
52
+ if ( time - Time . now . to_f ) <= 0.01
53
+ @executor . post ( &task )
54
+ else
55
+ @queue . push ( Task . new ( time , task ) )
56
+ check_processing_thread!
57
+ end
58
+
48
59
true
49
60
end
61
+
50
62
end
51
63
52
64
alias_method :kill , :shutdown
@@ -84,19 +96,13 @@ def self.calculate_schedule_time(intended_time, now = Time.now)
84
96
# @!visibility private
85
97
Task = Struct . new ( :time , :op ) do
86
98
include Comparable
99
+
87
100
def <=>( other )
88
101
self . time <=> other . time
89
102
end
90
103
end
91
104
92
- # @!visibility private
93
- def execute ( time , &task )
94
- if ( time - Time . now . to_f ) <= 0.01
95
- @executor . post ( &task )
96
- else
97
- @queue . push ( Task . new ( time , task ) )
98
- end
99
- end
105
+ private_constant :Task
100
106
101
107
# @!visibility private
102
108
def shutdown_execution
@@ -114,50 +120,14 @@ def shutdown_execution
114
120
#
115
121
# @!visibility private
116
122
def check_processing_thread!
117
- mutex . synchronize do
118
- return if shutdown? || @queue . empty?
119
- if @thread && @thread . status == 'sleep'
120
- @thread . wakeup
121
- elsif @thread . nil? || ! @thread . alive?
122
- @thread = Thread . new do
123
- Thread . current . abort_on_exception = true
124
- process_tasks
125
- end
126
- end
127
- end
128
- end
123
+ return if shutdown? || @queue . empty?
129
124
130
- # Check the head of the internal task queue for a ready task.
131
- #
132
- # @return [Task] the next task to be executed or nil if none are ready
133
- #
134
- # @!visibility private
135
- def next_task
136
- mutex . synchronize do
137
- unless @queue . empty? || @queue . peek . time > Time . now . to_f
138
- @queue . pop
139
- else
140
- nil
141
- end
125
+ @thread ||= Thread . new do
126
+ Thread . current . abort_on_exception = true
127
+ process_tasks
142
128
end
143
129
end
144
130
145
- # Calculate the time difference, in seconds and milliseconds, between
146
- # now and the intended execution time of the next task to be ececuted.
147
- #
148
- # @return [Integer] the number of seconds and milliseconds to sleep
149
- # or nil if the task queue is empty
150
- #
151
- # @!visibility private
152
- def next_sleep_interval
153
- mutex . synchronize do
154
- if @queue . empty?
155
- nil
156
- else
157
- @queue . peek . time - Time . now . to_f
158
- end
159
- end
160
- end
161
131
162
132
# Run a loop and execute tasks in the scheduled order and at the approximate
163
133
# scheduled time. If no tasks remain the thread will exit gracefully so that
@@ -167,13 +137,22 @@ def next_sleep_interval
167
137
# @!visibility private
168
138
def process_tasks
169
139
loop do
170
- while task = next_task do
171
- @executor . post ( &task . op )
172
- end
173
- if ( interval = next_sleep_interval ) . nil?
174
- break
175
- else
176
- sleep ( [ interval , 60 ] . min )
140
+
141
+ mutex . synchronize do
142
+ if @queue . empty?
143
+ @thread = nil
144
+ break
145
+ end
146
+
147
+ task = @queue . peek
148
+ interval = task . time - Time . now . to_f
149
+
150
+ if interval <= 0
151
+ @executor . post ( &task . op )
152
+ @queue . pop
153
+ else
154
+ @condition . wait ( mutex , [ interval , 60 ] . min )
155
+ end
177
156
end
178
157
end
179
158
end
0 commit comments