wires 0.1.12 → 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.
- checksums.yaml +4 -4
- data/lib/wires.rb +2 -2
- data/lib/wires/channel.rb +109 -0
- data/lib/wires/event.rb +161 -0
- data/lib/wires/hub.rb +180 -179
- data/lib/wires/time.rb +166 -162
- metadata +32 -4
- data/lib/wires/channels.rb +0 -107
- data/lib/wires/events.rb +0 -159
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c13e2e944a09925c428bbcfd549162c0dd64a782
|
4
|
+
data.tar.gz: a7addc2fbf45a815e96ead41faf7bc9bd9f9e006
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d175ae2bc54184972ca61169961256df0ff323a1fa0bbd77cdb328932d9f6f6a224902d181a23e8824bf7d57a7e715a8ffd5ace9ebab1978ea065a01d62afb23
|
7
|
+
data.tar.gz: f65e946b8aa4ac6ec4ceb3510d3ecb1ee65669e570f9989b6f276471386b7f3f9fb849c51b1c64d2db756ad2d73397817f4f81e332f5857aa07dba7d8f7f564b
|
data/lib/wires.rb
CHANGED
@@ -4,7 +4,7 @@ require 'active_support/core_ext' # Convenience functions from Rails
|
|
4
4
|
require 'threadlock' # Easily add re-entrant lock to instance methods
|
5
5
|
|
6
6
|
require 'wires/expect_type'
|
7
|
-
require 'wires/
|
7
|
+
require 'wires/event'
|
8
8
|
require 'wires/hub'
|
9
|
-
require 'wires/
|
9
|
+
require 'wires/channel'
|
10
10
|
require 'wires/time'
|
@@ -0,0 +1,109 @@
|
|
1
|
+
|
2
|
+
def on(events, channels='*', &codeblock)
|
3
|
+
channels = [channels] unless channels.is_a? Array
|
4
|
+
for channel in channels
|
5
|
+
Wires::Channel.new(channel).register(events, codeblock)
|
6
|
+
end
|
7
|
+
nil end
|
8
|
+
|
9
|
+
|
10
|
+
def fire(event, channel='*')
|
11
|
+
Wires::Channel.new(channel).fire(event, blocking:false)
|
12
|
+
nil end
|
13
|
+
|
14
|
+
def fire_and_wait(event, channel='*')
|
15
|
+
Wires::Channel.new(channel).fire(event, blocking:true)
|
16
|
+
nil end
|
17
|
+
|
18
|
+
|
19
|
+
def Channel(*args) Wires::Channel.new(*args) end
|
20
|
+
|
21
|
+
module Wires
|
22
|
+
class Channel
|
23
|
+
|
24
|
+
attr_reader :name
|
25
|
+
attr_reader :target_list
|
26
|
+
|
27
|
+
def initialize(name)
|
28
|
+
@name = name
|
29
|
+
@target_list = Set.new
|
30
|
+
nil end
|
31
|
+
|
32
|
+
|
33
|
+
# Redefine this class method to use an alternate Hub
|
34
|
+
def self.hub; Hub; end
|
35
|
+
# Don't redefine this instance method!
|
36
|
+
def hub; self.class.hub; end
|
37
|
+
|
38
|
+
# Channel registry hash and star channel reference are values
|
39
|
+
# In this Hash with the key being the reference to the Hub
|
40
|
+
@@channel_hash = Hash.new
|
41
|
+
@@channel_star = Hash.new
|
42
|
+
|
43
|
+
# Give out references to the star channel
|
44
|
+
def self.channel_star; @@channel_star[self.hub]; end
|
45
|
+
def channel_star; @@channel_star[self.hub]; end
|
46
|
+
|
47
|
+
# Ensure that there is only one instance of Channel per name
|
48
|
+
@@new_lock = Mutex.new
|
49
|
+
def self.new(*args, &block)
|
50
|
+
@@channel_star[self.hub] ||= Channel.new('*') unless (args[0]=='*')
|
51
|
+
@@new_lock.synchronize do
|
52
|
+
@@channel_hash[self.hub] ||= Hash.new
|
53
|
+
@@channel_hash[self.hub][args[0]] ||= super(*args, &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Register a proc to be triggered by an event on this channel
|
58
|
+
def register(events, proc)
|
59
|
+
|
60
|
+
if not proc.is_a?(Proc) then raise SyntaxError, \
|
61
|
+
"No Proc given to execute on event: #{events}" end
|
62
|
+
|
63
|
+
# Convert all events to strings
|
64
|
+
events = [events] unless events.is_a? Array
|
65
|
+
events.flatten!
|
66
|
+
events.map! { |e| (e.is_a?(Class) ? e.codestring : e.to_s) }
|
67
|
+
events.uniq!
|
68
|
+
|
69
|
+
@target_list << [events, proc]
|
70
|
+
nil end
|
71
|
+
|
72
|
+
# Fire an event on this channel
|
73
|
+
def fire(event, blocking:false)
|
74
|
+
|
75
|
+
# Create an instance object from one of several acceptable input forms
|
76
|
+
event = Event.new_from event
|
77
|
+
|
78
|
+
# Fire to each relevant target on each channel
|
79
|
+
for chan in relevant_channels()
|
80
|
+
for target in chan.target_list
|
81
|
+
for string in target[0] & event.class.codestrings
|
82
|
+
self.class.hub << [string, event, blocking, *target[1..-1]]
|
83
|
+
end end end
|
84
|
+
|
85
|
+
nil end
|
86
|
+
|
87
|
+
def relevant_channels
|
88
|
+
return @@channel_hash[hub].values if self==channel_star
|
89
|
+
|
90
|
+
if self.name.is_a?(Regexp) then raise TypeError,
|
91
|
+
"Cannot fire on Regexp channel: #{self.name}."\
|
92
|
+
" Regexp channels can only used in event handlers." end
|
93
|
+
|
94
|
+
relevant = [channel_star]
|
95
|
+
for c in @@channel_hash[hub].values
|
96
|
+
relevant << c if \
|
97
|
+
if c.name.is_a?(Regexp)
|
98
|
+
self.name =~ c.name
|
99
|
+
elsif (defined?(c.name.channel_name) and
|
100
|
+
defined?(self.name.channel_name))
|
101
|
+
self.name.channel_name == c.name.channel_name
|
102
|
+
else
|
103
|
+
self.name.to_s == c.name.to_s
|
104
|
+
end
|
105
|
+
end
|
106
|
+
return relevant.uniq
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/wires/event.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
|
2
|
+
module Wires
|
3
|
+
# Store a list of all Event classes that get loaded.
|
4
|
+
class EventRegistry
|
5
|
+
@@registry = []
|
6
|
+
|
7
|
+
def self.<<(cls)
|
8
|
+
@@registry << cls
|
9
|
+
@@registry.uniq!
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.list
|
13
|
+
@@registry
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# All Event classes should inherit from this one
|
18
|
+
class Event < Object # explicit for the sake of Event.ancestry
|
19
|
+
|
20
|
+
# Register with the EventRegistry and make subclasses do the same
|
21
|
+
EventRegistry << self
|
22
|
+
|
23
|
+
# Operate on the metaclass as a type of singleton pattern
|
24
|
+
class << self
|
25
|
+
|
26
|
+
def inherited(subcls)
|
27
|
+
|
28
|
+
# Be sure codestring doesn't conflict
|
29
|
+
existing = _from_codestring(subcls.codestring)
|
30
|
+
if existing then raise NameError, \
|
31
|
+
"New Event subclass '#{subcls}' conflicts with"\
|
32
|
+
" existing Event subclass '#{existing}'."\
|
33
|
+
" The generated codestring '#{subcls.codestring}'"\
|
34
|
+
" must be unique for each Event subclass." end
|
35
|
+
|
36
|
+
# Register, then call super
|
37
|
+
EventRegistry << subcls
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
# List of class inheritance lineage back to but excluding Object
|
42
|
+
def ancestry(cls=self)
|
43
|
+
_next = cls.superclass
|
44
|
+
[cls==Object ? [] : [cls, ancestry(_next)]].flatten
|
45
|
+
end
|
46
|
+
|
47
|
+
# Convert class <ClassNameEvent> to string "class_name"
|
48
|
+
def codestring(cls=self)
|
49
|
+
File.basename cls.to_s
|
50
|
+
.underscore
|
51
|
+
.gsub(/_event$/, "")
|
52
|
+
end
|
53
|
+
|
54
|
+
# List of codestrings associated with this event and ancestors
|
55
|
+
def codestrings
|
56
|
+
x = ancestry
|
57
|
+
.map {|cls| cls.codestring}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Pull class from registry by codestring
|
61
|
+
# (more reliable than crafting a reverse regexp)
|
62
|
+
def _from_codestring(str)
|
63
|
+
return EventRegistry.list
|
64
|
+
.select{|e| e.codestring==str}[0]
|
65
|
+
end; private :_from_codestring
|
66
|
+
|
67
|
+
def from_codestring(str)
|
68
|
+
cls = _from_codestring(str.to_s)
|
69
|
+
if not cls then raise NameError,
|
70
|
+
"No known Event subclass with codestring: '#{str}'" end
|
71
|
+
cls
|
72
|
+
end
|
73
|
+
|
74
|
+
# Convert an event from 'array notation' to an Event subclass instance
|
75
|
+
# TODO: List acceptable input forms here for documentation
|
76
|
+
def new_from(input)
|
77
|
+
|
78
|
+
# Standardize to array and pull out arguments if they exist
|
79
|
+
input = [input] unless input.is_a? Array
|
80
|
+
input, *args = input
|
81
|
+
|
82
|
+
# Create event object from event as an object, class, or symbol/string
|
83
|
+
event = case input
|
84
|
+
when Event
|
85
|
+
input
|
86
|
+
when Class
|
87
|
+
input.new(*args) if input < Event
|
88
|
+
else
|
89
|
+
Event.from_codestring(input.to_s).new(*args)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Create attributes and accessors for all arguments to the constructor.
|
94
|
+
# This is done here rather than in initialize so that the functionality
|
95
|
+
# will remain if the user developer overrides initialize in the subclass.
|
96
|
+
def new(*args, &block)
|
97
|
+
obj = super
|
98
|
+
|
99
|
+
kwargs = args[-1].is_a?(Hash) ? args.pop : Hash.new
|
100
|
+
kwargs[:args] = args
|
101
|
+
kwargs[:codeblock] = block if block
|
102
|
+
for key in kwargs.keys
|
103
|
+
att = key.to_s
|
104
|
+
obj.instance_variable_set("@#{att}", kwargs[key])
|
105
|
+
class_eval("def #{att}; @#{att}; end")
|
106
|
+
class_eval("def #{att}=(val); @#{att}=val; end")
|
107
|
+
end
|
108
|
+
|
109
|
+
obj
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
# Calling super in new with *args will complain if this isn't here
|
115
|
+
def initialize(*args, &block) end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
#
|
120
|
+
# Comparison support for Events and Symbols/Strings
|
121
|
+
#
|
122
|
+
|
123
|
+
# Reopen Event and add comparison functions
|
124
|
+
class Event
|
125
|
+
class << self
|
126
|
+
def ==(other)
|
127
|
+
other.is_a?(Class) ?
|
128
|
+
super : codestring==other.to_s
|
129
|
+
end
|
130
|
+
def <=(other)
|
131
|
+
other.is_a?(Class) ?
|
132
|
+
super : codestrings.include?(other.to_s)
|
133
|
+
end
|
134
|
+
def <(other)
|
135
|
+
other.is_a?(Class) ?
|
136
|
+
super : (self<=other and not self==other)
|
137
|
+
end
|
138
|
+
def >=(other)
|
139
|
+
other.is_a?(Class) ?
|
140
|
+
super : Event.from_codestring(other.to_s)<=self
|
141
|
+
end
|
142
|
+
def >(other)
|
143
|
+
other.is_a?(Class) ?
|
144
|
+
super : Event.from_codestring(other.to_s)<self
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Autogenerate the inverse comparison functions for Symbol/String
|
150
|
+
for cls in [Symbol, String]
|
151
|
+
%w(== < > <= >=).zip(%w(== > < >= <=))
|
152
|
+
.each do |ops|
|
153
|
+
op, opinv = ops # unzip operator and inverse operator
|
154
|
+
cls.class_eval(
|
155
|
+
"def #{op}(other)\n"\
|
156
|
+
" (other.is_a?(Class) and other<=Event) ? \n"\
|
157
|
+
" (other#{opinv}self) : super\n"\
|
158
|
+
"end\n")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/lib/wires/hub.rb
CHANGED
@@ -2,203 +2,204 @@
|
|
2
2
|
# Make sure puts goes to $stdout for all threads!
|
3
3
|
def puts(x) $stdout.puts(x) end
|
4
4
|
|
5
|
-
|
6
|
-
# An Event Hub. Event/proc associations come in, and the procs
|
7
|
-
# get called in new threads in the order received
|
8
|
-
class Hub
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@child_threads = Array.new
|
13
|
-
@child_threads_lock = Mutex.new
|
14
|
-
|
15
|
-
@before_kills = Queue.new
|
16
|
-
@after_kills = Queue.new
|
17
|
-
|
18
|
-
# Operate on the metaclass as a type of singleton pattern
|
19
|
-
class << self
|
5
|
+
module Wires
|
6
|
+
# An Event Hub. Event/proc associations come in, and the procs
|
7
|
+
# get called in new threads in the order received
|
8
|
+
class Hub
|
9
|
+
@queue = Queue.new
|
10
|
+
@state = [:dead, :alive, :dying][0]
|
20
11
|
|
21
|
-
|
22
|
-
|
23
|
-
def dying?; @state==:dying end
|
24
|
-
def state; @state end
|
12
|
+
@child_threads = Array.new
|
13
|
+
@child_threads_lock = Mutex.new
|
25
14
|
|
26
|
-
|
27
|
-
|
15
|
+
@before_kills = Queue.new
|
16
|
+
@after_kills = Queue.new
|
28
17
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
18
|
+
# Operate on the metaclass as a type of singleton pattern
|
19
|
+
class << self
|
20
|
+
|
21
|
+
def dead?; @state==:dead end
|
22
|
+
def alive?; @state==:alive end
|
23
|
+
def dying?; @state==:dying end
|
24
|
+
def state; @state end
|
25
|
+
|
26
|
+
# Clear the Hub queue, but do not kill working threads
|
27
|
+
def clear; @queue.clear end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Start the Hub event loop (optional flags change thread behavior)
|
31
|
+
#
|
32
|
+
# valid flags:
|
33
|
+
# [+:blocking+] Hub event loop will be run in calling thread,
|
34
|
+
# blocking until Hub is killed. If this flag is not
|
35
|
+
# specified, the Hub event loop is run in a new thread,
|
36
|
+
# which the main thread joins in at_exit.
|
37
|
+
def run(*flags)
|
38
|
+
if dead? # Only run if not already alive or dying
|
39
|
+
|
40
|
+
# If :blocking is not set, run in a new thread and join at_exit
|
41
|
+
if not (flags.include? :blocking)
|
42
|
+
@thread = Thread.new() do
|
43
|
+
self.send(:run_loop)
|
44
|
+
end
|
45
|
+
# Only join if main thread wasn't killed by an exception
|
46
|
+
at_exit { @thread.join if not $! }
|
47
|
+
|
48
|
+
# If :blocking is set, run in main thread and block until Hub death
|
49
|
+
else self.send(:run_loop) end
|
50
|
+
|
51
|
+
end
|
47
52
|
|
48
|
-
|
49
|
-
else self.send(:run_loop) end
|
53
|
+
sleep 0 # Yield to other threads
|
50
54
|
|
51
|
-
end
|
55
|
+
nil end
|
52
56
|
|
53
|
-
|
57
|
+
##
|
58
|
+
# Kill the Hub event loop (optional flags change thread behavior)
|
59
|
+
#
|
60
|
+
# valid flags:
|
61
|
+
# [+:finish_all+] Hub thread won't be done until all child threads done
|
62
|
+
# [+:blocking+] calling thread won't be done until Hub thread is done
|
63
|
+
def kill(*flags)
|
64
|
+
@finish_all = (flags.include? :finish_all)
|
65
|
+
@state=:dying
|
66
|
+
@thread.join if (dying? and flags.include? :blocking)
|
67
|
+
nil end
|
54
68
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
# Register hook to execute after kill - can call multiple times
|
77
|
-
def after_kill(proc=nil, retain:false, &block)
|
78
|
-
func = (block or proc)
|
79
|
-
expect_type func, Proc
|
80
|
-
@after_kills << [func, retain]
|
81
|
-
nil end
|
82
|
-
|
83
|
-
# Put x in the queue, and block until x is processed (if Hub is running)
|
84
|
-
def fire(x)
|
85
|
-
if not dead? # yield to event loop thread until awoken by it later
|
86
|
-
@queue << [x, Thread.current]
|
87
|
-
sleep
|
88
|
-
else # don't wait if Hub isn't running - would cause lockup
|
89
|
-
@queue << [x, nil]
|
90
|
-
end
|
91
|
-
nil end
|
92
|
-
def <<(x); fire(x); end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
# Kill all currently working child threads
|
97
|
-
# Newly fired events could still queue up,
|
98
|
-
# Waiting to be born until this thread is done killing
|
99
|
-
def kill_children
|
100
|
-
@child_threads_lock.synchronize do
|
101
|
-
until @child_threads.empty?
|
102
|
-
@child_threads.shift.exit
|
103
|
-
end
|
104
|
-
end
|
105
|
-
nil end
|
106
|
-
|
107
|
-
# Kill all currently working child threads
|
108
|
-
# Newly fired events could still queue up,
|
109
|
-
# But they will be cleared out and never be born
|
110
|
-
def kill_children_and_clear
|
111
|
-
@child_threads_lock.synchronize do
|
112
|
-
until @child_threads.empty?
|
113
|
-
@child_threads.shift.exit
|
69
|
+
# Register hook to execute before kill - can call multiple times
|
70
|
+
def before_kill(proc=nil, retain:false, &block)
|
71
|
+
func = (block or proc)
|
72
|
+
expect_type func, Proc
|
73
|
+
@before_kills << [func, retain]
|
74
|
+
nil end
|
75
|
+
|
76
|
+
# Register hook to execute after kill - can call multiple times
|
77
|
+
def after_kill(proc=nil, retain:false, &block)
|
78
|
+
func = (block or proc)
|
79
|
+
expect_type func, Proc
|
80
|
+
@after_kills << [func, retain]
|
81
|
+
nil end
|
82
|
+
|
83
|
+
# Put x in the queue, and block until x is processed (if Hub is running)
|
84
|
+
def fire(x)
|
85
|
+
if not dead? # yield to event loop thread until awoken by it later
|
86
|
+
@queue << [x, Thread.current]
|
87
|
+
sleep
|
88
|
+
else # don't wait if Hub isn't running - would cause lockup
|
89
|
+
@queue << [x, nil]
|
114
90
|
end
|
115
|
-
|
116
|
-
end
|
117
|
-
|
91
|
+
nil end
|
92
|
+
def <<(x); fire(x); end
|
93
|
+
|
94
|
+
private
|
118
95
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
96
|
+
# Kill all currently working child threads
|
97
|
+
# Newly fired events could still queue up,
|
98
|
+
# Waiting to be born until this thread is done killing
|
99
|
+
def kill_children
|
123
100
|
@child_threads_lock.synchronize do
|
124
|
-
|
101
|
+
until @child_threads.empty?
|
102
|
+
@child_threads.shift.exit
|
103
|
+
end
|
125
104
|
end
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
proc.call
|
138
|
-
end
|
139
|
-
while not retained.empty?
|
140
|
-
queue << retained.shift
|
141
|
-
end
|
142
|
-
nil end
|
143
|
-
|
144
|
-
# Run before_kill hooks, optionally join child threads, then die
|
145
|
-
def die
|
146
|
-
run_hooks(@before_kills)
|
147
|
-
join_children if @finish_all
|
148
|
-
@state = :dead
|
149
|
-
nil end
|
150
|
-
|
151
|
-
# Run main event loop, not finished until Hub is killed
|
152
|
-
def run_loop
|
153
|
-
@state = :alive
|
154
|
-
|
155
|
-
while not dead?
|
156
|
-
if @queue.empty? then sleep(0)
|
157
|
-
else process_item(@queue.shift) end
|
158
|
-
|
159
|
-
if dying?
|
160
|
-
die_thread ||= Thread.new { die }
|
105
|
+
nil end
|
106
|
+
|
107
|
+
# Kill all currently working child threads
|
108
|
+
# Newly fired events could still queue up,
|
109
|
+
# But they will be cleared out and never be born
|
110
|
+
def kill_children_and_clear
|
111
|
+
@child_threads_lock.synchronize do
|
112
|
+
until @child_threads.empty?
|
113
|
+
@child_threads.shift.exit
|
114
|
+
end
|
115
|
+
clear
|
161
116
|
end
|
162
|
-
end
|
117
|
+
nil end
|
163
118
|
|
164
|
-
|
165
|
-
|
119
|
+
# Join child threads, one by one, allowing more children to appear
|
120
|
+
def join_children
|
121
|
+
a_thread = Thread.new{nil}
|
122
|
+
while a_thread
|
123
|
+
@child_threads_lock.synchronize do
|
124
|
+
a_thread = @child_threads.shift
|
125
|
+
end
|
126
|
+
a_thread.join if a_thread
|
127
|
+
sleep 0 # Yield to other threads
|
128
|
+
end
|
129
|
+
nil end
|
166
130
|
|
167
|
-
|
131
|
+
# Flush/run queue of [proc, retain]s, retaining those with retain==true
|
132
|
+
def run_hooks(queue)
|
133
|
+
retained = Queue.new
|
134
|
+
while not queue.empty?
|
135
|
+
proc, retain = queue.shift
|
136
|
+
retained << [proc, retain] if retain
|
137
|
+
proc.call
|
138
|
+
end
|
139
|
+
while not retained.empty?
|
140
|
+
queue << retained.shift
|
141
|
+
end
|
142
|
+
nil end
|
168
143
|
|
169
|
-
|
170
|
-
|
171
|
-
|
144
|
+
# Run before_kill hooks, optionally join child threads, then die
|
145
|
+
def die
|
146
|
+
run_hooks(@before_kills)
|
147
|
+
join_children if @finish_all
|
148
|
+
@state = :dead
|
149
|
+
nil end
|
172
150
|
|
173
|
-
#
|
174
|
-
|
151
|
+
# Run main event loop, not finished until Hub is killed
|
152
|
+
def run_loop
|
153
|
+
@state = :alive
|
154
|
+
|
155
|
+
while not dead?
|
156
|
+
if @queue.empty? then sleep(0)
|
157
|
+
else process_item(@queue.shift) end
|
158
|
+
|
159
|
+
if dying?
|
160
|
+
die_thread ||= Thread.new { die }
|
161
|
+
end
|
162
|
+
end
|
175
163
|
|
176
|
-
|
177
|
-
@
|
164
|
+
run_hooks(@after_kills)
|
165
|
+
@finish_all = false
|
178
166
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
167
|
+
nil end
|
168
|
+
|
169
|
+
def process_item(x)
|
170
|
+
x, waiting_thread = x
|
171
|
+
string, event, blocking, proc = x
|
172
|
+
|
173
|
+
# Do all dealings with @child_threads under mutex
|
174
|
+
@child_threads_lock.synchronize do
|
175
|
+
|
176
|
+
# Clear dead child threads to free up memory
|
177
|
+
@child_threads.select! {|t| t.status}
|
178
|
+
|
179
|
+
# Start the new child thread
|
180
|
+
@child_threads << Thread.new do
|
181
|
+
begin
|
182
|
+
waiting_thread.wakeup unless blocking or not waiting_thread
|
183
|
+
proc.call(event)
|
184
|
+
waiting_thread.wakeup if blocking and waiting_thread
|
185
|
+
|
186
|
+
rescue Interrupt, SystemExit => e
|
187
|
+
@state = :dying
|
188
|
+
unhandled_exception(e)
|
189
|
+
|
190
|
+
rescue Exception => e
|
191
|
+
unhandled_exception(e)
|
192
|
+
end
|
192
193
|
end
|
194
|
+
|
193
195
|
end
|
194
|
-
|
195
|
-
end
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
nil end
|
196
|
+
|
197
|
+
nil end
|
198
|
+
|
199
|
+
def unhandled_exception(x)
|
200
|
+
$stderr.puts $!
|
201
|
+
$stderr.puts $@
|
202
|
+
nil end
|
203
|
+
end
|
203
204
|
end
|
204
|
-
end
|
205
|
+
end
|