wires 0.2.6 → 0.2.7
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/wires/channel.rb +1 -1
- data/lib/wires/hub.rb +109 -128
- data/lib/wires/time.rb +6 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 292c032ed0e9f02b7fb4700d477e4db91eb9af8c
|
4
|
+
data.tar.gz: d431d5471f1f1e4c4301dad21736c6be1f927455
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af4f5ad6bf247f95f9b6e7b8d5e8c236c4d858ef33d566beb610a7dcffde1ee0bbf98483ee6032a82c9618e553fbf7065cd2711965f5a85dac770d5ce4b17bfe
|
7
|
+
data.tar.gz: 1936dc17986f409580386ba6c6a2d2bcc6e25bcee0782eada1b3deaca158e11a8c65fd0df272d101e86d2e5009cdc8fb14bb56251719816665ea99466f87fc41
|
data/lib/wires/channel.rb
CHANGED
@@ -82,7 +82,7 @@ module Wires
|
|
82
82
|
for chan in relevant_channels()
|
83
83
|
for target in chan.target_list
|
84
84
|
for string in target[0] & event.class.codestrings
|
85
|
-
self.class.hub
|
85
|
+
self.class.hub.spawn(event, string, *target[1], blocking)
|
86
86
|
end end end
|
87
87
|
|
88
88
|
nil end
|
data/lib/wires/hub.rb
CHANGED
@@ -7,12 +7,22 @@ module Wires
|
|
7
7
|
# Operate on the metaclass as a type of singleton pattern
|
8
8
|
class << self
|
9
9
|
|
10
|
+
# Allow user to get/set limit to number of child threads
|
11
|
+
attr_accessor :max_child_threads
|
12
|
+
|
13
|
+
# Make subclasses call class_init
|
14
|
+
def inherited(subcls); subcls.class_init end
|
15
|
+
|
10
16
|
# Moved to a dedicated method for subclass' sake
|
11
17
|
def class_init
|
12
|
-
@queue = Queue.new
|
13
|
-
|
14
|
-
@child_threads
|
15
|
-
@child_threads_lock
|
18
|
+
# @queue = Queue.new
|
19
|
+
@max_child_threads = nil
|
20
|
+
@child_threads = Array.new
|
21
|
+
@child_threads_lock = Monitor.new
|
22
|
+
@neglected = Array.new
|
23
|
+
@neglected_lock = Monitor.new
|
24
|
+
@spawning_count = 0
|
25
|
+
@spawning_count_lock = Monitor.new
|
16
26
|
|
17
27
|
@before_runs = Queue.new
|
18
28
|
@after_runs = Queue.new
|
@@ -20,7 +30,6 @@ module Wires
|
|
20
30
|
@after_kills = Queue.new
|
21
31
|
|
22
32
|
@please_finish_all = false
|
23
|
-
@please_kill = false
|
24
33
|
|
25
34
|
@at_exit = Proc.new{nil}
|
26
35
|
at_exit do self.at_exit_proc end
|
@@ -31,31 +40,21 @@ module Wires
|
|
31
40
|
|
32
41
|
def at_exit_proc; @at_exit.call; end
|
33
42
|
|
34
|
-
# Make subclasses call class_init
|
35
|
-
def inherited(subcls); subcls.class_init end
|
36
|
-
|
37
43
|
|
38
44
|
def dead?; state==:dead end
|
39
45
|
def alive?; state==:alive end
|
40
46
|
|
41
47
|
##
|
42
|
-
# Start the Hub
|
48
|
+
# Start the Hub to allow task spawning.
|
43
49
|
#
|
44
|
-
# valid flags:
|
45
|
-
# [+:in_place+] Hub event loop will be run in calling thread,
|
46
|
-
# blocking until Hub is killed. If this flag is not
|
47
|
-
# specified, the Hub event loop is run in a new thread,
|
48
|
-
# which the main thread joins in at_exit.
|
49
50
|
def run(*flags)
|
50
|
-
sleep 0 until
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
51
|
+
sleep 0 until @spawning_count <= 0
|
52
|
+
@spawning_count_lock.synchronize do
|
53
|
+
sleep 0 until request_state :alive
|
54
|
+
end
|
55
|
+
spawn_neglected_task_threads
|
56
|
+
join_children
|
57
|
+
nil end
|
59
58
|
|
60
59
|
##
|
61
60
|
# Kill the Hub event loop (optional flags change thread behavior)
|
@@ -64,64 +63,114 @@ module Wires
|
|
64
63
|
# [+:nonblocking+]
|
65
64
|
# Without this flag, calling thread will be blocked
|
66
65
|
# until Hub thread is done
|
67
|
-
# [+:
|
66
|
+
# [+:purge_tasks+]
|
68
67
|
# Without this flag, Hub thread won't be done
|
69
68
|
# until all child threads are done
|
70
69
|
def kill(*flags)
|
71
|
-
|
72
|
-
@
|
73
|
-
|
70
|
+
sleep 0 until @spawning_count <= 0
|
71
|
+
# @spawning_count_lock.synchronize do
|
72
|
+
@please_finish_all = (not flags.include? :purge_tasks)
|
73
|
+
sleep 0 until request_state :dead unless (flags.include? :nonblocking)
|
74
|
+
# end
|
74
75
|
nil end
|
75
76
|
|
76
77
|
# Register hook to execute before run - can call multiple times
|
77
|
-
def before_run(
|
78
|
-
|
79
|
-
expect_type func, Proc
|
80
|
-
@before_runs << [func, retain]
|
78
|
+
def before_run(retain=false, &block)
|
79
|
+
@before_runs << [block, retain]
|
81
80
|
nil end
|
82
81
|
|
83
82
|
# Register hook to execute after run - can call multiple times
|
84
|
-
def after_run(
|
85
|
-
|
86
|
-
expect_type func, Proc
|
87
|
-
@after_runs << [func, retain]
|
83
|
+
def after_run(retain=false, &block)
|
84
|
+
@after_runs << [block, retain]
|
88
85
|
nil end
|
89
86
|
|
90
87
|
# Register hook to execute before kill - can call multiple times
|
91
|
-
def before_kill(
|
92
|
-
|
93
|
-
expect_type func, Proc
|
94
|
-
@before_kills << [func, retain]
|
88
|
+
def before_kill(retain=false, &block)
|
89
|
+
@before_kills << [block, retain]
|
95
90
|
nil end
|
96
91
|
|
97
92
|
# Register hook to execute after kill - can call multiple times
|
98
|
-
def after_kill(
|
99
|
-
|
100
|
-
expect_type func, Proc
|
101
|
-
@after_kills << [func, retain]
|
93
|
+
def after_kill(retain=false, &block)
|
94
|
+
@after_kills << [block, retain]
|
102
95
|
nil end
|
103
96
|
|
104
|
-
#
|
105
|
-
def
|
106
|
-
|
107
|
-
if Thread.current==@_hegemon_auto_thread \
|
108
|
-
or Thread.current==@thread
|
97
|
+
# Spawn a task
|
98
|
+
def spawn(*args) # :args: event, ch_string, proc, blocking
|
99
|
+
@spawning_count_lock.synchronize { @spawning_count += 1 }
|
109
100
|
|
110
|
-
|
111
|
-
(x[2] and @thread) ?
|
112
|
-
sleep :
|
113
|
-
Thread.pass
|
101
|
+
return neglect(*args) if dead?
|
114
102
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
103
|
+
event, ch_string, proc, blocking = *args
|
104
|
+
|
105
|
+
# If blocking, run the proc in this thread
|
106
|
+
if blocking
|
107
|
+
proc.call(event, ch_string)
|
108
|
+
return :done
|
109
|
+
end
|
110
|
+
|
111
|
+
# If not blocking, clear old threads and spawn a new thread
|
112
|
+
new_thread = nil
|
113
|
+
|
114
|
+
@child_threads_lock.synchronize do
|
115
|
+
|
116
|
+
# Clear out dead threads
|
117
|
+
@child_threads.select!{|t| t.status}
|
118
|
+
|
119
|
+
begin
|
120
|
+
# Raise ThreadError for user-set thread limit to mimic OS limit
|
121
|
+
raise ThreadError if (@max_child_threads) and \
|
122
|
+
(@max_child_threads <= @child_threads.size)
|
123
|
+
# Start the new child thread; follow with chain of neglected tasks
|
124
|
+
new_thread = Thread.new { proc.call(event, ch_string); \
|
125
|
+
spawn_neglected_task_chain }
|
126
|
+
# Capture ThreadError from either OS or user-set limitation
|
127
|
+
rescue ThreadError; return neglect(*args); end
|
128
|
+
|
129
|
+
@child_threads << new_thread
|
130
|
+
return new_thread
|
131
|
+
end
|
132
|
+
|
133
|
+
ensure
|
134
|
+
@spawning_count_lock.synchronize { @spawning_count -= 1 }
|
120
135
|
end
|
121
136
|
|
137
|
+
def purge_neglected
|
138
|
+
@neglected_lock.synchronize do
|
139
|
+
@neglected = Array.new
|
140
|
+
end
|
141
|
+
end
|
122
142
|
|
123
143
|
private
|
124
144
|
|
145
|
+
# Temporarily neglect a task until resources are available to run it
|
146
|
+
def neglect(*args)
|
147
|
+
$stderr.puts "#{self} neglected to spawn #{args.inspect}"
|
148
|
+
@neglected_lock.synchronize do
|
149
|
+
@neglected << args
|
150
|
+
end
|
151
|
+
false end
|
152
|
+
|
153
|
+
# Run a chain of @neglected tasks in place until no more are waiting
|
154
|
+
def spawn_neglected_task_chain
|
155
|
+
neglected_one = nil
|
156
|
+
@neglected_lock.synchronize do
|
157
|
+
return nil if @neglected.empty?
|
158
|
+
neglected_one = @neglected.shift
|
159
|
+
end
|
160
|
+
spawn(*((neglected_one)[0...-1]<<true)) # Call with blocking
|
161
|
+
spawn_neglected_task_chain
|
162
|
+
nil end
|
163
|
+
|
164
|
+
# Flush @neglected task queue, each in a new thread
|
165
|
+
def spawn_neglected_task_threads
|
166
|
+
until (cease||=false)
|
167
|
+
@neglected_lock.synchronize do
|
168
|
+
break if (cease = @neglected.empty?)
|
169
|
+
spawn(*((@neglected.shift)[0...-1]<<false)) # Call without blocking
|
170
|
+
end
|
171
|
+
end
|
172
|
+
nil end
|
173
|
+
|
125
174
|
# Flush/run queue of [proc, retain]s, retaining those with retain==true
|
126
175
|
def run_hooks(hooks)
|
127
176
|
retained = Queue.new
|
@@ -129,7 +178,7 @@ module Wires
|
|
129
178
|
proc, retain = hooks.shift
|
130
179
|
retained << [proc, retain] if retain
|
131
180
|
proc.call
|
132
|
-
flush_queue if alive?
|
181
|
+
# flush_queue if alive?
|
133
182
|
end
|
134
183
|
while not retained.empty?
|
135
184
|
hooks << retained.shift
|
@@ -141,7 +190,7 @@ module Wires
|
|
141
190
|
a_thread = Thread.new{nil}
|
142
191
|
while a_thread
|
143
192
|
@child_threads_lock.synchronize do
|
144
|
-
flush_queue
|
193
|
+
# flush_queue
|
145
194
|
a_thread = @child_threads.shift
|
146
195
|
end
|
147
196
|
a_thread.join if a_thread
|
@@ -149,47 +198,6 @@ module Wires
|
|
149
198
|
end
|
150
199
|
nil end
|
151
200
|
|
152
|
-
# Kill all currently working child threads
|
153
|
-
# Newly fired events could still queue up,
|
154
|
-
# Waiting to be born until this thread is done killing
|
155
|
-
def kill_children
|
156
|
-
@child_threads_lock.synchronize do
|
157
|
-
until @child_threads.empty?
|
158
|
-
@child_threads.shift.exit
|
159
|
-
end
|
160
|
-
end
|
161
|
-
nil end
|
162
|
-
|
163
|
-
# Kill all currently working child threads
|
164
|
-
# Newly fired events could still queue up,
|
165
|
-
# But they will be cleared out and never be born
|
166
|
-
def kill_children_and_clear
|
167
|
-
@child_threads_lock.synchronize do
|
168
|
-
until @child_threads.empty?
|
169
|
-
@child_threads.shift.exit
|
170
|
-
end
|
171
|
-
clear
|
172
|
-
end
|
173
|
-
nil end
|
174
|
-
|
175
|
-
def process_item(x)
|
176
|
-
x, waiting_thread = x
|
177
|
-
string, event, blocking, proc = x
|
178
|
-
|
179
|
-
# Do all dealings with @child_threads under mutex
|
180
|
-
@child_threads_lock.synchronize do
|
181
|
-
|
182
|
-
# Clear dead child threads to free up memory
|
183
|
-
@child_threads.select! {|t| t.status}
|
184
|
-
|
185
|
-
# Start the new child thread
|
186
|
-
@child_threads << Thread.new do
|
187
|
-
proc.call(event)
|
188
|
-
waiting_thread.wakeup if blocking and waiting_thread
|
189
|
-
end
|
190
|
-
end
|
191
|
-
nil end
|
192
|
-
|
193
201
|
end
|
194
202
|
|
195
203
|
|
@@ -216,46 +224,19 @@ module Wires
|
|
216
224
|
impose_state :dead
|
217
225
|
|
218
226
|
declare_state :dead do
|
219
|
-
|
220
227
|
transition_to :alive do
|
221
228
|
before { run_hooks @before_runs }
|
222
229
|
after { run_hooks @after_runs }
|
223
|
-
after { start_hegemon_auto_thread(0.05) }
|
224
230
|
end
|
225
231
|
end
|
226
232
|
|
227
233
|
declare_state :alive do
|
228
|
-
|
229
|
-
task do |i|
|
230
|
-
if i==0
|
231
|
-
@thread_continue = true
|
232
|
-
@thread = Thread.new do
|
233
|
-
while @thread_continue
|
234
|
-
if @queue.empty? then sleep 0.01
|
235
|
-
else process_item(@queue.shift) end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
end
|
241
|
-
|
242
234
|
transition_to :dead do
|
243
|
-
condition {@please_kill}
|
244
|
-
|
245
235
|
before { run_hooks @before_kills }
|
246
|
-
|
236
|
+
before { purge_neglected }
|
247
237
|
before { join_children if @please_finish_all }
|
248
|
-
|
249
|
-
after { @thread_continue=false
|
250
|
-
@thread.join
|
251
|
-
@thread = nil }
|
252
|
-
|
253
|
-
after { @please_kill = false }
|
254
238
|
after { @please_finish_all = false }
|
255
|
-
|
256
239
|
after { run_hooks @after_kills }
|
257
|
-
|
258
|
-
after { end_hegemon_auto_thread }
|
259
240
|
end
|
260
241
|
end
|
261
242
|
|
data/lib/wires/time.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
|
2
|
+
|
2
3
|
module Wires
|
3
4
|
|
4
|
-
class TimeSchedulerStartEvent < Event; end
|
5
5
|
class TimeSchedulerAnonEvent < Event; end
|
6
6
|
|
7
|
-
|
8
7
|
class TimeSchedulerItem
|
9
8
|
|
10
9
|
attr_reader :time, :event, :channel, :interval
|
@@ -68,7 +67,7 @@ module Wires
|
|
68
67
|
# Block until event is ready, then fire and block until it is done
|
69
68
|
def fire_when_ready(*args);
|
70
69
|
wait_until_ready
|
71
|
-
|
70
|
+
fire(*args)
|
72
71
|
end
|
73
72
|
|
74
73
|
# Lock (almost) all instance methods with common re-entrant lock
|
@@ -233,6 +232,10 @@ class ActiveSupport::Duration
|
|
233
232
|
|
234
233
|
end
|
235
234
|
|
235
|
+
def fire_every(interval, event, channel='*', **kwargs)
|
236
|
+
Wires::TimeScheduler << \
|
237
|
+
Wires::TimeSchedulerItem.new(self, event, channel, **kwargs)
|
238
|
+
end
|
236
239
|
|
237
240
|
# TODO: Repeatable event sugar?
|
238
241
|
# TODO: Tests for all new functionality
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wires
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe McIlvain
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: activesupport
|
14
|
+
name: activesupport ~> 4.0
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.0.
|
33
|
+
version: 0.0.8
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.0.
|
40
|
+
version: 0.0.8
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|