wires 0.2.6 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63192901a598961c0bd3aafa25c6e49da1e4d328
4
- data.tar.gz: 5477285e774357f9d62fa7f41088a7ad16f7f90d
3
+ metadata.gz: 292c032ed0e9f02b7fb4700d477e4db91eb9af8c
4
+ data.tar.gz: d431d5471f1f1e4c4301dad21736c6be1f927455
5
5
  SHA512:
6
- metadata.gz: 00163b5c1c47e4037c8db05a3338bdaeeca7541dbc0949995f60aa167120c31a2cdd4c23e6100403668dbeb0cb13f93a6a43a08873a019dfc49af42aa1e63f41
7
- data.tar.gz: 36a4df0cd53af0005e2459847e44e5220062699e0255ef0c0f904e5ac02a4c8e43928846e6bc78127e4e67d8d98bf4da733bb87b6069b4460af5692b9ea0677b
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 << [string, event, blocking, *target[1..-1]]
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 = Array.new
15
- @child_threads_lock = Monitor.new
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 event loop (optional flags change thread behavior)
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 request_state :alive
51
- sleep 0 until @thread
52
-
53
- # If :blocking, block now, else block at exit
54
- (flags.include? :in_place) ?
55
- (@thread.join) :
56
- (@at_exit = Proc.new { @thread.join if @thread and not $! })
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
- # [+:purge_events+]
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
- @please_finish_all = (not flags.include? :purge_events)
72
- @please_kill = true
73
- block_until_state :dead unless (flags.include? :nonblocking)
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(proc=nil, retain:false, &block)
78
- func = (block or proc)
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(proc=nil, retain:false, &block)
85
- func = (block or proc)
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(proc=nil, retain:false, &block)
92
- func = (block or proc)
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(proc=nil, retain:false, &block)
99
- func = (block or proc)
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
- # Put x in the queue, and block until x is processed (if Hub is running)
105
- def fire(x)
106
- raise ThreadError, "You can't fire events from this thread." \
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
- @queue << [x, Thread.current]
111
- (x[2] and @thread) ?
112
- sleep :
113
- Thread.pass
101
+ return neglect(*args) if dead?
114
102
 
115
- nil end
116
- def <<(x); fire(x); end
117
-
118
- def flush_queue
119
- (process_item(@queue.shift) until @queue.empty?)
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
- fire_if_ready(*args)
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.6
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-09 00:00:00.000000000 Z
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.7
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.7
40
+ version: 0.0.8
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement