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
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
YTVjMmVhODBmZjU3NTA4MmQ4MDBkOTRlZmE4OTMzNjA4ZWM4NjgwZmM2ZWVm
|
10
|
-
YWRlYTM0Mzg0NTlhYzYwYzgyOWIwZmMzMzcyZTEzNDU2MjFjNGQyYjliMmFh
|
11
|
-
YzJmMWYwZWU5MTdjYmJmZjk0ZDgwZmQ2YzZhOGQxZjdmNTNmZTA=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
OGRmZjQ3YWI2NTllOWI2MzQzODVhOGVjYzAzMjI5ZDgwNTQ3NTU4MjM4NjVk
|
14
|
-
Yjk5MzU0MmJmM2QwNjUxYzllZTZlMWIyMTVlMGM5OTE1YWZmOTA1OTIzZGRm
|
15
|
-
Y2VjYjI4N2JkNzRjOTcxNDgzMTA0NTU1YmM0ZWYwOWFmMzQ1M2I=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 47d9ec01f1c2e637dd7b8bd49e8df5ab2f6c9ec2
|
4
|
+
data.tar.gz: 9fea025ea55da57597fc31ecb8fc17055afcca9b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3c2709abad08e6337fd8d930e71d7b1d970cd7add92ab40eee2574500de96cdad5bc8c09acb375dd4cd16cb67aa8f992f64db7b05a88eff0f8d92e6ced62c115
|
7
|
+
data.tar.gz: b0ad9bfe37a314ec281d61c1ff9eb1221153d1f199740ebde8acc9a9a755bbd5e7d25bdf66d3e18410ea2677f74e671093cdd177b0871ac346b42547a1378a44
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.6
|
data/Gemfile
CHANGED
@@ -3,7 +3,10 @@ source 'https://rubygems.org'
|
|
3
3
|
# Specify your gem's dependencies in zmachine.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
+
gem 'awesome_print'
|
7
|
+
gem 'brice'
|
8
|
+
gem 'fuubar'
|
6
9
|
gem 'ruby-debug'
|
7
10
|
gem 'ruby-debug-ide'
|
8
|
-
gem '
|
9
|
-
gem '
|
11
|
+
gem 'madvertise-ext'
|
12
|
+
gem 'simplecov'
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Zmachine
|
2
2
|
|
3
|
-
|
3
|
+
ZMachine is a JRuby Event Reactor based on java.nio.Selector and a HashedWheel
|
4
|
+
timer implementation. ZMachine supports TCP and ZeroMQ sockets natively and
|
5
|
+
exposes an EventMachine compatible API where possible.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
data/Rakefile
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
bundle exec jruby \
|
4
|
+
--server \
|
5
|
+
-J-Djruby.compile.fastest=true \
|
6
|
+
-J-Djruby.compile.frameless=true \
|
7
|
+
-J-Djruby.compile.positionless=true \
|
8
|
+
-J-Djruby.compile.threadless=true \
|
9
|
+
-J-Djruby.compile.fastops=true \
|
10
|
+
-J-Djruby.compile.fastcase=true \
|
11
|
+
-J-Djruby.compile.invokedynamic=true \
|
12
|
+
"$@" | pv -l >/dev/null
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'zmachine/tcp_channel'
|
4
|
+
|
5
|
+
include ZMachine
|
6
|
+
|
7
|
+
server = TCPChannel.new
|
8
|
+
server.bind("0.0.0.0", 12345)
|
9
|
+
|
10
|
+
client = TCPChannel.new
|
11
|
+
client.connect("0.0.0.0", 12345)
|
12
|
+
|
13
|
+
channel = server.accept
|
14
|
+
client.finish_connecting
|
15
|
+
|
16
|
+
data = ("x" * 2048).freeze
|
17
|
+
|
18
|
+
loop do
|
19
|
+
client.send_data(data)
|
20
|
+
client.write_outbound_data
|
21
|
+
received = channel.read_inbound_data
|
22
|
+
puts received
|
23
|
+
channel.send_data(received)
|
24
|
+
channel.write_outbound_data
|
25
|
+
client.read_inbound_data
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'zmachine/zmq_channel'
|
4
|
+
|
5
|
+
include ZMachine
|
6
|
+
|
7
|
+
server = ZMQChannel.new(ZMQ::REP)
|
8
|
+
#server.bind("tcp://0.0.0.0:12345")
|
9
|
+
server.bind("inproc://x")
|
10
|
+
|
11
|
+
client = ZMQChannel.new(ZMQ::REQ)
|
12
|
+
#client.connect("tcp://0.0.0.0:12345")
|
13
|
+
client.connect("inproc://x")
|
14
|
+
|
15
|
+
data = ("x" * 2048).to_java_bytes
|
16
|
+
|
17
|
+
loop do
|
18
|
+
client.send_data([data])
|
19
|
+
received = server.read_inbound_data.first
|
20
|
+
puts received
|
21
|
+
server.send_data([data])
|
22
|
+
client.read_inbound_data
|
23
|
+
end
|
data/echo_client.rb
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'bundler/setup'
|
5
|
+
require 'madvertise/boot'
|
5
6
|
require 'zmachine'
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
$log.level = :debug
|
9
|
+
ZMachine.logger = $log
|
10
|
+
ZMachine.debug = true
|
10
11
|
|
11
12
|
#set_trace_func proc { |event, file, line, id, binding, classname|
|
12
13
|
# printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
|
@@ -44,28 +45,9 @@ class TCPEcho < ZMachine::Connection
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
ZMachine.run
|
48
|
-
ZMachine.connect("tcp://127.0.0.1:10000", ZMQ::ROUTER, ZMQEcho) do |handler|
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
#ctx = ZContext.new
|
55
|
-
#socket = ctx.create_socket(ZMQ::ROUTER)
|
56
|
-
#socket.connect("tcp://127.0.0.1:10000")
|
57
|
-
#socket.identity = "client".to_java_bytes
|
58
|
-
|
59
|
-
#sleep(1)
|
60
|
-
|
61
|
-
#loop do
|
62
|
-
# msg = ZMsg.new_string_msg($i.to_s)
|
63
|
-
# msg.wrap(ZFrame.new("server"))
|
64
|
-
# msg.java_send(:send, [org.zeromq.ZMQ::Socket], socket)
|
65
|
-
# $i += 1
|
66
|
-
# break if $i > 100
|
67
|
-
# #puts ZMsg.recvMsg(socket).inspect
|
68
|
-
#end
|
69
|
-
|
70
|
-
#socket.close
|
71
|
-
#ctx.destroy
|
48
|
+
ZMachine.run do
|
49
|
+
#ZMachine.connect("tcp://127.0.0.1:10000", ZMQ::ROUTER, ZMQEcho) do |handler|
|
50
|
+
# handler.channel.identity = "client"
|
51
|
+
#end
|
52
|
+
ZMachine.connect("127.0.0.1", 10000, TCPEcho)
|
53
|
+
end
|
data/echo_server.rb
CHANGED
@@ -2,40 +2,33 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'bundler/setup'
|
5
|
+
require 'madvertise/boot'
|
5
6
|
require 'zmachine'
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
$log.level = :debug
|
9
|
+
ZMachine.logger = $log
|
10
|
+
ZMachine.debug = true
|
9
11
|
|
10
12
|
#set_trace_func proc { |event, file, line, id, binding, classname|
|
11
13
|
# printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
|
12
14
|
#}
|
13
15
|
|
14
|
-
class
|
15
|
-
def receive_data(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
class ZMQEchoServer < ZMachine::Connection
|
17
|
+
def receive_data(buffer)
|
18
|
+
send_data(buffer)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class TCPEchoServer < ZMachine::Connection
|
23
|
+
def receive_data(buffer)
|
24
|
+
send_data(buffer)
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
24
28
|
ZMachine.run do
|
25
|
-
ZMachine.start_server("tcp://*:10000", ZMQ::ROUTER,
|
29
|
+
ZMachine.start_server("tcp://*:10000", ZMQ::ROUTER, ZMQEchoServer) do |handler|
|
26
30
|
handler.channel.identity = "server"
|
27
31
|
end
|
28
|
-
#ZMachine.start_server("0.0.0.0", 10000,
|
32
|
+
#ZMachine.start_server("0.0.0.0", 10000, TCPEchoServer)
|
29
33
|
puts "machine running"
|
30
34
|
end
|
31
|
-
|
32
|
-
#ctx = ZContext.new
|
33
|
-
#socket = ctx.create_socket(ZMQ::ROUTER)
|
34
|
-
#socket.bind("tcp://127.0.0.1:10000")
|
35
|
-
#socket.identity = "server".to_java_bytes
|
36
|
-
|
37
|
-
#loop do
|
38
|
-
# puts socket.events.inspect
|
39
|
-
# msg = ZMsg.recvMsg(socket)
|
40
|
-
# puts "recv(#{msg.to_a.map {|f| String.from_java_bytes(f.data) }.inspect})"
|
41
|
-
#end
|
data/lib/zmachine.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
require '
|
1
|
+
require 'zmachine/jeromq-0.3.0-SNAPSHOT.jar'
|
2
|
+
java_import org.zeromq.ZContext
|
3
|
+
|
4
|
+
require 'madvertise/boot'
|
2
5
|
|
3
6
|
require 'zmachine/connection'
|
4
7
|
require 'zmachine/deferrable'
|
5
8
|
require 'zmachine/reactor'
|
6
9
|
require 'zmachine/timers'
|
7
10
|
|
8
|
-
|
9
11
|
module ZMachine
|
10
12
|
class ConnectionError < RuntimeError; end
|
11
13
|
class ConnectionNotBound < RuntimeError; end
|
@@ -25,27 +27,11 @@ module ZMachine
|
|
25
27
|
Thread.current[:reactor] ||= Reactor.new
|
26
28
|
end
|
27
29
|
|
30
|
+
# TODO: move to ZMQChannel
|
28
31
|
def self.context
|
29
32
|
Thread.current[:context] ||= ZContext.new
|
30
33
|
end
|
31
34
|
|
32
|
-
class << self
|
33
|
-
extend Forwardable
|
34
|
-
def_delegator :reactor, :add_shutdown_hook
|
35
|
-
def_delegator :reactor, :add_timer
|
36
|
-
def_delegator :reactor, :cancel_timer
|
37
|
-
def_delegator :reactor, :connect
|
38
|
-
def_delegator :reactor, :connection_count
|
39
|
-
def_delegator :reactor, :error_handler
|
40
|
-
def_delegator :reactor, :next_tick
|
41
|
-
def_delegator :reactor, :run
|
42
|
-
def_delegator :reactor, :reactor_running?
|
43
|
-
def_delegator :reactor, :reconnect
|
44
|
-
def_delegator :reactor, :start_server
|
45
|
-
def_delegator :reactor, :stop_event_loop
|
46
|
-
def_delegator :reactor, :stop_server
|
47
|
-
end
|
48
|
-
|
49
35
|
def self._not_implemented
|
50
36
|
raise RuntimeError.new("API call not implemented! #{caller[0]}")
|
51
37
|
end
|
@@ -56,6 +42,14 @@ module ZMachine
|
|
56
42
|
PeriodicTimer.new(interval, callback)
|
57
43
|
end
|
58
44
|
|
45
|
+
def self.add_shutdown_hook(&block)
|
46
|
+
reactor.add_shutdown_hook(&block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.add_timer(*args, &block)
|
50
|
+
reactor.add_timer(*args, &block)
|
51
|
+
end
|
52
|
+
|
59
53
|
def self.attach(io, handler = nil, *args, &blk)
|
60
54
|
_not_implemented
|
61
55
|
end
|
@@ -68,10 +62,26 @@ module ZMachine
|
|
68
62
|
_not_implemented
|
69
63
|
end
|
70
64
|
|
65
|
+
def self.cancel_timer(timer_or_sig)
|
66
|
+
timer_or_sig.cancel # we do not support signatures
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.close_connection(connection)
|
70
|
+
reactor.close_connection(connection)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.connect(server, port_or_type=nil, handler=nil, *args, &block)
|
74
|
+
reactor.connect(server, port_or_type, handler, *args, &block)
|
75
|
+
end
|
76
|
+
|
71
77
|
def self.connect_unix_domain(socketname, *args, &blk)
|
72
78
|
_not_implemented
|
73
79
|
end
|
74
80
|
|
81
|
+
def self.connection_count
|
82
|
+
reactor.connections.size
|
83
|
+
end
|
84
|
+
|
75
85
|
def self.defer(op = nil, callback = nil, &blk)
|
76
86
|
_not_implemented
|
77
87
|
end
|
@@ -88,6 +98,10 @@ module ZMachine
|
|
88
98
|
_not_implemented
|
89
99
|
end
|
90
100
|
|
101
|
+
def self.error_handler(callback = nil, &block)
|
102
|
+
_not_implemented
|
103
|
+
end
|
104
|
+
|
91
105
|
def self.fork_reactor(&block)
|
92
106
|
_not_implemented
|
93
107
|
end
|
@@ -97,11 +111,15 @@ module ZMachine
|
|
97
111
|
end
|
98
112
|
|
99
113
|
def self.heartbeat_interval
|
100
|
-
|
114
|
+
reactor.heartbeat_interval
|
101
115
|
end
|
102
116
|
|
103
117
|
def self.heartbeat_interval=(time)
|
104
|
-
|
118
|
+
reactor.heartbeat_interval = time
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.next_tick(callback=nil, &block)
|
122
|
+
reactor.next_tick(callback, &block)
|
105
123
|
end
|
106
124
|
|
107
125
|
def self.open_datagram_socket(address, port, handler = nil, *args)
|
@@ -112,12 +130,20 @@ module ZMachine
|
|
112
130
|
_not_implemented
|
113
131
|
end
|
114
132
|
|
133
|
+
def self.reactor_running?
|
134
|
+
reactor.running?
|
135
|
+
end
|
136
|
+
|
115
137
|
def self.reactor_thread?
|
116
138
|
_not_implemented
|
117
139
|
end
|
118
140
|
|
119
|
-
def self.
|
120
|
-
|
141
|
+
def self.reconnect(server, port, handler)
|
142
|
+
reactor.reconnect(server, port, handler)
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.run(callback=nil, shutdown_hook=nil, &block)
|
146
|
+
reactor.run(callback, shutdown_hook, &block)
|
121
147
|
end
|
122
148
|
|
123
149
|
def self.run_block(&block)
|
@@ -152,10 +178,26 @@ module ZMachine
|
|
152
178
|
_not_implemented
|
153
179
|
end
|
154
180
|
|
181
|
+
def self.start_server(server, port_or_type=nil, handler=nil, *args, &block)
|
182
|
+
reactor.bind(server, port_or_type, handler, *args, &block)
|
183
|
+
end
|
184
|
+
|
155
185
|
def self.start_unix_domain_server(filename, *args, &block)
|
156
186
|
_not_implemented
|
157
187
|
end
|
158
188
|
|
189
|
+
def self.stop_event_loop
|
190
|
+
reactor.stop_event_loop
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.stop_server(signature)
|
194
|
+
reactor.stop_server(signature)
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.stop
|
198
|
+
Reactor.terminate_all_reactors
|
199
|
+
end
|
200
|
+
|
159
201
|
def self.system(cmd, *args, &cb)
|
160
202
|
_not_implemented
|
161
203
|
end
|
@@ -177,3 +219,9 @@ module ZMachine
|
|
177
219
|
end
|
178
220
|
|
179
221
|
end
|
222
|
+
|
223
|
+
if ENV['DEBUG']
|
224
|
+
$log.level = :debug
|
225
|
+
ZMachine.logger = $log
|
226
|
+
ZMachine.debug = true
|
227
|
+
end
|
data/lib/zmachine/channel.rb
CHANGED
@@ -1,42 +1,44 @@
|
|
1
|
+
java_import java.nio.ByteBuffer
|
2
|
+
|
1
3
|
module ZMachine
|
2
4
|
class Channel
|
3
5
|
|
4
6
|
attr_accessor :socket
|
5
|
-
attr_reader :selector
|
6
|
-
attr_accessor :handler
|
7
|
-
attr_accessor :reactor
|
8
|
-
|
9
|
-
attr_reader :comm_inactivity_timeout
|
10
|
-
attr_reader :last_comm_activity
|
11
7
|
|
12
|
-
def initialize
|
13
|
-
@
|
8
|
+
def initialize
|
9
|
+
@inbound_buffer = ByteBuffer.allocate(1024 * 1024)
|
14
10
|
@outbound_queue = []
|
15
|
-
@
|
16
|
-
@timedout = false
|
17
|
-
mark_active!
|
18
|
-
end
|
19
|
-
|
20
|
-
# assigned in seconds!!
|
21
|
-
def comm_inactivity_timeout=(value)
|
22
|
-
# we are in nanos
|
23
|
-
@comm_inactivity_timeout = value * 1000_000_000
|
24
|
-
end
|
25
|
-
|
26
|
-
def mark_active!
|
27
|
-
@last_comm_activity = System.nano_time
|
11
|
+
@close_scheduled = false
|
28
12
|
end
|
29
13
|
|
30
|
-
|
31
|
-
|
14
|
+
# methods that need to be implemented in sub classes:
|
15
|
+
#
|
16
|
+
# selectable_fd
|
17
|
+
# bind(address, port = nil)
|
18
|
+
# bound?
|
19
|
+
# accept
|
20
|
+
# connect(address, port = nil)
|
21
|
+
# connection_pending?
|
22
|
+
# finish_connecting
|
23
|
+
# connected?
|
24
|
+
# read_inbound_data
|
25
|
+
# send_data(data)
|
26
|
+
# closed?
|
27
|
+
# peer
|
28
|
+
# write_outbound_data
|
29
|
+
|
30
|
+
def can_send?
|
31
|
+
!@outbound_queue.empty?
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
|
34
|
+
def close(after_writing = false)
|
35
|
+
ZMachine.logger.debug("zmachine:channel:#{__method__}", channel: self, after_writing: after_writing) if ZMachine.debug
|
36
|
+
@close_scheduled = true
|
37
|
+
@outbound_queue.clear unless after_writing
|
36
38
|
end
|
37
39
|
|
38
|
-
def
|
39
|
-
|
40
|
+
def close_after_writing
|
41
|
+
close(true)
|
40
42
|
end
|
41
43
|
|
42
44
|
end
|