zmachine 0.3.2 → 0.4.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.
@@ -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