background_queue 0.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.
Files changed (91) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +48 -0
  4. data/Gemfile +19 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +69 -0
  7. data/Rakefile +42 -0
  8. data/TODO +2 -0
  9. data/VERSION +1 -0
  10. data/background_queue.gemspec +158 -0
  11. data/bin/bg_queue +26 -0
  12. data/lib/background_queue.rb +8 -0
  13. data/lib/background_queue/client.rb +96 -0
  14. data/lib/background_queue/client_lib/command.rb +36 -0
  15. data/lib/background_queue/client_lib/config.rb +109 -0
  16. data/lib/background_queue/client_lib/connection.rb +105 -0
  17. data/lib/background_queue/client_lib/job_handle.rb +19 -0
  18. data/lib/background_queue/command.rb +49 -0
  19. data/lib/background_queue/config.rb +118 -0
  20. data/lib/background_queue/server_lib/balanced_queue.rb +108 -0
  21. data/lib/background_queue/server_lib/config.rb +339 -0
  22. data/lib/background_queue/server_lib/event_connection.rb +133 -0
  23. data/lib/background_queue/server_lib/event_server.rb +35 -0
  24. data/lib/background_queue/server_lib/job.rb +252 -0
  25. data/lib/background_queue/server_lib/job_registry.rb +30 -0
  26. data/lib/background_queue/server_lib/lru.rb +193 -0
  27. data/lib/background_queue/server_lib/owner.rb +54 -0
  28. data/lib/background_queue/server_lib/priority_queue.rb +156 -0
  29. data/lib/background_queue/server_lib/queue_registry.rb +123 -0
  30. data/lib/background_queue/server_lib/server.rb +314 -0
  31. data/lib/background_queue/server_lib/sorted_workers.rb +52 -0
  32. data/lib/background_queue/server_lib/task.rb +79 -0
  33. data/lib/background_queue/server_lib/task_registry.rb +51 -0
  34. data/lib/background_queue/server_lib/thread_manager.rb +121 -0
  35. data/lib/background_queue/server_lib/worker.rb +18 -0
  36. data/lib/background_queue/server_lib/worker_balancer.rb +97 -0
  37. data/lib/background_queue/server_lib/worker_client.rb +94 -0
  38. data/lib/background_queue/server_lib/worker_thread.rb +70 -0
  39. data/lib/background_queue/utils.rb +40 -0
  40. data/lib/background_queue/worker/base.rb +46 -0
  41. data/lib/background_queue/worker/calling.rb +59 -0
  42. data/lib/background_queue/worker/config.rb +35 -0
  43. data/lib/background_queue/worker/environment.rb +70 -0
  44. data/lib/background_queue/worker/worker_loader.rb +94 -0
  45. data/lib/background_queue_server.rb +21 -0
  46. data/lib/background_queue_worker.rb +5 -0
  47. data/spec/background_queue/client_lib/command_spec.rb +75 -0
  48. data/spec/background_queue/client_lib/config_spec.rb +156 -0
  49. data/spec/background_queue/client_lib/connection_spec.rb +170 -0
  50. data/spec/background_queue/client_spec.rb +95 -0
  51. data/spec/background_queue/command_spec.rb +34 -0
  52. data/spec/background_queue/config_spec.rb +134 -0
  53. data/spec/background_queue/server_lib/balanced_queue_spec.rb +122 -0
  54. data/spec/background_queue/server_lib/config_spec.rb +443 -0
  55. data/spec/background_queue/server_lib/event_connection_spec.rb +190 -0
  56. data/spec/background_queue/server_lib/event_server_spec.rb +48 -0
  57. data/spec/background_queue/server_lib/integration/full_test_spec.rb +247 -0
  58. data/spec/background_queue/server_lib/integration/queue_integration_spec.rb +98 -0
  59. data/spec/background_queue/server_lib/integration/serialize_spec.rb +127 -0
  60. data/spec/background_queue/server_lib/job_registry_spec.rb +65 -0
  61. data/spec/background_queue/server_lib/job_spec.rb +525 -0
  62. data/spec/background_queue/server_lib/owner_spec.rb +33 -0
  63. data/spec/background_queue/server_lib/priority_queue_spec.rb +182 -0
  64. data/spec/background_queue/server_lib/server_spec.rb +353 -0
  65. data/spec/background_queue/server_lib/sorted_workers_spec.rb +122 -0
  66. data/spec/background_queue/server_lib/task_registry_spec.rb +69 -0
  67. data/spec/background_queue/server_lib/task_spec.rb +20 -0
  68. data/spec/background_queue/server_lib/thread_manager_spec.rb +106 -0
  69. data/spec/background_queue/server_lib/worker_balancer_spec.rb +127 -0
  70. data/spec/background_queue/server_lib/worker_client_spec.rb +196 -0
  71. data/spec/background_queue/server_lib/worker_thread_spec.rb +125 -0
  72. data/spec/background_queue/utils_spec.rb +27 -0
  73. data/spec/background_queue/worker/base_spec.rb +35 -0
  74. data/spec/background_queue/worker/calling_spec.rb +103 -0
  75. data/spec/background_queue/worker/environment_spec.rb +67 -0
  76. data/spec/background_queue/worker/worker_loader_spec.rb +103 -0
  77. data/spec/background_queue_spec.rb +7 -0
  78. data/spec/resources/config-client.yml +7 -0
  79. data/spec/resources/config-serialize.yml +12 -0
  80. data/spec/resources/config.yml +12 -0
  81. data/spec/resources/example_worker.rb +4 -0
  82. data/spec/resources/example_worker_with_error.rb +4 -0
  83. data/spec/resources/test_worker.rb +8 -0
  84. data/spec/shared/queue_registry_shared.rb +216 -0
  85. data/spec/spec_helper.rb +15 -0
  86. data/spec/support/default_task.rb +9 -0
  87. data/spec/support/private.rb +23 -0
  88. data/spec/support/simple_server.rb +28 -0
  89. data/spec/support/simple_task.rb +58 -0
  90. data/spec/support/test_worker_server.rb +205 -0
  91. metadata +254 -0
@@ -0,0 +1,54 @@
1
+ module BackgroundQueue::ServerLib
2
+
3
+ class Owner < PriorityQueue
4
+ include BackgroundQueue::ServerLib::QueueRegistry
5
+
6
+ attr_accessor :id
7
+
8
+ def initialize(id, balanced_queues)
9
+ @id = id
10
+ @balanced_queues = balanced_queues
11
+ super()
12
+ end
13
+
14
+
15
+ def ==(other)
16
+ @id == other.id
17
+ end
18
+
19
+ def server
20
+ @balanced_queues.server
21
+ end
22
+
23
+ def inspect
24
+ "OwnerQueue:#{self.id} (#{self.object_id})"
25
+ end
26
+
27
+ def self.queue_class
28
+ BackgroundQueue::ServerLib::Job
29
+ end
30
+
31
+ def synchronous?
32
+ false
33
+ end
34
+
35
+ private
36
+
37
+ def create_queue(queue_id)
38
+ job = super(queue_id)
39
+ @balanced_queues.register_job(job)
40
+ job
41
+ end
42
+
43
+ def get_queue_id_from_item(item)
44
+ item.job_id
45
+ end
46
+
47
+ def add_item_to_queue(queue, item)
48
+ queue.add_item(item)
49
+ end
50
+
51
+
52
+
53
+ end
54
+ end
@@ -0,0 +1,156 @@
1
+ module BackgroundQueue::ServerLib
2
+ #internally implemented using a list of queues
3
+ #this does not do any locking, subclasses should look after any locking
4
+ class PriorityQueue
5
+
6
+ def initialize
7
+ @queues = []
8
+ @items = {}
9
+ @stalled_items = {}
10
+ @stalled = false
11
+ end
12
+
13
+ def pop
14
+ q = get_next_queue
15
+ return nil if q.nil?
16
+ item = q.shift
17
+ if q.empty?
18
+ remove_queue(q)
19
+ end
20
+ item
21
+ end
22
+
23
+ def push(item)
24
+ q = get_queue_for_priority(item.priority, true)
25
+ q.push(item)
26
+ end
27
+
28
+ def remove(item, override_priority=nil)
29
+ override_priority = item.priority if override_priority.nil?
30
+ q = get_queue_for_priority(override_priority, false)
31
+ raise "unable to get queue at priority #{override_priority} when removing" if q.nil?
32
+ q.delete_if { |q_item| q_item.id == item.id }
33
+ if q.empty?
34
+ remove_queue(q)
35
+ end
36
+ end
37
+
38
+ #the highest priority queue
39
+ def priority
40
+ return nil if @queues.empty?
41
+ get_next_queue.priority
42
+ end
43
+
44
+ def stalled?
45
+ @stalled
46
+ end
47
+
48
+ def stalled=(stall)
49
+ @stalled = stall
50
+ end
51
+
52
+ def empty?
53
+ @queues.empty?
54
+ end
55
+
56
+ def number_of_priorities
57
+ @queues.length
58
+ end
59
+
60
+ def number_if_items_at_priority(priority)
61
+ q = get_queue_for_priority(priority, false)
62
+ return 0 if q.nil?
63
+ q.length
64
+ end
65
+
66
+ def peek
67
+ q = get_next_queue
68
+ return nil if q.nil?
69
+ q.first
70
+ end
71
+
72
+ def each_item(&block)
73
+ for queue in @queues
74
+ for item in queue
75
+ block.call(item)
76
+ end
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def get_queue_for_priority(priority, create)
83
+ @queues.each_with_index do |q, idx|
84
+ return q if q.priority == priority
85
+ if q.priority > priority #passed it.. insert here...
86
+ return nil unless create
87
+ return insert_queue_at_index(priority, idx)
88
+ end
89
+ end
90
+ return insert_queue_at_index(priority, -1)
91
+ end
92
+
93
+ def insert_queue_at_index(priority, index)
94
+ q = PriorityArray.new(priority)
95
+ @queues.insert(index, q)
96
+ q
97
+ end
98
+
99
+ def get_next_queue
100
+ @queues.first
101
+ end
102
+
103
+ def remove_queue(queue)
104
+ @queues.delete(queue)
105
+ end
106
+
107
+ def get_queues
108
+ @queues
109
+ end
110
+ end
111
+
112
+
113
+ #this is an array with a priority attribute
114
+ class PriorityArray < Array
115
+
116
+ attr_accessor :priority
117
+ def initialize(priority)
118
+ @priority = priority
119
+ super(0)
120
+ end
121
+
122
+ def ==(other)
123
+ other.priority == self.priority
124
+ end
125
+
126
+ def inspect
127
+ "#{self.priority}:#{super}"
128
+ end
129
+ end
130
+ end
131
+
132
+
133
+ #balanced_queue => owners => jobs => items
134
+ #items can change owner/jobs
135
+ #balanced_queue, and owner are priority queues
136
+ #job is a normal queue
137
+ #as an item is popped from the queue, its priority is recalculated
138
+
139
+ #bq.next_task gets pops owner with highest priority. Calls owner.next_task(). If no tasks, will wait()
140
+ #owner.next_task pops next job with highest priority. Calls job.next_task
141
+ #job.next_task pops next task with highest priority
142
+ #job is added back to owner with current priority (unless empty?). Task is returned
143
+ #owner is added back to bq with current priority (unless empty?), Task is returned
144
+
145
+ #task is run()
146
+ #mark task as running
147
+ #call http to call the worker
148
+ #deregister task if run_version is the same (run_version is incremented each time the same task is reregistered)
149
+
150
+
151
+ #task registry keeps track of items so they can be found/re-inserted
152
+ #the queue keeps track of all items using hashes until item is removed (not popped)
153
+ #this allows a get(id) method and a pop() method
154
+ #get will add an instance to the hash if not found
155
+
156
+
@@ -0,0 +1,123 @@
1
+ module BackgroundQueue::ServerLib
2
+ module QueueRegistry
3
+
4
+ def add_item(item)
5
+ in_queue, queue = get_queue(get_queue_id_from_item(item), true)
6
+ priority_increased, original_priority = track_priority_when_adding_to_queue(queue, item)
7
+ if !queue.stalled? && (!in_queue || priority_increased)
8
+ if in_queue #remove from existing priority queue
9
+ remove(queue, original_priority)
10
+ end
11
+ push(queue)
12
+ end
13
+ end
14
+
15
+ def next_item
16
+ item = nil
17
+ queue = pop
18
+ unless queue.nil?
19
+ priority_decreased, original_priority, item = remove_item_from_queue(queue, :next)
20
+
21
+ # some items must run synchronously, so we dont want to add it back until the task is finished.
22
+ # if the queue it empty we still want to keep it there until the task is finished, incase the running task queues more tasks against the job.
23
+ if queue.empty? || queue.synchronous?
24
+ @items.delete(queue.id)
25
+ stall_queue(queue)
26
+ else
27
+ push(queue)
28
+ end
29
+ end
30
+ item
31
+ end
32
+
33
+ def remove_item(item)
34
+ in_queue, queue = get_queue(get_queue_id_from_item(item), false)
35
+ raise "Unable to remove task #{item.id} at priority #{item.priority} (no queue at that priority)" if queue.nil?
36
+ priority_decreased, original_priority, item = remove_item_from_queue(queue, item)
37
+ if queue.empty?
38
+ remove(queue, original_priority)
39
+ @items.delete(queue.id)
40
+ elsif priority_decreased
41
+ remove(queue, original_priority)
42
+ push(queue)
43
+ end
44
+ end
45
+
46
+ def finish_item(item)
47
+ #puts "#{self.class.name}:finish item: #{item.inspect}"
48
+ in_queue, queue = get_queue(get_queue_id_from_item(item), false)
49
+ raise "Queue #{get_queue_id_from_item(item)} unavailble when finishing item" if queue.nil?
50
+ queue.finish_item(item)
51
+ resume_queue(queue)
52
+ end
53
+
54
+ private
55
+
56
+ def create_queue(queue_id)
57
+ self.class.queue_class.new(queue_id, self)
58
+ end
59
+
60
+ def track_priority_when_adding_to_queue(queue, item)
61
+ original_priority = queue.priority
62
+ add_item_to_queue(queue, item) #queue.add_item(item)
63
+
64
+ if original_priority.nil? || original_priority > queue.priority
65
+ return [true, original_priority]
66
+ end
67
+ [false, original_priority]
68
+ end
69
+
70
+ def remove_item_from_queue(queue, target_item)
71
+ original_priority = queue.priority
72
+
73
+ if target_item == :next
74
+ item = queue.next_item
75
+ else
76
+ item = queue.remove_item(target_item)
77
+ end
78
+
79
+ if queue.priority.nil? || original_priority < queue.priority
80
+ return [true, original_priority, item]
81
+ end
82
+ [false, original_priority, item]
83
+ end
84
+
85
+ def get_queue(queue_id, create)
86
+ queue = @items[queue_id]
87
+ queue = @stalled_items[queue_id] if queue.nil?
88
+ return [true, queue] unless queue.nil?
89
+ return [false, nil] unless create
90
+ queue = create_queue(queue_id) #BackgroundQueue::ServerLib::Owner.new(owner_id)
91
+ @items[queue_id] = queue
92
+ return [false, queue]
93
+ end
94
+
95
+ def stall_queue(queue)
96
+ queue.stalled = true
97
+ #puts "stalling queue #{queue.inspect}"
98
+ @stalled_items[queue.id] = queue
99
+ end
100
+
101
+ def resume_queue(queue)
102
+ if queue.stalled?
103
+ @stalled_items.delete(queue.id)
104
+ unless queue.empty?
105
+ queue.stalled = false
106
+ push(queue)
107
+ @items[queue.id] = queue
108
+ #puts "returned q: #{queue.inspect}"
109
+ else
110
+ @items.delete(queue.id)
111
+ #puts "q empty"
112
+ end
113
+ #else
114
+ # puts "q not stalled"
115
+ end
116
+ end
117
+
118
+ def stalled_items
119
+ @stalled_items
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,314 @@
1
+ require 'optparse'
2
+ require 'logger'
3
+
4
+ module BackgroundQueue::ServerLib
5
+ class Server
6
+
7
+ attr_accessor :config
8
+ attr_accessor :thread_manager
9
+ attr_accessor :task_queue
10
+ attr_accessor :event_server
11
+ attr_accessor :workers
12
+ attr_accessor :jobs
13
+ attr_accessor :logger
14
+
15
+ def initialize
16
+ @running = false
17
+ @stat_mutex = Mutex.new
18
+ @stats = {
19
+ :tasks=>0,
20
+ :run_tasks=>0,
21
+ :running=>0
22
+ }
23
+ end
24
+
25
+ def process_args(argv)
26
+ argv = argv.clone
27
+ cmd = argv.shift
28
+
29
+ if cmd.nil?
30
+ raise BackgroundQueue::ServerLib::InitError, "Usage: server command [options]"
31
+ end
32
+
33
+ options = {:command=>cmd.nil? ? nil : cmd.downcase.intern}
34
+
35
+ env_to_load = "development"
36
+
37
+ OptionParser.new do |opts|
38
+ opts.banner = "Usage: server command [options]"
39
+ case options[:command]
40
+ when :start, :test
41
+ opts.on("-c", "--config PATH", "Configuration Path") do |cp|
42
+ options[:config] = cp
43
+ end
44
+ when :stop
45
+
46
+ when nil
47
+
48
+ else
49
+ raise "Invalid Command: #{cmd}"
50
+ end
51
+ opts.on("-l", "--logfile [PATH]", "Logfile Path") do |lf|
52
+ options[:log_file] = lf
53
+ end
54
+ opts.on("-v", "--loglevel [LEVEL]", "Log Level") do |ll|
55
+ options[:log_level] = ll
56
+ end
57
+ opts.on("-p", "--pidfile [PATH]", "Pid file Path (/var/run/background_queue.pid)") do |pf|
58
+ options[:pid_file] = pf
59
+ end
60
+ opts.on("-e", "--environment [RAILS_ENV]", "testing/development/production (development)") do |env|
61
+ env_to_load = env
62
+ end
63
+ end.parse!(argv)
64
+
65
+ ENV['RAILS_ENV']=env_to_load
66
+
67
+ raise BackgroundQueue::ServerLib::InitError, "Missing config argument (-c)" if options[:config].nil? && ([:test, :start].include?(options[:command]) )
68
+
69
+ options
70
+ end
71
+
72
+ def load_configuration(path)
73
+ @config = BackgroundQueue::ServerLib::Config.load_file(path)
74
+ true
75
+ end
76
+
77
+ def resolve_logging_path(path)
78
+ File.expand_path(path)
79
+ end
80
+
81
+ def set_logging_level(log, level)
82
+ if level.nil? || level.strip.length == 0
83
+ level = "warn"
84
+ else
85
+ level = level.to_s.downcase
86
+ end
87
+ case level
88
+ when 'debug'
89
+ log.level = Logger::DEBUG
90
+ when 'info'
91
+ log.level = Logger::INFO
92
+ when 'warn'
93
+ log.level = Logger::WARN
94
+ when 'error'
95
+ log.level = Logger::ERROR
96
+ when 'fatal'
97
+ log.level = Logger::FATAL
98
+ else
99
+ raise BackgroundQueue::ServerLib::InitError, "Unknown logging level: #{level}"
100
+ end
101
+ end
102
+
103
+ def init_logging(path, level)
104
+ unless path.nil? || path.to_s.strip.length == 0
105
+ path = resolve_logging_path(path)
106
+ begin
107
+ @logger = Logger.new(path, "daily")
108
+ set_logging_level(@logger, level)
109
+ rescue Exception=>e
110
+ raise BackgroundQueue::ServerLib::InitError, "Error initializing log file #{path}: #{e.message}"
111
+ end
112
+ end
113
+ if @logger.nil?
114
+ #just make a fallback logger...
115
+ @logger = Logger.new($stderr)
116
+ set_logging_level(@logger, "fatal")
117
+ end
118
+ end
119
+
120
+ def get_pid_path(options)
121
+ if options[:pid_file]
122
+ options[:pid_file]
123
+ else
124
+ "/var/run/background_queue.pid"
125
+ end
126
+ end
127
+
128
+ def get_pid(options)
129
+ sPid = nil
130
+ begin
131
+ sPid = File.open(get_pid_path(options)) { |f|
132
+ f.read
133
+ }
134
+ rescue
135
+ return nil
136
+ end
137
+ return nil if sPid.nil? || sPid.to_i == 0
138
+ nPid = sPid.to_i
139
+ begin
140
+ Process.kill(0, nPid)
141
+ return nPid
142
+ rescue
143
+ return nil
144
+ end
145
+ end
146
+
147
+ def check_not_running(options)
148
+ proc_id = get_pid(options)
149
+ raise BackgroundQueue::ServerLib::InitError, "Process #{proc_id} already running" unless proc_id.nil?
150
+ nil
151
+ end
152
+
153
+ def stop_pid(options)
154
+ proc_id = get_pid(options)
155
+ unless proc_id.nil?
156
+ begin
157
+ Process.kill(15, proc_id)
158
+ rescue
159
+ #dont care... the process may have died already?
160
+ end
161
+ count = 0
162
+ while get_pid(options) && count < 10
163
+ puts "Waiting..."
164
+ sleep(1)
165
+ end
166
+ kill_pid(options) #make sure
167
+ end
168
+ end
169
+
170
+ def kill_pid(options)
171
+ proc_id = get_pid(options)
172
+ begin
173
+ Process.kill(9, proc_id) unless proc_id.nil?
174
+ rescue
175
+ #dont care... the process may have died already?
176
+ end
177
+ end
178
+
179
+ def write_pid(options)
180
+ proc_id = Process.pid
181
+ begin
182
+ File.open(get_pid_path(options), "w") { |f|
183
+ f.write(proc_id.to_s)
184
+ }
185
+ rescue Exception=>e
186
+ raise BackgroundQueue::ServerLib::InitError, "Unable to write to pid file #{get_pid_path(options)}: #{e.message}"
187
+ end
188
+ end
189
+
190
+ def remove_pid(options)
191
+ begin
192
+ File.delete(get_pid_path(options))
193
+ rescue
194
+ end
195
+ end
196
+
197
+ def trap_signals
198
+ Signal.trap("TERM") do
199
+ puts "Terminating..."
200
+ self.stop()
201
+ end
202
+ end
203
+
204
+
205
+ def daemonize(options)
206
+ fork{
207
+ stdin = open '/dev/null', 'r'
208
+ stdout = open '/dev/null', 'w'
209
+ stderr = open '/dev/null', 'w'
210
+ STDIN.reopen stdin
211
+ STDOUT.reopen stdout
212
+ STDERR.reopen stderr
213
+ fork{
214
+ write_pid(options) unless options[:skip_pid]
215
+ run(options)
216
+ } and exit!
217
+ }
218
+ end
219
+
220
+ def start(options)
221
+ begin
222
+ load_configuration(options[:config])
223
+ init_logging(options[:log_file], options[:log_level])
224
+ check_not_running(options) unless options[:skip_pid]
225
+ write_pid(options) unless options[:skip_pid] #this will make sure we can write the pid file... the daemon will write it again
226
+ if options[:command] == :start
227
+ daemonize(options)
228
+ elsif options[:command] == :run
229
+ run(options)
230
+ else
231
+ raise BackgroundQueue::ServerLib::InitError, "Unknown Command: #{options[:command]}"
232
+ end
233
+ rescue BackgroundQueue::ServerLib::InitError=>ie
234
+ STDERR.puts ie.message
235
+ rescue Exception=>e
236
+ STDERR.puts e.message
237
+ STDERR.puts e.backtrace.join("\n")
238
+ end
239
+ end
240
+
241
+ def running?
242
+ @running
243
+ end
244
+
245
+ def run(options)
246
+ trap_signals
247
+ @running = true
248
+ @thread_manager = BackgroundQueue::ServerLib::ThreadManager.new(self, self.config.connections_per_worker)
249
+
250
+ @workers = BackgroundQueue::ServerLib::WorkerBalancer.new(self)
251
+ @task_queue = BackgroundQueue::ServerLib::BalancedQueue.new(self)
252
+
253
+ @thread_manager.start(BackgroundQueue::ServerLib::WorkerThread)
254
+
255
+ @event_server = BackgroundQueue::ServerLib::EventServer.new(self)
256
+
257
+ @jobs = BackgroundQueue::ServerLib::JobRegistry.new
258
+
259
+ load_tasks(config.task_file)
260
+
261
+ @event_server.start
262
+ end
263
+
264
+ def stop(timeout_secs=10)
265
+ @running = false
266
+ @event_server.stop
267
+ @thread_manager.wait(timeout_secs)
268
+ save_tasks(config.task_file)
269
+ end
270
+
271
+ def change_stat(stat, delta)
272
+ @stat_mutex.synchronize {
273
+ @stats[stat] += delta
274
+ }
275
+ end
276
+
277
+ def get_stats
278
+ @stat_mutex.synchronize {
279
+ @stats.clone
280
+ }
281
+ end
282
+
283
+ def load_tasks(path)
284
+ return if path.nil?
285
+ if File.exist?(path)
286
+ begin
287
+ File.open(path, 'r') { |io|
288
+ task_queue.load_from_file(io)
289
+ }
290
+ rescue Exception=>e
291
+ logger.error("Error loading tasks from #{path}: #{e.message}")
292
+ logger.debug(e.backtrace.join("\n"))
293
+ end
294
+ end
295
+ end
296
+
297
+ def save_tasks(path)
298
+ return if path.nil?
299
+
300
+ begin
301
+ File.open(path, 'w') { |io|
302
+ task_queue.save_to_file(io)
303
+ }
304
+ rescue Exception=>e
305
+ logger.error("Error saving tasks to #{path}: #{e.message}")
306
+ logger.debug(e.backtrace.join("\n"))
307
+ end
308
+ end
309
+ end
310
+
311
+ class InitError < Exception
312
+
313
+ end
314
+ end