zmachine 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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