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 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