zmachine 0.1.3 → 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 +6 -14
- data/.gitignore +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +5 -2
- data/README.md +3 -1
- data/Rakefile +1 -0
- data/benchmarks/benchmark.sh +12 -0
- data/benchmarks/tcp_channel.rb +26 -0
- data/benchmarks/zmq_channel.rb +23 -0
- data/echo_client.rb +10 -28
- data/echo_server.rb +15 -22
- data/lib/zmachine.rb +71 -23
- data/lib/zmachine/channel.rb +29 -27
- data/lib/zmachine/connection.rb +178 -51
- data/lib/zmachine/connection_manager.rb +133 -0
- data/lib/zmachine/hashed_wheel.rb +23 -31
- data/lib/zmachine/reactor.rb +66 -393
- data/lib/zmachine/tcp_channel.rb +47 -109
- data/lib/zmachine/zmq_channel.rb +74 -115
- data/spec/connection_manager_spec.rb +16 -0
- data/spec/connection_spec.rb +56 -0
- data/spec/hashed_wheel_spec.rb +15 -12
- data/spec/spec_helper.rb +22 -14
- data/spec/tcp_channel_spec.rb +109 -0
- data/spec/zmq_channel_spec.rb +113 -0
- data/zmachine.gemspec +4 -2
- metadata +54 -26
@@ -1,18 +1,27 @@
|
|
1
|
+
java_import java.lang.System
|
2
|
+
|
1
3
|
module ZMachine
|
2
4
|
|
3
5
|
class HashedWheelTimeout
|
4
|
-
attr_accessor :round
|
5
6
|
attr_reader :deadline
|
6
7
|
attr_reader :callback
|
7
|
-
|
8
|
-
|
8
|
+
|
9
|
+
def initialize(deadline, &block)
|
9
10
|
@deadline = deadline
|
10
11
|
@callback = block
|
12
|
+
@canceled = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def cancel
|
16
|
+
@canceled = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def canceled?
|
20
|
+
@canceled
|
11
21
|
end
|
12
22
|
end
|
13
23
|
|
14
24
|
class HashedWheel
|
15
|
-
|
16
25
|
attr_reader :slots
|
17
26
|
attr_accessor :last
|
18
27
|
|
@@ -21,26 +30,15 @@ module ZMachine
|
|
21
30
|
@tick_length = tick_length * 1_000_000
|
22
31
|
@last = start_time
|
23
32
|
@current_tick = 0
|
24
|
-
|
25
|
-
@next = nil
|
26
|
-
end
|
27
|
-
|
28
|
-
def next_deadline
|
29
|
-
return Float::INFINITY if !@next
|
30
|
-
@next.deadline
|
31
33
|
end
|
32
34
|
|
33
35
|
def add(timeout, &block)
|
34
36
|
timeout *= 1_000_000 # ms to ns
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@next = hwt
|
40
|
-
else
|
41
|
-
@next = hwt if @next.deadline > hwt.deadline
|
37
|
+
ticks = timeout / @tick_length
|
38
|
+
slot = (@current_tick + ticks) % @slots.length
|
39
|
+
HashedWheelTimeout.new(System.nano_time + timeout, &block).tap do |hwt|
|
40
|
+
@slots[slot] << hwt
|
42
41
|
end
|
43
|
-
hwt
|
44
42
|
end
|
45
43
|
|
46
44
|
def reset(time = System.nano_time)
|
@@ -51,27 +49,21 @@ module ZMachine
|
|
51
49
|
|
52
50
|
# returns all timeouts
|
53
51
|
def advance(now = System.nano_time)
|
54
|
-
# how many tickts have passed?
|
55
52
|
passed_ticks = (now - @last) / @tick_length
|
56
|
-
round = 0
|
57
53
|
result = []
|
58
54
|
begin
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
@slots[@current_tick].delete_if do |timeout|
|
63
|
-
timeout.round -= round
|
64
|
-
result << timeout if timeout.round <= 0 && timeout.deadline < now
|
55
|
+
@current_tick %= @slots.length
|
56
|
+
@slots[@current_tick].delete_if do |timeout|
|
57
|
+
result << timeout if timeout.deadline < now
|
65
58
|
end
|
66
59
|
@current_tick += 1
|
67
60
|
passed_ticks -= 1
|
68
61
|
end while passed_ticks > 0
|
69
62
|
@last = now
|
70
|
-
result
|
63
|
+
result.reject do |timeout|
|
64
|
+
timeout.canceled?
|
65
|
+
end
|
71
66
|
end
|
72
67
|
|
73
68
|
end
|
74
69
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
data/lib/zmachine/reactor.rb
CHANGED
@@ -1,32 +1,14 @@
|
|
1
1
|
java_import java.lang.System
|
2
|
-
java_import java.io.FileDescriptor
|
3
|
-
java_import java.io.IOException
|
4
|
-
java_import java.net.InetSocketAddress
|
5
|
-
java_import java.nio.ByteBuffer
|
6
|
-
java_import java.nio.channels.ClosedChannelException
|
7
|
-
java_import java.nio.channels.SelectionKey
|
8
2
|
java_import java.nio.channels.Selector
|
9
|
-
java_import java.nio.channels.ServerSocketChannel
|
10
|
-
java_import java.nio.channels.SocketChannel
|
11
|
-
java_import java.util.TreeMap
|
12
|
-
java_import java.util.concurrent.atomic.AtomicBoolean
|
13
3
|
java_import java.util.concurrent.ConcurrentLinkedQueue
|
14
4
|
|
15
|
-
require 'zmachine/jeromq-0.3.0-SNAPSHOT.jar'
|
16
|
-
java_import org.zeromq.ZContext
|
17
|
-
|
18
|
-
require 'zmachine/acceptor'
|
19
|
-
require 'zmachine/tcp_channel'
|
20
|
-
require 'zmachine/zmq_channel'
|
21
5
|
require 'zmachine/hashed_wheel'
|
6
|
+
require 'zmachine/connection_manager'
|
22
7
|
|
23
8
|
module ZMachine
|
24
9
|
|
25
|
-
class NotReactorOwner < Exception
|
26
|
-
end
|
27
|
-
|
28
|
-
class NoReactorError < Exception
|
29
|
-
end
|
10
|
+
class NotReactorOwner < Exception; end
|
11
|
+
class NoReactorError < Exception; end
|
30
12
|
|
31
13
|
class Reactor
|
32
14
|
|
@@ -40,7 +22,6 @@ module ZMachine
|
|
40
22
|
end
|
41
23
|
|
42
24
|
def self.terminate_all_reactors
|
43
|
-
# should have a lock ....
|
44
25
|
@mutex.synchronize do
|
45
26
|
@reactors.each(&:stop_event_loop)
|
46
27
|
@reactors.clear
|
@@ -54,186 +35,121 @@ module ZMachine
|
|
54
35
|
end
|
55
36
|
|
56
37
|
def initialize
|
57
|
-
#
|
58
|
-
@
|
59
|
-
@last = now = System.nano_time
|
60
|
-
@heartbeat_interval = 50_000_000 # 50 ms
|
61
|
-
@timers = TreeMap.new
|
62
|
-
@timer_callbacks = {}
|
63
|
-
@channels = []
|
64
|
-
@new_channels = []
|
65
|
-
@unbound_channels = []
|
66
|
-
@next_signature = 0
|
67
|
-
@shutdown_hooks = []
|
38
|
+
# a 10 ms tick wheel with 512 slots => ~5s for a round
|
39
|
+
@heartbeat_interval = 0.5 # coarse grained by default
|
68
40
|
@next_tick_queue = ConcurrentLinkedQueue.new
|
69
41
|
@running = false
|
70
|
-
|
71
|
-
|
72
|
-
@read_buffer = ByteBuffer.allocate(32*1024)
|
42
|
+
@shutdown_hooks = []
|
43
|
+
@wheel = HashedWheel.new(512, 10)
|
73
44
|
end
|
74
45
|
|
75
46
|
def add_shutdown_hook(&block)
|
76
47
|
@shutdown_hooks << block
|
77
48
|
end
|
78
49
|
|
79
|
-
def
|
50
|
+
def add_timer(*args, &block)
|
80
51
|
check_reactor_thread
|
81
52
|
interval = args.shift
|
82
53
|
callback = args.shift || block
|
54
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", interval: interval, callback: callback) if ZMachine.debug
|
83
55
|
return unless callback
|
84
56
|
@wheel.add((interval.to_f * 1000).to_i, &callback)
|
85
57
|
end
|
86
58
|
|
87
|
-
|
88
|
-
|
59
|
+
def bind(server, port_or_type=nil, handler=nil, *args, &block)
|
60
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", server: server, port_or_type: port_or_type) if ZMachine.debug
|
89
61
|
check_reactor_thread
|
90
|
-
|
91
|
-
callback = args.shift || block
|
92
|
-
return unless callback
|
93
|
-
|
94
|
-
signature = next_signature
|
95
|
-
deadline = System.nano_time + (interval.to_f * 1000_000_000).to_i
|
96
|
-
|
97
|
-
if @timers.contains_key(deadline)
|
98
|
-
@timers.get(deadline) << signature
|
99
|
-
else
|
100
|
-
@timers.put(deadline, [signature])
|
101
|
-
end
|
102
|
-
|
103
|
-
ZMachine.logger.debug("zmachine:#{__method__}", signature: signature) if ZMachine.debug
|
104
|
-
@timer_callbacks[signature] = callback
|
105
|
-
signature
|
106
|
-
end
|
107
|
-
|
108
|
-
def unbind_channel(channel)
|
109
|
-
channel.handler = nil
|
110
|
-
ZMachine.logger.debug "zmachine:unbind_channel", channel:channel if ZMachine.debug
|
111
|
-
@unbound_channels << channel
|
62
|
+
@connection_manager.bind(server, port_or_type, handler, *args, &block)
|
112
63
|
end
|
113
64
|
|
114
|
-
def
|
115
|
-
|
116
|
-
timer_or_sig.cancel
|
117
|
-
else
|
118
|
-
ZMachine.logger.debug("zmachine:#{__method__}", signature: timer_or_sig) if ZMachine.debug
|
119
|
-
@timer_callbacks[timer_or_sig] = false if @timer_callbacks.has_key?(timer_or_sig)
|
120
|
-
end
|
65
|
+
def close_connection(connection)
|
66
|
+
@connection_manager.close_connection(connection)
|
121
67
|
end
|
122
68
|
|
123
69
|
def connect(server, port_or_type=nil, handler=nil, *args, &block)
|
124
|
-
ZMachine.logger.debug("zmachine:#{__method__}", server: server, port_or_type: port_or_type) if ZMachine.debug
|
70
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", server: server, port_or_type: port_or_type) if ZMachine.debug
|
125
71
|
check_reactor_thread
|
126
|
-
|
127
|
-
_connect_zmq(server, port_or_type, handler, *args, &block)
|
128
|
-
else
|
129
|
-
_connect_tcp(server, port_or_type, handler, *args, &block)
|
130
|
-
end
|
131
|
-
rescue java.nio.channels.UnresolvedAddressException
|
132
|
-
raise ZMachine::ConnectionError.new('unable to resolve server address')
|
72
|
+
@connection_manager.connect(server, port_or_type, handler, *args, &block)
|
133
73
|
end
|
134
74
|
|
135
|
-
def
|
136
|
-
|
137
|
-
return if handler && handler.channel.is_a?(ZMQChannel) #
|
138
|
-
# TODO : we might want to check if this connection is really dead?
|
139
|
-
connect server, port, handler
|
75
|
+
def connections
|
76
|
+
@connection_manager.connections
|
140
77
|
end
|
141
78
|
|
142
|
-
def
|
143
|
-
@
|
79
|
+
def heartbeat_interval
|
80
|
+
@heartbeat_interval
|
144
81
|
end
|
145
82
|
|
146
|
-
def
|
147
|
-
|
83
|
+
def heartbeat_interval=(value)
|
84
|
+
value = 0.01 if value < 0.01
|
85
|
+
@heartbeat_interval = value
|
148
86
|
end
|
149
87
|
|
150
88
|
def next_tick(callback=nil, &block)
|
151
89
|
@next_tick_queue << (callback || block)
|
152
|
-
|
90
|
+
wakeup if running?
|
153
91
|
end
|
154
92
|
|
155
|
-
def
|
156
|
-
|
157
|
-
|
158
|
-
@
|
93
|
+
def reconnect(server, port_or_type, handler)
|
94
|
+
return if handler && handler.channel.is_a?(ZMQChannel)
|
95
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", server: server, port_or_type: port_or_type) if ZMachine.debug
|
96
|
+
@connection_manager.connect(server, port_or_type, handler)
|
97
|
+
end
|
159
98
|
|
99
|
+
def run(callback=nil, shutdown_hook=nil, &block)
|
100
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}") if ZMachine.debug
|
160
101
|
add_shutdown_hook(shutdown_hook) if shutdown_hook
|
161
|
-
|
162
102
|
begin
|
163
|
-
# list of active reactors
|
164
103
|
Reactor.register_reactor(self)
|
165
|
-
|
166
104
|
@running = true
|
167
|
-
|
168
|
-
|
169
|
-
add_timer(0) do
|
170
|
-
@callback.call(self)
|
171
|
-
end
|
105
|
+
if callback = (callback || block)
|
106
|
+
add_timer(0) { callback.call(self) }
|
172
107
|
end
|
173
|
-
|
174
108
|
@selector = Selector.open
|
109
|
+
@connection_manager = ConnectionManager.new(@selector)
|
175
110
|
@run_reactor = true
|
176
|
-
|
177
|
-
while @run_reactor
|
178
|
-
ZMachine.logger.debug("zmachine:#{__method__}", run_reactor: true) if ZMachine.debug
|
179
|
-
run_deferred_callbacks
|
180
|
-
break unless @run_reactor
|
181
|
-
run_timers
|
182
|
-
break unless @run_reactor
|
183
|
-
# advance_timer_wheel
|
184
|
-
# break unless @run_reactor
|
185
|
-
remove_timed_out_channels
|
186
|
-
remove_unbound_channels
|
187
|
-
check_io
|
188
|
-
add_new_channels
|
189
|
-
process_io
|
190
|
-
end
|
191
|
-
rescue => e
|
192
|
-
puts e.message
|
193
|
-
raise e
|
194
|
-
# # maybe add error check callback here
|
195
|
-
# puts "FATAL reactor died : #{e.message}"
|
196
|
-
# puts e.backtrace
|
111
|
+
run_reactor while @run_reactor
|
197
112
|
ensure
|
198
|
-
ZMachine.logger.debug("zmachine:#{__method__}", stop: :selector) if ZMachine.debug
|
113
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", stop: :selector) if ZMachine.debug
|
199
114
|
@selector.close rescue nil
|
200
115
|
@selector = nil
|
201
|
-
ZMachine.logger.debug("zmachine:#{__method__}", stop: :
|
202
|
-
@
|
203
|
-
|
204
|
-
ZMachine.logger.debug("zmachine:#{__method__}", stop: :shutdown_hooks) if ZMachine.debug
|
116
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", stop: :connections) if ZMachine.debug
|
117
|
+
@connection_manager.shutdown
|
118
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", stop: :shutdown_hooks) if ZMachine.debug
|
205
119
|
@shutdown_hooks.pop.call until @shutdown_hooks.empty?
|
206
120
|
@next_tick_queue = ConcurrentLinkedQueue.new
|
207
121
|
@running = false
|
208
122
|
Reactor.unregister_reactor(self)
|
209
|
-
ZMachine.logger.debug("zmachine:#{__method__}", stop: :zcontext) if ZMachine.debug
|
123
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", stop: :zcontext) if ZMachine.debug
|
210
124
|
ZMachine.context.destroy
|
211
125
|
ZMachine.clear_current_reactor
|
212
126
|
end
|
213
127
|
end
|
214
128
|
|
215
|
-
def
|
216
|
-
|
217
|
-
|
129
|
+
def run_reactor
|
130
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}") if ZMachine.debug
|
131
|
+
run_deferred_callbacks
|
132
|
+
break unless @run_reactor
|
133
|
+
run_timers
|
134
|
+
break unless @run_reactor
|
135
|
+
@connection_manager.cleanup
|
136
|
+
if @connection_manager.idle?
|
137
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", select: @heartbeat_interval) if ZMachine.debug
|
138
|
+
@selector.select(@heartbeat_interval * 1000)
|
139
|
+
else
|
140
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", select: :now) if ZMachine.debug
|
141
|
+
@selector.select_now
|
218
142
|
end
|
143
|
+
@connection_manager.process
|
219
144
|
end
|
220
145
|
|
221
|
-
def
|
222
|
-
@running
|
223
|
-
end
|
224
|
-
|
225
|
-
def start_server(server, port_or_type=nil, handler=nil, *args, &block)
|
226
|
-
ZMachine.logger.debug("zmachine:#{__method__}", server: server, port_or_type: port_or_type) if ZMachine.debug
|
227
|
-
if server =~ %r{\w+://}
|
228
|
-
_bind_zmq(server, port_or_type, handler, *args, &block)
|
229
|
-
else
|
230
|
-
_bind_tcp(server, port_or_type, handler, *args, &block)
|
231
|
-
end
|
146
|
+
def running?
|
147
|
+
@running
|
232
148
|
end
|
233
149
|
|
234
150
|
def stop_event_loop
|
235
151
|
@run_reactor = false
|
236
|
-
|
152
|
+
wakeup
|
237
153
|
end
|
238
154
|
|
239
155
|
def stop_server(channel)
|
@@ -247,268 +163,25 @@ module ZMachine
|
|
247
163
|
raise NotReactorOwner if Thread.current[:reactor] != self
|
248
164
|
end
|
249
165
|
|
250
|
-
|
251
|
-
def _bind_tcp(address, port, handler, *args, &block)
|
252
|
-
klass = _klass_from_handler(Connection, handler)
|
253
|
-
channel = TCPChannel.new(@selector)
|
254
|
-
channel.bind(address, port)
|
255
|
-
add_channel(channel, Acceptor, klass, *(args+[block]) )
|
256
|
-
channel.handler
|
257
|
-
end
|
258
|
-
|
259
|
-
def _bind_zmq(address, type, handler, *args, &block)
|
260
|
-
klass = _klass_from_handler(Connection, handler)
|
261
|
-
channel = ZMQChannel.new(type, @selector)
|
262
|
-
channel.bind(address)
|
263
|
-
add_channel(channel, klass, *args, &block)
|
264
|
-
channel.handler
|
265
|
-
end
|
266
|
-
|
267
|
-
def _connect_tcp(address, port, handler=nil, *args, &block)
|
268
|
-
klass = _klass_from_handler(Connection, handler)
|
269
|
-
channel = TCPChannel.new(@selector)
|
270
|
-
channel.connect(address, port)
|
271
|
-
channel.connect_pending = true
|
272
|
-
add_channel(channel, klass, *args, &block)
|
273
|
-
channel.mark_active!
|
274
|
-
channel.handler
|
275
|
-
end
|
276
|
-
|
277
|
-
def _connect_zmq(address, type, handler=nil, *args, &block)
|
278
|
-
klass = _klass_from_handler(Connection, handler)
|
279
|
-
channel = ZMQChannel.new(type, @selector)
|
280
|
-
add_channel(channel, klass, *args, &block)
|
281
|
-
channel.connect(address)
|
282
|
-
channel.handler.connection_completed
|
283
|
-
channel.handler
|
284
|
-
end
|
285
|
-
|
286
|
-
def check_io
|
287
|
-
|
288
|
-
now = System.nano_time
|
289
|
-
|
290
|
-
if @new_channels.size > 0
|
291
|
-
timeout = -1
|
292
|
-
elsif !@timers.empty?
|
293
|
-
timer_key = @timers.first_key
|
294
|
-
timeout = (timer_key - now)
|
295
|
-
timeout = -1 if timeout <= 0
|
296
|
-
else
|
297
|
-
# puts "hb = #{@heartbeat_interval}"
|
298
|
-
#timeout = (@heartbeat_interval - (now - @last)) / 1_000_000
|
299
|
-
timeout = 0
|
300
|
-
end
|
301
|
-
|
302
|
-
# timeout = @heartbeat_interval - (now - @last)
|
303
|
-
# wnd = @wheel.next_deadline - now
|
304
|
-
# timeout = wnd if timeout > wnd
|
305
|
-
# timeout = -1 if timeout == 0
|
306
|
-
# @last = now
|
307
|
-
|
308
|
-
# sucks a bit ...
|
309
|
-
timeout = -1 if @channels.any?(&:has_more?) # iterate all channels????
|
310
|
-
|
311
|
-
# we have to convert the timeout from ns to ms
|
312
|
-
if timeout > 0
|
313
|
-
timeout /= 1_000_000
|
314
|
-
timeout = -1 if timeout == 0 # check if it would be below 1ms .. 0 would be an indefinite wait but we want to select NOW
|
315
|
-
end
|
316
|
-
|
317
|
-
ZMachine.logger.debug("zmachine:#{__method__}", timeout: timeout) if ZMachine.debug
|
318
|
-
|
319
|
-
if timeout < 0
|
320
|
-
@selector.select_now
|
321
|
-
else
|
322
|
-
@selector.select(timeout)
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
def process_io
|
327
|
-
it = @selector.selected_keys.iterator
|
328
|
-
|
329
|
-
while it.has_next
|
330
|
-
selected_key = it.next
|
331
|
-
it.remove
|
332
|
-
if selected_key.connectable?
|
333
|
-
is_connectable(selected_key.attachment)
|
334
|
-
elsif selected_key.acceptable?
|
335
|
-
is_acceptable(selected_key.attachment)
|
336
|
-
else
|
337
|
-
is_writable(selected_key.attachment) if selected_key.writable?
|
338
|
-
is_readable(selected_key.attachment) if selected_key.readable?
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
@channels.each do |channel|
|
343
|
-
is_readable(channel) if channel.has_more?
|
344
|
-
is_writable(channel) if channel.can_send?
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
def is_acceptable(channel)
|
349
|
-
ZMachine.logger.debug("zmachine:#{__method__}", channel: channel) if ZMachine.debug
|
350
|
-
client_channel = channel.accept
|
351
|
-
acceptor = channel.handler
|
352
|
-
new_channel = add_channel(client_channel, acceptor.klass, *acceptor.args, &acceptor.callback)
|
353
|
-
new_channel.mark_active!
|
354
|
-
new_channel
|
355
|
-
rescue IOException => e
|
356
|
-
ZMachine.logger.debug("zmachine:#{__method__} unbind", channel: channel, e:e.message) if ZMachine.debug
|
357
|
-
channel.close
|
358
|
-
end
|
359
|
-
|
360
|
-
def is_readable(channel)
|
361
|
-
ZMachine.logger.debug("zmachine:#{__method__}", channel: channel) if ZMachine.debug
|
362
|
-
channel.mark_active!
|
363
|
-
data = channel.read_inbound_data(@read_buffer)
|
364
|
-
channel.handler.receive_data(data) if data
|
365
|
-
rescue IOException => e
|
366
|
-
ZMachine.logger.debug("zmachine:#{__method__} unbind", channel: channel, e:e.message) if ZMachine.debug
|
367
|
-
@unbound_channels << channel
|
368
|
-
end
|
369
|
-
|
370
|
-
def is_writable(channel)
|
371
|
-
ZMachine.logger.debug("zmachine:#{__method__}", channel: channel) if ZMachine.debug
|
372
|
-
channel.mark_active!
|
373
|
-
@unbound_channels << channel unless channel.write_outbound_data
|
374
|
-
rescue IOException => e
|
375
|
-
ZMachine.logger.debug("zmachine:#{__method__} unbind", channel: channel, e:e.message) if ZMachine.debug
|
376
|
-
@unbound_channels << channel
|
377
|
-
end
|
378
|
-
|
379
|
-
def is_connectable(channel)
|
380
|
-
channel.mark_active!
|
381
|
-
ZMachine.logger.debug("zmachine:#{__method__}", channel: channel) if ZMachine.debug
|
382
|
-
result = channel.finish_connecting
|
383
|
-
if !result
|
384
|
-
ZMachine.logger.warn("zmachine:finish_connecting failed", channel: channel) if !result
|
385
|
-
@unbound_channels << channel
|
386
|
-
else
|
387
|
-
channel.handler.connection_completed
|
388
|
-
end
|
389
|
-
rescue IOException => e
|
390
|
-
ZMachine.logger.debug("zmachine:#{__method__} unbind", channel: channel, e:e.message) if ZMachine.debug
|
391
|
-
@unbound_channels << channel
|
392
|
-
end
|
393
|
-
|
394
|
-
def add_channel(channel, klass_or_instance, *args, &block)
|
395
|
-
ZMachine.logger.debug("zmachine:#{__method__}", channel: channel) if ZMachine.debug
|
396
|
-
if klass_or_instance.is_a?(Connection)
|
397
|
-
# if klass_or_instance is not a class but already an instance
|
398
|
-
channel.handler = klass_or_instance
|
399
|
-
klass_or_instance.channel = channel
|
400
|
-
else
|
401
|
-
channel.handler = klass_or_instance.new(channel, *args)
|
402
|
-
end
|
403
|
-
channel.reactor = self
|
404
|
-
@new_channels << channel
|
405
|
-
block.call(channel.handler) if block
|
406
|
-
channel
|
407
|
-
rescue => e
|
408
|
-
puts "ERROR adding channel #{e.message}"
|
409
|
-
puts e.backtrace
|
410
|
-
end
|
411
|
-
|
412
|
-
def add_new_channels
|
413
|
-
@new_channels.each do |channel|
|
414
|
-
begin
|
415
|
-
channel.register
|
416
|
-
@channels << channel
|
417
|
-
rescue ClosedChannelException => e
|
418
|
-
puts "UNBIND on add_channel #{e.message}"
|
419
|
-
@unbound_channels << channel
|
420
|
-
end
|
421
|
-
end
|
422
|
-
@new_channels.clear
|
423
|
-
end
|
424
|
-
|
425
|
-
def remove_timed_out_channels
|
426
|
-
# TODO : we want to replace all of our timer handling with hashed wheels in the future
|
427
|
-
now = now = System.nano_time
|
428
|
-
@channels.each do |channel|
|
429
|
-
next if channel.comm_inactivity_timeout == 0
|
430
|
-
if !channel.was_active?(now)
|
431
|
-
ZMachine.logger.debug("zmachine:#{__method__} unbind", channel: channel, now:now,last:channel.last_comm_activity, delta:(now - channel.last_comm_activity), timeout:timeout=channel.comm_inactivity_timeout) if ZMachine.debug
|
432
|
-
channel.timedout!
|
433
|
-
@unbound_channels << channel
|
434
|
-
end
|
435
|
-
end
|
436
|
-
end
|
437
|
-
|
438
|
-
def remove_unbound_channels
|
439
|
-
return if @unbound_channels.empty?
|
440
|
-
@unbound_channels.each do |channel|
|
441
|
-
reason = nil
|
442
|
-
channel, reason = *channel if channel.is_a?(Array)
|
443
|
-
begin
|
444
|
-
# puts "#{channel} unbound"
|
445
|
-
@channels.delete(channel)
|
446
|
-
channel.handler.unbind if channel.handler
|
447
|
-
channel.close
|
448
|
-
rescue Exception => e
|
449
|
-
ZMachine.logger.debug("zmachine:#{__method__} unbind error", channel: channel, reason:reason, klass:e.class, msg:e.message) if ZMachine.debug
|
450
|
-
end
|
451
|
-
end
|
452
|
-
@unbound_channels.clear
|
453
|
-
end
|
454
|
-
|
455
166
|
def run_deferred_callbacks
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
begin
|
460
|
-
size -= 1
|
461
|
-
callback.call
|
462
|
-
# ensure
|
463
|
-
# ZMachine.next_tick {} if $!
|
464
|
-
end
|
167
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}") if ZMachine.debug
|
168
|
+
while callback = @next_tick_queue.poll
|
169
|
+
callback.call
|
465
170
|
end
|
466
171
|
end
|
467
172
|
|
468
|
-
# TODO : we should definitly optimize periodic timers ... right now they are wasting a hell of cycle es they are recreated on every invocation
|
469
173
|
def run_timers
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
signatures = @timers.get(timer_key)
|
475
|
-
@timers.remove(timer_key)
|
476
|
-
# Fire all timers at this timestamp
|
477
|
-
signatures.each do |signature|
|
478
|
-
callback = @timer_callbacks.delete(signature)
|
479
|
-
return if callback == false # callback cancelled
|
480
|
-
callback or raise UnknownTimerFired, "callback data: #{signature}"
|
481
|
-
callback.call
|
482
|
-
end
|
174
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}") if ZMachine.debug
|
175
|
+
@wheel.advance.each do |timeout|
|
176
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}", callback: timeout.callback) if ZMachine.debug
|
177
|
+
timeout.callback.call
|
483
178
|
end
|
484
179
|
end
|
485
180
|
|
486
|
-
def
|
181
|
+
def wakeup
|
182
|
+
ZMachine.logger.debug("zmachine:reactor:#{__method__}") if ZMachine.debug
|
487
183
|
@selector.wakeup if @selector
|
488
184
|
end
|
489
185
|
|
490
|
-
def next_signature
|
491
|
-
@next_signature += 1
|
492
|
-
end
|
493
|
-
|
494
|
-
def _klass_from_handler(klass = Connection, handler = nil)
|
495
|
-
if handler and handler.is_a?(Class)
|
496
|
-
handler
|
497
|
-
elsif handler and handler.is_a?(Connection)
|
498
|
-
# can happen on reconnect
|
499
|
-
handler
|
500
|
-
elsif handler
|
501
|
-
_handler_from_module(klass, handler)
|
502
|
-
else
|
503
|
-
klass
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
507
|
-
def _handler_from_module(klass, handler)
|
508
|
-
handler::CONNECTION_CLASS
|
509
|
-
rescue NameError
|
510
|
-
handler::const_set(:CONNECTION_CLASS, Class.new(klass) { include handler })
|
511
|
-
end
|
512
|
-
|
513
186
|
end
|
514
187
|
end
|