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