zmachine 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,120 @@
1
+ package com.liquidm.zmachine;
2
+
3
+ import java.lang.System;
4
+ import java.util.ArrayList;
5
+ import java.util.Iterator;
6
+ import java.util.concurrent.Callable;
7
+
8
+ public class HashedWheel
9
+ {
10
+ private class Timeout
11
+ {
12
+ long deadline;
13
+ Object callback;
14
+ boolean canceled;
15
+
16
+ public Timeout(long deadline, Object callback)
17
+ {
18
+ this.deadline = deadline;
19
+ this.callback = callback;
20
+ this.canceled = false;
21
+ }
22
+
23
+ public Object getCallback()
24
+ {
25
+ return this.callback;
26
+ }
27
+
28
+ public void cancel()
29
+ {
30
+ this.canceled = true;
31
+ }
32
+
33
+ public boolean isCanceled()
34
+ {
35
+ return this.canceled;
36
+ }
37
+ }
38
+
39
+ int number_of_slots;
40
+ long tick_length;
41
+ Object[] slots;
42
+ int current_tick;
43
+ long last;
44
+
45
+ public HashedWheel(int number_of_slots, long tick_length)
46
+ {
47
+ this.number_of_slots = number_of_slots;
48
+ this.tick_length = tick_length * 1000000; // ms to ns
49
+ reset();
50
+ }
51
+
52
+ @SuppressWarnings("unchecked")
53
+ public Object[] getSlots()
54
+ {
55
+ return this.slots;
56
+ }
57
+
58
+ public Timeout add(int timeout)
59
+ {
60
+ return add(timeout, null);
61
+ }
62
+
63
+ @SuppressWarnings("unchecked")
64
+ public Timeout add(long timeout, Object callback)
65
+ {
66
+ timeout = timeout * 1000000; // ms to ns
67
+ long ticks = timeout / this.tick_length;
68
+ int slot = (int)((this.current_tick + ticks) % this.number_of_slots);
69
+ long deadline = System.nanoTime() + timeout;
70
+ Timeout hwt = new Timeout(deadline, callback);
71
+ ArrayList<Timeout> list = (ArrayList<Timeout>) this.slots[slot];
72
+ list.add(hwt);
73
+ return hwt;
74
+ }
75
+
76
+ public long reset()
77
+ {
78
+ return reset(System.nanoTime());
79
+ }
80
+
81
+ public long reset(long last)
82
+ {
83
+ this.slots = new Object[this.number_of_slots];
84
+ for (int i = 0; i < this.number_of_slots; i++) {
85
+ this.slots[i] = new ArrayList<Timeout>();
86
+ }
87
+ this.current_tick = 0;
88
+ this.last = last;
89
+ return last;
90
+ }
91
+
92
+ public ArrayList<Timeout> advance()
93
+ {
94
+ return advance(System.nanoTime());
95
+ }
96
+
97
+ @SuppressWarnings("unchecked")
98
+ public ArrayList<Timeout> advance(long now)
99
+ {
100
+ long passed_ticks = (now - this.last) / this.tick_length;
101
+ ArrayList<Timeout> result = new ArrayList<Timeout>();
102
+ do {
103
+ this.current_tick = this.current_tick % this.number_of_slots;
104
+ Iterator<Timeout> it = ((ArrayList<Timeout>)this.slots[this.current_tick]).iterator();
105
+ while (it.hasNext()) {
106
+ Timeout timeout = it.next();
107
+ if (timeout.deadline < now) {
108
+ if (!timeout.isCanceled()) {
109
+ result.add(timeout);
110
+ }
111
+ it.remove();
112
+ }
113
+ }
114
+ this.current_tick += 1;
115
+ passed_ticks -= 1;
116
+ } while (passed_ticks > 0);
117
+ this.last = now;
118
+ return result;
119
+ }
120
+ }
@@ -46,8 +46,8 @@ module ZMachine
46
46
  timer_or_sig.cancel # we do not support signatures
47
47
  end
48
48
 
49
- def self.close_connection(connection, reason = nil)
50
- reactor.close_connection(connection, reason)
49
+ def self.close_connection(connection, after_writing = false, reason = nil)
50
+ reactor.close_connection(connection, after_writing, reason)
51
51
  end
52
52
 
53
53
  def self.connect(server, port_or_type=nil, handler=nil, *args, &block)
@@ -59,11 +59,11 @@ module ZMachine
59
59
  end
60
60
 
61
61
  def self.heartbeat_interval
62
- reactor.heartbeat_interval
62
+ @heartbeat_interval
63
63
  end
64
64
 
65
65
  def self.heartbeat_interval=(time)
66
- reactor.heartbeat_interval = time
66
+ @heartbeat_interval = time
67
67
  end
68
68
 
69
69
  def self.next_tick(callback=nil, &block)
@@ -8,7 +8,6 @@ module ZMachine
8
8
  attr_accessor :raw
9
9
 
10
10
  def initialize
11
- @inbound_buffer = ByteBuffer.allocate(1024 * 1024)
12
11
  @outbound_queue = ConcurrentLinkedQueue.new
13
12
  @raw = false
14
13
  end
@@ -30,7 +29,7 @@ module ZMachine
30
29
  # write_outbound_data
31
30
 
32
31
  def can_send?
33
- !@outbound_queue.empty?
32
+ connected? && !@outbound_queue.empty?
34
33
  end
35
34
 
36
35
  def send_data(data)
@@ -55,25 +54,13 @@ module ZMachine
55
54
  break if buffer.has_remaining
56
55
  @outbound_queue.poll
57
56
  end
58
- maybe_close_with_callback
59
57
  end
60
58
 
61
- def close(after_writing = false, &block)
59
+ def close
62
60
  return true if closed?
63
- ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self, after_writing: after_writing, caller: caller[0].inspect) if ZMachine.debug
64
- @close_scheduled = true
65
- @closed_callback = block if block
66
- @outbound_queue.clear unless after_writing
67
- maybe_close_with_callback
68
- end
69
-
70
- def maybe_close_with_callback
71
- ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self, can_send: can_send?) if ZMachine.debug
72
- return false if can_send?
73
- return true unless @close_scheduled
61
+ ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self, caller: caller[0].inspect) if ZMachine.debug
62
+ @outbound_queue.clear
74
63
  close!
75
- @closed_callback.call if @closed_callback
76
- return true
77
64
  end
78
65
 
79
66
  end
@@ -10,6 +10,7 @@ module ZMachine
10
10
  extend Forwardable
11
11
 
12
12
  attr_accessor :channel
13
+ attr_reader :timer
13
14
 
14
15
  def self.new(*args)
15
16
  allocate.instance_eval do
@@ -61,14 +62,13 @@ module ZMachine
61
62
  # EventMachine Connection API
62
63
 
63
64
  def_delegator :@channel, :bound?
65
+ def_delegator :@channel, :can_send?
64
66
  def_delegator :@channel, :closed?
65
67
  def_delegator :@channel, :connected?
66
68
  def_delegator :@channel, :connection_pending?
67
69
 
68
70
  def close_connection(after_writing = false)
69
- @channel.close(after_writing) do
70
- ZMachine.close_connection(self)
71
- end
71
+ ZMachine.close_connection(self, after_writing)
72
72
  end
73
73
 
74
74
  alias :close :close_connection
@@ -79,6 +79,11 @@ module ZMachine
79
79
 
80
80
  alias :close_after_writing close_connection_after_writing
81
81
 
82
+ def close!
83
+ @timer.cancel if @timer
84
+ @channel.close!
85
+ end
86
+
82
87
  def comm_inactivity_timeout
83
88
  @inactivity_timeout
84
89
  end
@@ -213,8 +218,7 @@ module ZMachine
213
218
  readable! if @channel_key.readable?
214
219
  end
215
220
  rescue Java::JavaNioChannels::CancelledKeyException
216
- # channel may have been closed by write handler. ignore exception and
217
- # wait for cleanup
221
+ ZMachine.close_connection(self)
218
222
  end
219
223
 
220
224
  def mark_active!
@@ -225,9 +229,9 @@ module ZMachine
225
229
  def renew_timer
226
230
  @timer.cancel if @timer
227
231
  if connection_pending? && @connect_timeout
228
- @timer = ZMachine.add_timer(@connect_timeout) { ZMachine.close_connection(self, Errno::ETIMEDOUT) }
232
+ @timer = ZMachine.add_timer(@connect_timeout) { ZMachine.close_connection(self, true, Errno::ETIMEDOUT) }
229
233
  elsif @inactivity_timeout
230
- @timer = ZMachine.add_timer(@inactivity_timeout) { ZMachine.close_connection(self, Errno::ETIMEDOUT) }
234
+ @timer = ZMachine.add_timer(@inactivity_timeout) { ZMachine.close_connection(self, true, Errno::ETIMEDOUT) }
231
235
  end
232
236
  end
233
237
 
@@ -15,7 +15,7 @@ module ZMachine
15
15
  @connections = Set.new
16
16
  @zmq_connections = Set.new
17
17
  @new_connections = Set.new
18
- @unbound_connections = Set.new
18
+ @closing_connections = []
19
19
  end
20
20
 
21
21
  def idle?
@@ -25,7 +25,7 @@ module ZMachine
25
25
 
26
26
  def shutdown
27
27
  ZMachine.logger.debug("zmachine:connection_manager:#{__method__}") if ZMachine.debug
28
- @unbound_connections += @connections
28
+ @closing_connections += @connections.to_a
29
29
  cleanup
30
30
  end
31
31
 
@@ -73,12 +73,12 @@ module ZMachine
73
73
  new_connection = connection.process_events
74
74
  @new_connections << new_connection if new_connection
75
75
  rescue IOException => e
76
- close_connection(connection, e)
76
+ close_connection(connection, false, e)
77
77
  end
78
78
 
79
- def close_connection(connection, reason = nil)
80
- ZMachine.logger.debug("zmachine:connection_manager:#{__method__}", connection: connection, reason: reason.inspect) if ZMachine.debug
81
- @unbound_connections << [connection, reason]
79
+ def close_connection(connection, after_writing = false, reason = nil)
80
+ ZMachine.logger.debug("zmachine:connection_manager:#{__method__}", connection: connection, after_writing: after_writing, reason: reason.inspect) if ZMachine.debug
81
+ @closing_connections << [connection, after_writing, reason]
82
82
  end
83
83
 
84
84
  def add_new_connections
@@ -92,7 +92,7 @@ module ZMachine
92
92
  connection.connection_completed
93
93
  end
94
94
  rescue ClosedChannelException => e
95
- @unbound_connections << [connection, e]
95
+ @closing_connections << [connection, false, e]
96
96
  end
97
97
  end
98
98
  @new_connections.clear
@@ -103,25 +103,34 @@ module ZMachine
103
103
  end
104
104
 
105
105
  def cleanup
106
- return if @unbound_connections.empty?
106
+ return if @closing_connections.empty?
107
107
  ZMachine.logger.debug("zmachine:connection_manager:#{__method__}") if ZMachine.debug
108
- @unbound_connections.each do |connection|
109
- reason = nil
110
- connection, reason = *connection if connection.is_a?(Array)
111
- begin
112
- @connections.delete(connection)
113
- @zmq_connections.delete(connection)
114
- if connection.method(:unbind).arity != 0
115
- connection.unbind(reason)
116
- else
117
- connection.unbind
118
- end
119
- connection.channel.close!
120
- rescue Exception => e
121
- ZMachine.logger.exception(e, "failed to unbind connection") if ZMachine.debug
122
- end
108
+ closing_connections = @closing_connections
109
+ @closing_connections = []
110
+ closing_connections.each do |connection|
111
+ unbind_connection(connection)
112
+ end
113
+ end
114
+
115
+ def unbind_connection(connection)
116
+ after_writing = false
117
+ reason = nil
118
+ connection, after_writing, reason = *connection if connection.is_a?(Array)
119
+ if connection.method(:unbind).arity != 0
120
+ connection.unbind(reason)
121
+ else
122
+ connection.unbind
123
+ end
124
+ ZMachine.logger.debug("zmachine:connection_manager:#{__method__}", connection: connection, after_writing: after_writing, can_send: connection.can_send?) if ZMachine.debug
125
+ if after_writing && connection.can_send?
126
+ ZMachine.close_connection(connection, true)
127
+ else
128
+ connection.close!
129
+ @connections.delete(connection)
130
+ @zmq_connections.delete(connection)
123
131
  end
124
- @unbound_connections.clear
132
+ rescue Exception => e
133
+ ZMachine.logger.exception(e, "failed to unbind connection") if ZMachine.debug
125
134
  end
126
135
 
127
136
  private
@@ -1,70 +1,5 @@
1
- java_import java.lang.System
1
+ $CLASSPATH << File.expand_path("../../../java", __FILE__)
2
2
 
3
3
  module ZMachine
4
-
5
- class HashedWheelTimeout
6
- attr_reader :deadline
7
- attr_reader :callback
8
-
9
- def initialize(deadline, &block)
10
- @deadline = deadline
11
- @callback = block
12
- @canceled = false
13
- end
14
-
15
- def cancel
16
- @canceled = true
17
- end
18
-
19
- def canceled?
20
- @canceled
21
- end
22
- end
23
-
24
- class HashedWheel
25
- attr_reader :slots
26
- attr_accessor :last
27
-
28
- def initialize(number_of_slots, tick_length, start_time = System.nano_time)
29
- @slots = Array.new(number_of_slots) { [] }
30
- @tick_length = tick_length * 1_000_000_000
31
- @last = start_time
32
- @current_tick = 0
33
- end
34
-
35
- def add(timeout, &block)
36
- timeout *= 1_000_000_000 # s to ns
37
- ticks = timeout / @tick_length
38
- slot = (@current_tick + ticks) % @slots.length
39
- deadline = System.nano_time + timeout
40
- @slots[slot] << hwt = HashedWheelTimeout.new(deadline, &block)
41
- hwt
42
- end
43
-
44
- def reset(time = nil)
45
- @slots = Array.new(@slots.length) { [] }
46
- @current_tick = 0
47
- @last = time || System.nano_time
48
- end
49
-
50
- # returns all timeouts
51
- def advance(now = nil)
52
- now ||= System.nano_time
53
- passed_ticks = (now - @last) / @tick_length
54
- result = []
55
- begin
56
- @current_tick %= @slots.length
57
- @slots[@current_tick].delete_if do |timeout|
58
- result << timeout if timeout.deadline < now
59
- end
60
- @current_tick += 1
61
- passed_ticks -= 1
62
- end while passed_ticks > 0
63
- @last = now
64
- result.reject do |timeout|
65
- timeout.canceled?
66
- end
67
- end
68
-
69
- end
4
+ HashedWheel = Java::ComLiquidmZmachine::HashedWheel
70
5
  end
@@ -35,12 +35,12 @@ module ZMachine
35
35
  end
36
36
 
37
37
  def initialize
38
- # a 10 ms tick wheel with 512 slots => ~5s for a round
39
- @heartbeat_interval = 0.5 # coarse grained by default
38
+ @heartbeat_interval = ZMachine.heartbeat_interval || 0.5 # coarse grained by default
40
39
  @next_tick_queue = ConcurrentLinkedQueue.new
41
40
  @running = false
42
41
  @shutdown_hooks = []
43
- @wheel = HashedWheel.new(512, 0.01)
42
+ # a 10 ms tick wheel with 512 slots => ~5s for a round
43
+ @wheel = HashedWheel.new(512, 10)
44
44
  end
45
45
 
46
46
  def add_shutdown_hook(&block)
@@ -53,7 +53,7 @@ module ZMachine
53
53
  callback = args.shift || block
54
54
  ZMachine.logger.debug("zmachine:reactor:#{__method__}", interval: interval, callback: callback) if ZMachine.debug
55
55
  return unless callback
56
- @wheel.add(interval, &callback)
56
+ @wheel.add((interval * 1000).to_i, &callback)
57
57
  end
58
58
 
59
59
  def bind(server, port_or_type=nil, handler=nil, *args, &block)
@@ -62,9 +62,9 @@ module ZMachine
62
62
  @connection_manager.bind(server, port_or_type, handler, *args, &block)
63
63
  end
64
64
 
65
- def close_connection(connection, reason = nil)
65
+ def close_connection(connection, after_writing = false, reason = nil)
66
66
  return true unless @connection_manager
67
- @connection_manager.close_connection(connection, reason)
67
+ @connection_manager.close_connection(connection, after_writing, reason)
68
68
  end
69
69
 
70
70
  def connect(server, port_or_type=nil, handler=nil, *args, &block)
@@ -94,7 +94,6 @@ module ZMachine
94
94
  def reconnect(server, port_or_type, handler)
95
95
  return handler if handler && handler.channel.is_a?(ZMQChannel)
96
96
  ZMachine.logger.debug("zmachine:reactor:#{__method__}", server: server, port_or_type: port_or_type) if ZMachine.debug
97
- return handler if handler.connected?
98
97
  connect(server, port_or_type, handler)
99
98
  end
100
99