zmachine 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1057ef6c0aa36f93cf288ce0f90f6d231888c7b8
4
- data.tar.gz: bf0799c9baf044fdd371bdbf7d1b71e325cd419d
3
+ metadata.gz: ae108dc1b3349d10953fdbf480eb04399e955210
4
+ data.tar.gz: 0c21b76fea1dac030296db4af61132a4378f5c6c
5
5
  SHA512:
6
- metadata.gz: 01187e9649e5f6d48f03cb4ac0a8fd1766e1be10cba3fbfbaefdf49cefce174157c5fb155acd60effb79bc8e8a02a9c20ec3f5ec6779ca44dac90aa5294fddf4
7
- data.tar.gz: 8ea1eb56b13ce6b7786de2224859e6fdad142e14fe7602bf6a499e16a7853291dea5e504427f4e8a625b8ff5800a51dd76aa97bad52c0de908d82af89481bf40
6
+ metadata.gz: cf74518f087c16dbdc05982c3aae1beeea4e948995b83cdad5f77223cdcb0c6c90c1461df3c616ce5fa4713b337ed6f65736ece7c10874f3677239b38a6ee9b7
7
+ data.tar.gz: 1756c03d9be7a36ed549f8e954c74d8bf2614d7e9b1638b2bb9d343b4c4c566366aee682a05382a365754c72d630877db3242fa56ba5a9b76a2a9c85c8ffadc7
data/Gemfile CHANGED
@@ -4,3 +4,5 @@ gemspec
4
4
 
5
5
  gem 'liquid-development'
6
6
  gem 'liquid-ext'
7
+ gem 'ruby-debug'
8
+ gem 'z-http-request', path: '../z-http-request'
data/README.md CHANGED
@@ -4,6 +4,7 @@ ZMachine is a pure JRuby multi-threaded event loop based on java.nio.Selector
4
4
  and a hashed wheel timer implementation. ZMachine supports TCP and ZeroMQ
5
5
  sockets natively and exposes an EventMachine compatible API where possible.
6
6
 
7
+ [![Gem Version](https://badge.fury.io/rb/zmachine.png)](http://badge.fury.io/rb/zmachine)
7
8
  [![Build Status](https://travis-ci.org/liquidm/zmachine.png)](https://travis-ci.org/liquidm/zmachine)
8
9
  [![Code Climate](https://codeclimate.com/github/liquidm/zmachine.png)](https://codeclimate.com/github/liquidm/zmachine)
9
10
  [![Dependency Status](https://gemnasium.com/liquidm/zmachine.png)](https://gemnasium.com/liquidm/zmachine)
File without changes
File without changes
data/lib/zmachine.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'zmachine/jeromq-0.3.0-SNAPSHOT.jar'
1
+ require 'zmachine/jeromq-0.3.2-SNAPSHOT.jar'
2
2
  java_import org.zeromq.ZContext
3
3
 
4
4
  require 'liquid/boot'
@@ -10,9 +10,6 @@ require 'zmachine/timers'
10
10
 
11
11
  module ZMachine
12
12
  class ConnectionError < RuntimeError; end
13
- class ConnectionNotBound < RuntimeError; end
14
- class UnknownTimerFired < RuntimeError; end
15
- class Unsupported < RuntimeError; end
16
13
 
17
14
  class << self
18
15
  attr_accessor :logger
@@ -27,15 +24,10 @@ module ZMachine
27
24
  Thread.current[:reactor] ||= Reactor.new
28
25
  end
29
26
 
30
- # TODO: move to ZMQChannel
31
27
  def self.context
32
28
  Thread.current[:context] ||= ZContext.new
33
29
  end
34
30
 
35
- def self._not_implemented
36
- raise RuntimeError.new("API call not implemented! #{caller[0]}")
37
- end
38
-
39
31
  def self.add_periodic_timer(*args, &block)
40
32
  interval = args.shift
41
33
  callback = args.shift || block
@@ -50,66 +42,22 @@ module ZMachine
50
42
  reactor.add_timer(*args, &block)
51
43
  end
52
44
 
53
- def self.attach(io, handler = nil, *args, &blk)
54
- _not_implemented
55
- end
56
-
57
- def self.bind_connect(bind_addr, bind_port, server, port = nil, handler = nil, *args)
58
- _not_implemented
59
- end
60
-
61
- def self.Callback(object = nil, method = nil, &blk)
62
- _not_implemented
63
- end
64
-
65
45
  def self.cancel_timer(timer_or_sig)
66
46
  timer_or_sig.cancel # we do not support signatures
67
47
  end
68
48
 
69
- def self.close_connection(connection)
70
- reactor.close_connection(connection)
49
+ def self.close_connection(connection, reason = nil)
50
+ reactor.close_connection(connection, reason)
71
51
  end
72
52
 
73
53
  def self.connect(server, port_or_type=nil, handler=nil, *args, &block)
74
54
  reactor.connect(server, port_or_type, handler, *args, &block)
75
55
  end
76
56
 
77
- def self.connect_unix_domain(socketname, *args, &blk)
78
- _not_implemented
79
- end
80
-
81
57
  def self.connection_count
82
58
  reactor.connections.size
83
59
  end
84
60
 
85
- def self.defer(op = nil, callback = nil, &blk)
86
- _not_implemented
87
- end
88
-
89
- def self.defers_finished?
90
- _not_implemented
91
- end
92
-
93
- def self.disable_proxy(from)
94
- _not_implemented
95
- end
96
-
97
- def self.enable_proxy(from, to, bufsize = 0, length = 0)
98
- _not_implemented
99
- end
100
-
101
- def self.error_handler(callback = nil, &block)
102
- _not_implemented
103
- end
104
-
105
- def self.fork_reactor(&block)
106
- _not_implemented
107
- end
108
-
109
- def self.get_max_timers
110
- _not_implemented
111
- end
112
-
113
61
  def self.heartbeat_interval
114
62
  reactor.heartbeat_interval
115
63
  end
@@ -122,22 +70,10 @@ module ZMachine
122
70
  reactor.next_tick(callback, &block)
123
71
  end
124
72
 
125
- def self.open_datagram_socket(address, port, handler = nil, *args)
126
- _not_implemented
127
- end
128
-
129
- def self.popen(cmd, handler = nil, *args)
130
- _not_implemented
131
- end
132
-
133
73
  def self.reactor_running?
134
74
  reactor.running?
135
75
  end
136
76
 
137
- def self.reactor_thread?
138
- _not_implemented
139
- end
140
-
141
77
  def self.reconnect(server, port, handler)
142
78
  reactor.reconnect(server, port, handler)
143
79
  end
@@ -154,38 +90,10 @@ module ZMachine
154
90
  run(&pr)
155
91
  end
156
92
 
157
- def self.schedule(*a, &b)
158
- _not_implemented
159
- end
160
-
161
- def self.set_descriptor_table_size(n_descriptors = nil)
162
- _not_implemented
163
- end
164
-
165
- def self.set_effective_user(username)
166
- _not_implemented
167
- end
168
-
169
- def self.set_max_timers(ct)
170
- _not_implemented
171
- end
172
-
173
- def self.set_quantum(mills)
174
- _not_implemented
175
- end
176
-
177
- def self.spawn(&block)
178
- _not_implemented
179
- end
180
-
181
93
  def self.start_server(server, port_or_type=nil, handler=nil, *args, &block)
182
94
  reactor.bind(server, port_or_type, handler, *args, &block)
183
95
  end
184
96
 
185
- def self.start_unix_domain_server(filename, *args, &block)
186
- _not_implemented
187
- end
188
-
189
97
  def self.stop_event_loop
190
98
  reactor.stop_event_loop
191
99
  end
@@ -198,26 +106,6 @@ module ZMachine
198
106
  Reactor.terminate_all_reactors
199
107
  end
200
108
 
201
- def self.system(cmd, *args, &cb)
202
- _not_implemented
203
- end
204
-
205
- def self.tick_loop(*a, &b)
206
- _not_implemented
207
- end
208
-
209
- def self.watch(io, handler = nil, *args, &blk)
210
- _not_implemented
211
- end
212
-
213
- def self.watch_file(filename, handler = nil, *args)
214
- _not_implemented
215
- end
216
-
217
- def self.watch_process(pid, handler = nil, *args)
218
- _not_implemented
219
- end
220
-
221
109
  end
222
110
 
223
111
  if ENV['DEBUG']
@@ -4,7 +4,7 @@ module ZMachine
4
4
  attr_reader :args
5
5
  attr_reader :callback
6
6
 
7
- def initialize(channel, klass, *args )
7
+ def initialize(channel, klass, *args)
8
8
  @klass, @args, @callback = klass, args[0...-1], args.last
9
9
  @channel = channel
10
10
  end
@@ -4,11 +4,12 @@ module ZMachine
4
4
  class Channel
5
5
 
6
6
  attr_accessor :socket
7
+ attr_accessor :raw
7
8
 
8
9
  def initialize
9
10
  @inbound_buffer = ByteBuffer.allocate(1024 * 1024)
10
11
  @outbound_queue = []
11
- @close_scheduled = false
12
+ @raw = false
12
13
  end
13
14
 
14
15
  # methods that need to be implemented in sub classes:
@@ -31,14 +32,46 @@ module ZMachine
31
32
  !@outbound_queue.empty?
32
33
  end
33
34
 
34
- def close(after_writing = false)
35
- ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self, after_writing: after_writing) if ZMachine.debug
35
+ def send_data(data)
36
+ ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self) if ZMachine.debug
37
+ raise RuntimeError.new("send_data called after close") if @closed_callback
38
+ return unless data
39
+ buffer = ByteBuffer.wrap(data)
40
+ if buffer.has_remaining
41
+ @outbound_queue << buffer
42
+ end
43
+ end
44
+
45
+ def write_outbound_data
46
+ ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self, can_send: can_send?) if ZMachine.debug
47
+ while can_send?
48
+ buffer = @outbound_queue.first
49
+ @socket.write(buffer) if buffer.has_remaining
50
+ # Did we consume the whole outbound buffer? If yes,
51
+ # pop it off and keep looping. If no, the outbound network
52
+ # buffers are full, so break out of here.
53
+ break if buffer.has_remaining
54
+ @outbound_queue.shift
55
+ end
56
+ maybe_close_with_callback
57
+ end
58
+
59
+ def close(after_writing = false, &block)
60
+ return true if closed?
61
+ ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self, after_writing: after_writing, caller: caller[0].inspect) if ZMachine.debug
36
62
  @close_scheduled = true
63
+ @closed_callback = block if block
37
64
  @outbound_queue.clear unless after_writing
65
+ maybe_close_with_callback
38
66
  end
39
67
 
40
- def close_after_writing
41
- close(true)
68
+ def maybe_close_with_callback
69
+ ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self, can_send: can_send?) if ZMachine.debug
70
+ return false if can_send?
71
+ return true unless @close_scheduled
72
+ close!
73
+ @closed_callback.call if @closed_callback
74
+ return true
42
75
  end
43
76
 
44
77
  end
@@ -2,19 +2,19 @@ java_import java.io.IOException
2
2
  java_import java.nio.ByteBuffer
3
3
  java_import java.nio.channels.SelectionKey
4
4
 
5
+ require 'zmachine'
6
+
5
7
  module ZMachine
6
8
  class Connection
7
9
 
8
10
  extend Forwardable
9
11
 
10
12
  attr_accessor :channel
11
- attr_accessor :args
12
- attr_accessor :block
13
13
 
14
- def self.new(*args, &block)
14
+ def self.new(*args)
15
15
  allocate.instance_eval do
16
- initialize(*args, &block)
17
- @args, @block = args, block
16
+ initialize(*args)
17
+ @args = args
18
18
  post_init
19
19
  self
20
20
  end
@@ -22,48 +22,52 @@ module ZMachine
22
22
 
23
23
  # channel type dispatch
24
24
 
25
- def bind(address, port_or_type)
25
+ def bind(address, port_or_type, &block)
26
26
  ZMachine.logger.debug("zmachine:connection:#{__method__}", connection: self) if ZMachine.debug
27
- if address =~ %r{\w+://}
28
- @channel = ZMQChannel.new(port_or_type)
29
- @channel.bind(address)
30
- else
31
- @channel = TCPChannel.new
32
- @channel.bind(address, port_or_type)
33
- end
27
+ klass = (address =~ %r{\w+://}) ? ZMQChannel : TCPChannel
28
+ @channel = klass.new
29
+ @channel.bind(address, port_or_type)
30
+ @block = block
34
31
  self
35
32
  end
36
33
 
37
- def connect(address, port_or_type)
34
+ def connect(address, port_or_type, &block)
38
35
  ZMachine.logger.debug("zmachine:connection:#{__method__}", connection: self) if ZMachine.debug
39
- if address.nil? or address =~ %r{\w+://}
40
- @channel = ZMQChannel.new(port_or_type)
41
- @channel.connect(address) if address
42
- else
43
- @channel = TCPChannel.new
44
- @channel.connect(address, port_or_type)
45
- end
46
- if @connect_timeout
47
- @timer = ZMachine.add_timer(@connect_timeout) do
48
- ZMachine.reactor.close_connection(self)
49
- end
50
- end
36
+ klass = (address.nil? || address =~ %r{\w+://}) ? ZMQChannel : TCPChannel
37
+ @channel = klass.new
38
+ @channel.connect(address, port_or_type) if address
39
+ yield self if block_given?
40
+ renew_timer
51
41
  self
52
42
  end
53
43
 
44
+ # callbacks
45
+ def connection_accepted
46
+ end
47
+
48
+ def connection_completed
49
+ end
50
+
51
+ def post_init
52
+ end
53
+
54
+ def receive_data(data)
55
+ end
56
+
57
+ def unbind
58
+ end
59
+
60
+ # EventMachine Connection API
61
+
54
62
  def_delegator :@channel, :bound?
55
63
  def_delegator :@channel, :closed?
56
64
  def_delegator :@channel, :connected?
57
65
  def_delegator :@channel, :connection_pending?
58
66
 
59
- # EventMachine Connection API
60
-
61
- def _not_implemented
62
- raise RuntimeError.new("API call not implemented! #{caller[0]}")
63
- end
64
-
65
67
  def close_connection(after_writing = false)
66
- @channel.close(after_writing)
68
+ @channel.close(after_writing) do
69
+ ZMachine.close_connection(self)
70
+ end
67
71
  end
68
72
 
69
73
  alias :close :close_connection
@@ -84,160 +88,51 @@ module ZMachine
84
88
 
85
89
  alias :set_comm_inactivity_timeout :comm_inactivity_timeout=
86
90
 
87
- def connection_accepted(channel)
88
- end
89
-
90
- def connection_completed
91
- end
92
-
93
- def detach
94
- _not_implemented
95
- end
96
-
97
- def error?
98
- _not_implemented
99
- end
100
-
101
91
  def get_idle_time
102
92
  (System.nano_time - @last_activity) / 1_000_000
103
93
  end
104
94
 
105
- def get_peer_cert
106
- _not_implemented
107
- end
108
-
109
95
  def get_peername
110
96
  if peer = @channel.peer
111
97
  ::Socket.pack_sockaddr_in(*peer)
112
98
  end
113
99
  end
114
100
 
115
- def get_pid
116
- _not_implemented
117
- end
118
-
119
- def get_proxied_bytes
120
- _not_implemented
121
- end
122
-
123
- def get_sock_opt(level, option)
124
- _not_implemented
125
- end
126
-
127
- def get_sockname
128
- _not_implemented
129
- end
130
-
131
- def get_status
132
- _not_implemented
133
- end
134
-
135
- def notify_readable=(mode)
136
- _not_implemented
137
- end
138
-
139
101
  def notify_readable?
140
102
  true
141
103
  end
142
104
 
143
- def notify_writable=(mode)
144
- _not_implemented
145
- end
146
-
147
105
  def notify_writable?
148
106
  @channel.can_send?
149
107
  end
150
108
 
151
- def pause
152
- _not_implemented
153
- end
154
-
155
- def paused?
156
- _not_implemented
157
- end
158
-
159
109
  def pending_connect_timeout=(value)
160
110
  @connect_timeout = value
161
111
  end
162
112
 
163
113
  alias :set_pending_connect_timeout :pending_connect_timeout=
164
114
 
165
- def post_init
166
- end
167
-
168
- def proxy_completed
169
- _not_implemented
170
- end
171
-
172
- def proxy_incoming_to(conn, bufsize = 0)
173
- _not_implemented
174
- end
175
-
176
- def proxy_target_unbound
177
- _not_implemented
178
- end
179
-
180
- def receive_data(data)
181
- end
182
-
183
115
  def reconnect(server, port_or_type)
184
116
  ZMachine.reconnect(server, port_or_type, self)
185
117
  end
186
118
 
187
- def resume
188
- _not_implemented
189
- end
190
-
191
119
  def send_data(data)
192
120
  ZMachine.logger.debug("zmachine:connection:#{__method__}", connection: self) if ZMachine.debug
121
+ data = data.to_java_bytes if data.is_a?(String) # EM compat
193
122
  @channel.send_data(data)
194
123
  update_events
195
124
  end
196
125
 
197
- def send_datagram(data, recipient_address, recipient_port)
198
- _not_implemented
199
- end
200
-
201
- def send_file_data(filename)
202
- _not_implemented
203
- end
204
-
205
- def set_sock_opt(level, optname, optval)
206
- _not_implemented
207
- end
208
-
209
- def ssl_handshake_completed
210
- _not_implemented
211
- end
212
-
213
- def ssl_verify_peer(cert)
214
- _not_implemented
215
- end
216
-
217
- def start_tls(args = {})
218
- _not_implemented
219
- end
220
-
221
- def stop_proxying
222
- _not_implemented
223
- end
224
-
225
- def stream_file_data(filename, args = {})
226
- _not_implemented
227
- end
228
-
229
- def unbind
230
- end
231
-
232
126
  # triggers
233
127
 
234
128
  def acceptable!
235
129
  client = @channel.accept
236
- connection_accepted(client) if client.connected?
237
130
  ZMachine.logger.debug("zmachine:connection:#{__method__}", connection: self, client: client) if ZMachine.debug
238
- self.class.new(*@args, &@block).tap do |instance|
239
- instance.channel = client
240
- end
131
+ connection = self.class.new(*@args)
132
+ connection.channel = client
133
+ @block.call(connection) if @block
134
+ connection.connection_accepted
135
+ connection
241
136
  end
242
137
 
243
138
  def connectable!
@@ -273,8 +168,13 @@ module ZMachine
273
168
  @channel_key ||= @channel.selectable_fd.register(selector, current_events, self)
274
169
  end
275
170
 
171
+ def valid?
172
+ @channel_key &&
173
+ @channel_key.valid?
174
+ end
175
+
276
176
  def update_events
277
- return unless @channel_key
177
+ return unless valid?
278
178
  ZMachine.logger.debug("zmachine:connection:#{__method__}", connection: self) if ZMachine.debug
279
179
  @channel_key.interest_ops(current_events)
280
180
  end
@@ -301,7 +201,7 @@ module ZMachine
301
201
  end
302
202
 
303
203
  def process_events
304
- return unless @channel_key
204
+ return unless valid?
305
205
  ZMachine.logger.debug("zmachine:connection:#{__method__}", connection: self) if ZMachine.debug
306
206
  if @channel_key.connectable?
307
207
  connectable!
@@ -311,6 +211,9 @@ module ZMachine
311
211
  writable! if @channel_key.writable?
312
212
  readable! if @channel_key.readable?
313
213
  end
214
+ rescue Java::JavaNioChannels::CancelledKeyException
215
+ # channel may have been closed by write handler. ignore exception and
216
+ # wait for cleanup
314
217
  end
315
218
 
316
219
  def mark_active!
@@ -320,8 +223,10 @@ module ZMachine
320
223
 
321
224
  def renew_timer
322
225
  @timer.cancel if @timer
323
- @timer = ZMachine.add_timer(@inactivity_timeout) do
324
- ZMachine.reactor.close_connection(self)
226
+ if connection_pending? && @connect_timeout
227
+ @timer = ZMachine.add_timer(@connect_timeout) { ZMachine.close_connection(self, Errno::ETIMEDOUT) }
228
+ elsif @inactivity_timeout
229
+ @timer = ZMachine.add_timer(@inactivity_timeout) { ZMachine.close_connection(self, Errno::ETIMEDOUT) }
325
230
  end
326
231
  end
327
232