stackdriver-core 1.1.0 → 1.2.0
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/stackdriver/core.rb +1 -0
- data/lib/stackdriver/core/async_actor.rb +359 -0
- data/lib/stackdriver/core/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bb62b8cdb5f28de4f303da6a8ff2e0a31d6cbcd
|
4
|
+
data.tar.gz: 2e6eda3a499805fef92f27a3d0b5167d8d508e31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 122c12d22560686b2a45f63aca85b33d68876d5d3a513dc5bf8deef952fa060dd4065f948447dc096ef96ec0e29102fb70d429bbdba0b18becda93c141e2faa8
|
7
|
+
data.tar.gz: c88f340bd0a325928c9d94d2587e8c7c72a38f1735631416541070a1b8ed30999b725b871bbca1e5580d8baeaff451708ed4c3c761f3a206e1500ef9e44c4037
|
data/lib/stackdriver/core.rb
CHANGED
@@ -0,0 +1,359 @@
|
|
1
|
+
# Copyright 2017 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "set"
|
16
|
+
|
17
|
+
|
18
|
+
module Stackdriver
|
19
|
+
module Core
|
20
|
+
##
|
21
|
+
# # AsyncActor
|
22
|
+
#
|
23
|
+
# @private An module that provides a class asynchronous capability when
|
24
|
+
# included. It can create a child thread to run background jobs, and
|
25
|
+
# help make sure the child thread terminates properly when process
|
26
|
+
# is interrupted.
|
27
|
+
#
|
28
|
+
# To use AsyncActor, the classes that are including this module need to
|
29
|
+
# define a run_backgrounder method that does the background job. The
|
30
|
+
# classes can then control the child thread job through instance methods
|
31
|
+
# like async_start, async_stop, etc.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# class Foo
|
35
|
+
# include AsyncActor
|
36
|
+
#
|
37
|
+
# def initialize
|
38
|
+
# super()
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def run_backgrounder
|
42
|
+
# # run async job
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# foo = Foo.new
|
47
|
+
# foo.async_start
|
48
|
+
#
|
49
|
+
module AsyncActor
|
50
|
+
include MonitorMixin
|
51
|
+
|
52
|
+
CLEANUP_TIMEOUT = 10.0
|
53
|
+
WAIT_INTERVAL = 1.0
|
54
|
+
|
55
|
+
@cleanup_list = nil
|
56
|
+
@exit_lock = Monitor.new
|
57
|
+
|
58
|
+
##
|
59
|
+
# @private The async actor state
|
60
|
+
attr_reader :async_state
|
61
|
+
|
62
|
+
##
|
63
|
+
# Starts the child thread and asynchronous job
|
64
|
+
def async_start
|
65
|
+
ensure_thread
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Nicely ask the child thread to stop by setting the state to
|
70
|
+
# :stopping if it's not stopped already.
|
71
|
+
#
|
72
|
+
# @return [Boolean] False if child thread is already stopped. Otherwise
|
73
|
+
# true.
|
74
|
+
def async_stop
|
75
|
+
ensure_thread
|
76
|
+
synchronize do
|
77
|
+
if async_state != :stopped
|
78
|
+
@async_state = :stopping
|
79
|
+
async_state_change
|
80
|
+
true
|
81
|
+
else
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Set the state to :suspend if the current state is :running.
|
89
|
+
#
|
90
|
+
# @return [Boolean] Returns true if the state has been set to
|
91
|
+
# :suspended. Otherwise return false.
|
92
|
+
#
|
93
|
+
def async_suspend
|
94
|
+
ensure_thread
|
95
|
+
synchronize do
|
96
|
+
if async_state == :running
|
97
|
+
@async_state = :suspended
|
98
|
+
async_state_change
|
99
|
+
true
|
100
|
+
else
|
101
|
+
false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Set the state to :running if the current state is :suspended.
|
108
|
+
#
|
109
|
+
# @return [Boolean] True if the state has been set to :running.
|
110
|
+
# Otherwise return false.
|
111
|
+
#
|
112
|
+
def async_resume
|
113
|
+
ensure_thread
|
114
|
+
synchronize do
|
115
|
+
if async_state == :suspended
|
116
|
+
@async_state = :running
|
117
|
+
async_state_change
|
118
|
+
true
|
119
|
+
else
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Check if async job is running
|
127
|
+
#
|
128
|
+
# @return [Boolean] True if state equals :running. Otherwise false.
|
129
|
+
def async_running?
|
130
|
+
synchronize do
|
131
|
+
async_state == :running
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# Check if async job is suspended
|
137
|
+
#
|
138
|
+
# @return [Boolean] True if state equals :suspended. Otherwise false.
|
139
|
+
def async_suspended?
|
140
|
+
synchronize do
|
141
|
+
async_state == :suspended
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Check if async job is working.
|
147
|
+
#
|
148
|
+
# @return [Boolean] True if state is either :running or :suspended.
|
149
|
+
# Otherwise false.
|
150
|
+
def async_working?
|
151
|
+
synchronize do
|
152
|
+
async_state == :suspended || async_state == :running
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Check if async job has stopped
|
158
|
+
#
|
159
|
+
# @return [Boolean] True if state equals :stopped. Otherwise false.
|
160
|
+
def async_stopped?
|
161
|
+
synchronize do
|
162
|
+
async_state == :stopped
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Check if async job is stopping
|
168
|
+
#
|
169
|
+
# @return [Boolean] True if state equals :stopping. Otherwise false.
|
170
|
+
def async_stopping?
|
171
|
+
synchronize do
|
172
|
+
async_state == :stopping
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Ask async job to stop. Then forcefully kill thread if it doesn't stop
|
178
|
+
# after timeout if needed.
|
179
|
+
#
|
180
|
+
# @return [Symbol] :stopped if async job already stopped. :waited if
|
181
|
+
# async job terminates within timeout range. :timeout if async job
|
182
|
+
# doesn't terminate after timeout. :forced if thread is killed by
|
183
|
+
# force after timeout.
|
184
|
+
def async_stop!
|
185
|
+
ensure_thread
|
186
|
+
|
187
|
+
timeout = @cleanup_options[:timeout]
|
188
|
+
force = @cleanup_options[:force]
|
189
|
+
|
190
|
+
return :stopped unless async_stop
|
191
|
+
|
192
|
+
return :waited if timeout.to_f > 0 && wait_until_async_stopped(timeout)
|
193
|
+
|
194
|
+
return :timeout unless force
|
195
|
+
|
196
|
+
@thread.kill
|
197
|
+
@thread.join
|
198
|
+
:forced
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Block current thread until the async job is fully stopped.
|
203
|
+
#
|
204
|
+
# @param [Integer] timeout If given, lift the blocking when the time has
|
205
|
+
# exceeded the timeout. If nil, block indefinitely.
|
206
|
+
#
|
207
|
+
# @return [Boolean] True if async job is fully stopped. False if timeout.
|
208
|
+
#
|
209
|
+
def wait_until_async_stopped timeout = nil
|
210
|
+
ensure_thread
|
211
|
+
deadline = timeout ? ::Time.new.to_f + timeout : nil
|
212
|
+
synchronize do
|
213
|
+
until async_state == :stopped
|
214
|
+
cur_time = ::Time.new.to_f
|
215
|
+
return false if deadline && cur_time >= deadline
|
216
|
+
max_interval = @cleanup_options[:wait_interval]
|
217
|
+
interval = deadline ? deadline - cur_time : max_interval
|
218
|
+
interval = max_interval if interval > max_interval
|
219
|
+
@lock_cond.wait interval
|
220
|
+
end
|
221
|
+
end
|
222
|
+
true
|
223
|
+
end
|
224
|
+
|
225
|
+
##
|
226
|
+
# Abstract method that the inheriting classes should implement.
|
227
|
+
#
|
228
|
+
# This should be the main task job that will be run asynchronously and
|
229
|
+
# repeatly.
|
230
|
+
def run_backgrounder
|
231
|
+
fail "#{self.class} class should override #run_backgrounder method"
|
232
|
+
end
|
233
|
+
|
234
|
+
##
|
235
|
+
# @private Cleanup this async job when process exists
|
236
|
+
#
|
237
|
+
def self.register_for_cleanup actor
|
238
|
+
@exit_lock.synchronize do
|
239
|
+
unless @cleanup_list
|
240
|
+
@cleanup_list = []
|
241
|
+
at_exit { run_cleanup }
|
242
|
+
end
|
243
|
+
@cleanup_list.push actor
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
##
|
248
|
+
# @private Take this async job off exit cleanup list
|
249
|
+
#
|
250
|
+
def self.unregister_for_cleanup actor
|
251
|
+
@exit_lock.synchronize do
|
252
|
+
@cleanup_list.delete actor if @cleanup_list
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
##
|
257
|
+
# @private Cleanup the async job
|
258
|
+
#
|
259
|
+
def self.run_cleanup
|
260
|
+
@exit_lock.synchronize do
|
261
|
+
if @cleanup_list
|
262
|
+
@cleanup_list.shift.async_stop! until @cleanup_list.empty?
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
private_class_method :run_cleanup
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
##
|
272
|
+
# @private Constructor to initialize MonitorMixin
|
273
|
+
#
|
274
|
+
def initialize
|
275
|
+
super()
|
276
|
+
@startup_lock = Mutex.new
|
277
|
+
@cleanup_options = {
|
278
|
+
wait_interval: WAIT_INTERVAL,
|
279
|
+
timeout: CLEANUP_TIMEOUT,
|
280
|
+
force: true
|
281
|
+
}
|
282
|
+
end
|
283
|
+
|
284
|
+
##
|
285
|
+
# @private Wrapper method for running the async job. It runs a loop
|
286
|
+
# while the condition allows, and it calls the run_backgrounder method.
|
287
|
+
# This method also ensures the state variable gets set to :stopped when
|
288
|
+
# it exits.
|
289
|
+
def async_run_job
|
290
|
+
until async_stopped?
|
291
|
+
run_backgrounder
|
292
|
+
|
293
|
+
return if async_stopping? && backgrounder_stoppable?
|
294
|
+
end
|
295
|
+
ensure
|
296
|
+
@async_state = :stopped
|
297
|
+
async_state_change
|
298
|
+
end
|
299
|
+
|
300
|
+
##
|
301
|
+
# @private Ensures the child thread is started and kick off the job
|
302
|
+
# to run async_run_job. Also make calls register_for_cleanup on the
|
303
|
+
# async job to make sure it exits properly.
|
304
|
+
def ensure_thread
|
305
|
+
fail "async_actor not initialized" if @startup_lock.nil?
|
306
|
+
@startup_lock.synchronize do
|
307
|
+
if (@thread.nil? || !@thread.alive?) && @async_state != :stopped
|
308
|
+
@lock_cond = new_cond
|
309
|
+
AsyncActor.register_for_cleanup self
|
310
|
+
|
311
|
+
@async_state = :running
|
312
|
+
async_state_change
|
313
|
+
|
314
|
+
@thread = Thread.new do
|
315
|
+
async_run_job
|
316
|
+
AsyncActor.unregister_for_cleanup self
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
##
|
323
|
+
# @private Set cleanup options.
|
324
|
+
#
|
325
|
+
# @param [Hash] *kwargs Hash of cleanup options. :timeout is the cleanup
|
326
|
+
# wait timeout. :wait_interval is the cleanup wait interval. :force for
|
327
|
+
# forcefully terminate actor when all other options fail.
|
328
|
+
def set_cleanup_options **kwargs
|
329
|
+
@cleanup_options.merge! kwargs
|
330
|
+
end
|
331
|
+
|
332
|
+
##
|
333
|
+
# @private Default backgrounder stop condition when asked to be stopped
|
334
|
+
# gracefully. Called from #async_run_job when async actor state changes
|
335
|
+
# to :stopping
|
336
|
+
def backgrounder_stoppable?
|
337
|
+
true
|
338
|
+
end
|
339
|
+
|
340
|
+
##
|
341
|
+
# @private Handler when the async actor's state changes. Call
|
342
|
+
# the `on_async_state_change` callback function if it's defined.
|
343
|
+
def async_state_change
|
344
|
+
on_async_state_change
|
345
|
+
|
346
|
+
synchronize do
|
347
|
+
@lock_cond.broadcast
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
##
|
352
|
+
# @private Default abstract definition of this function that's a no-op.
|
353
|
+
# The extending classes can override this method to handle state changing
|
354
|
+
# logic.
|
355
|
+
def on_async_state_change
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackdriver-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Azuma
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -150,6 +150,7 @@ files:
|
|
150
150
|
- lib/google/cloud/configuration.rb
|
151
151
|
- lib/stackdriver-core.rb
|
152
152
|
- lib/stackdriver/core.rb
|
153
|
+
- lib/stackdriver/core/async_actor.rb
|
153
154
|
- lib/stackdriver/core/configuration.rb
|
154
155
|
- lib/stackdriver/core/trace_context.rb
|
155
156
|
- lib/stackdriver/core/version.rb
|