rbuv 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,102 @@
1
+ #include "rbuv_tcp.h"
2
+
3
+ struct rbuv_tcp_s {
4
+ uv_tcp_t *uv_handle;
5
+ VALUE cb_on_close;
6
+ VALUE cb_on_connection;
7
+ VALUE cb_on_read;
8
+ };
9
+
10
+ VALUE cRbuvTcp;
11
+
12
+ /* Allocator/deallocator */
13
+ static VALUE rbuv_tcp_alloc(VALUE klass);
14
+ static void rbuv_tcp_mark(rbuv_tcp_t *rbuv_tcp);
15
+ static void rbuv_tcp_free(rbuv_tcp_t *rbuv_tcp);
16
+
17
+ /* Methods */
18
+ /*
19
+ uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in)
20
+ uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6)
21
+ uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, struct sockaddr_in address, uv_connect❯
22
+ uv_tcp_connect6(uv_connect_t* req, uv_tcp_t* handle, struct sockaddr_in6 address, uv_conne❯
23
+ uv_tcp_getpeername(uv_tcp_t* handle, struct sockaddr* name, int* namelen)
24
+ uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, int* namelen)
25
+ uv_tcp_init(uv_loop_t*, uv_tcp_t* handle)
26
+ uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay)
27
+ uv_tcp_nodelay(uv_tcp_t* handle, int enable)
28
+ uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock)
29
+ uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable)
30
+ */
31
+ static VALUE rbuv_tcp_bind(VALUE self, VALUE ip, VALUE port);
32
+ //static VALUE rbuv_tcp_bind6(VALUE self, VALUE ip, VALUE port);
33
+
34
+ void Init_rbuv_tcp() {
35
+ cRbuvTcp = rb_define_class_under(mRbuv, "Tcp", cRbuvStream);
36
+ rb_define_alloc_func(cRbuvTcp, rbuv_tcp_alloc);
37
+
38
+ rb_define_method(cRbuvTcp, "bind", rbuv_tcp_bind, 2);
39
+ //rb_define_method(cRbuvTcp, "bind6", rbuv_tcp_bind6, 2);
40
+ }
41
+
42
+ VALUE rbuv_tcp_alloc(VALUE klass) {
43
+ rbuv_tcp_t *rbuv_tcp;
44
+ VALUE tcp;
45
+
46
+ rbuv_tcp = malloc(sizeof(*rbuv_tcp));
47
+ rbuv_tcp->uv_handle = malloc(sizeof(*rbuv_tcp->uv_handle));
48
+ uv_tcp_init(uv_default_loop(), rbuv_tcp->uv_handle);
49
+ rbuv_tcp->cb_on_close = Qnil;
50
+ rbuv_tcp->cb_on_connection = Qnil;
51
+ rbuv_tcp->cb_on_read = Qnil;
52
+
53
+ tcp = Data_Wrap_Struct(klass, rbuv_tcp_mark, rbuv_tcp_free, rbuv_tcp);
54
+ rbuv_tcp->uv_handle->data = (void *)tcp;
55
+
56
+ RBUV_DEBUG_LOG_DETAIL("rbuv_tcp: %p, uv_handle: %p, tcp: %s",
57
+ rbuv_tcp, rbuv_tcp->uv_handle,
58
+ RSTRING_PTR(rb_inspect(tcp)));
59
+
60
+ return tcp;
61
+ }
62
+
63
+ void rbuv_tcp_mark(rbuv_tcp_t *rbuv_tcp) {
64
+ assert(rbuv_tcp);
65
+ RBUV_DEBUG_LOG_DETAIL("rbuv_tcp: %p, uv_handle: %p, self: %lx",
66
+ rbuv_tcp, rbuv_tcp->uv_handle,
67
+ (VALUE)rbuv_tcp->uv_handle->data);
68
+ rb_gc_mark(rbuv_tcp->cb_on_close);
69
+ rb_gc_mark(rbuv_tcp->cb_on_connection);
70
+ rb_gc_mark(rbuv_tcp->cb_on_read);
71
+ }
72
+
73
+ void rbuv_tcp_free(rbuv_tcp_t *rbuv_tcp) {
74
+ RBUV_DEBUG_LOG_DETAIL("rbuv_tcp: %p, uv_handle: %p", rbuv_tcp, rbuv_tcp->uv_handle);
75
+
76
+ if (!_rbuv_handle_is_closing((rbuv_handle_t *)rbuv_tcp)) {
77
+ uv_close((uv_handle_t *)rbuv_tcp->uv_handle, NULL);
78
+ }
79
+
80
+ free(rbuv_tcp);
81
+ }
82
+
83
+ VALUE rbuv_tcp_bind(VALUE self, VALUE ip, VALUE port) {
84
+ const char *uv_ip;
85
+ int uv_port;
86
+ rbuv_tcp_t *rbuv_tcp;
87
+ struct sockaddr_in bind_addr;
88
+
89
+ uv_ip = RSTRING_PTR(ip);
90
+ uv_port = FIX2INT(port);
91
+
92
+ bind_addr = uv_ip4_addr(uv_ip, uv_port);
93
+
94
+ Data_Get_Struct(self, rbuv_tcp_t, rbuv_tcp);
95
+ RBUV_CHECK_UV_RETURN(uv_tcp_bind(rbuv_tcp->uv_handle, bind_addr));
96
+
97
+ RBUV_DEBUG_LOG_DETAIL("self: %s, ip: %s, port: %d, rbuv_tcp: %p, uv_handle: %p",
98
+ RSTRING_PTR(rb_inspect(self)), uv_ip, uv_port, rbuv_tcp,
99
+ rbuv_tcp->uv_handle);
100
+
101
+ return self;
102
+ }
@@ -0,0 +1,12 @@
1
+ #ifndef RBUV_TCP_H_
2
+ #define RBUV_TCP_H_
3
+
4
+ #include "rbuv.h"
5
+
6
+ typedef struct rbuv_tcp_s rbuv_tcp_t;
7
+
8
+ extern VALUE cRbuvTcp;
9
+
10
+ void Init_rbuv_tcp();
11
+
12
+ #endif /* RBUV_TCP_H_ */
@@ -1,12 +1,18 @@
1
- #include "timer.h"
1
+ #include "rbuv_timer.h"
2
2
 
3
3
  VALUE cRbuvTimer;
4
4
 
5
5
  struct rbuv_timer_s {
6
6
  uv_timer_t *uv_handle;
7
- VALUE cb;
7
+ VALUE cb_on_close;
8
+ VALUE cb_on_timeout;
8
9
  };
9
10
 
11
+ typedef struct {
12
+ uv_timer_t *uv_timer;
13
+ int status;
14
+ } _uv_timer_on_timeout_no_gvl_arg_t;
15
+
10
16
  /* Allocator/deallocator */
11
17
  static VALUE rbuv_timer_alloc(VALUE klass);
12
18
  static void rbuv_timer_mark(rbuv_timer_t *rbuv_timer);
@@ -20,6 +26,7 @@ static VALUE rbuv_timer_repeat_set(VALUE self, VALUE repeat);
20
26
 
21
27
  /* Private methods */
22
28
  static void _uv_timer_on_timeout(uv_timer_t *uv_timer, int status);
29
+ static void _uv_timer_on_timeout_no_gvl(_uv_timer_on_timeout_no_gvl_arg_t *arg);
23
30
 
24
31
  void Init_rbuv_timer() {
25
32
  cRbuvTimer = rb_define_class_under(mRbuv, "Timer", cRbuvHandle);
@@ -38,7 +45,8 @@ VALUE rbuv_timer_alloc(VALUE klass) {
38
45
  rbuv_timer = malloc(sizeof(*rbuv_timer));
39
46
  rbuv_timer->uv_handle = malloc(sizeof(*rbuv_timer->uv_handle));
40
47
  uv_timer_init(uv_default_loop(), rbuv_timer->uv_handle);
41
- rbuv_timer->cb = Qnil;
48
+ rbuv_timer->cb_on_close = Qnil;
49
+ rbuv_timer->cb_on_timeout = Qnil;
42
50
 
43
51
  timer = Data_Wrap_Struct(klass, rbuv_timer_mark, rbuv_timer_free, rbuv_timer);
44
52
  rbuv_timer->uv_handle->data = (void *)timer;
@@ -48,12 +56,16 @@ VALUE rbuv_timer_alloc(VALUE klass) {
48
56
 
49
57
  void rbuv_timer_mark(rbuv_timer_t *rbuv_timer) {
50
58
  assert(rbuv_timer);
51
- rb_gc_mark(rbuv_timer->cb);
59
+ rb_gc_mark(rbuv_timer->cb_on_close);
60
+ rb_gc_mark(rbuv_timer->cb_on_timeout);
52
61
  }
53
62
 
54
63
  void rbuv_timer_free(rbuv_timer_t *rbuv_timer) {
55
64
  assert(rbuv_timer);
56
- rbuv_handle_close((rbuv_handle_t *)rbuv_timer);
65
+ RBUV_DEBUG_LOG_DETAIL("rbuv_timer: %p, uv_handle: %p", rbuv_timer, rbuv_timer->uv_handle);
66
+ if (!_rbuv_handle_is_closing((rbuv_handle_t *)rbuv_timer)) {
67
+ uv_close((uv_handle_t *)rbuv_timer->uv_handle, NULL);
68
+ }
57
69
  free(rbuv_timer);
58
70
  }
59
71
 
@@ -75,10 +87,11 @@ VALUE rbuv_timer_start(VALUE self, VALUE timeout, VALUE repeat) {
75
87
  uv_repeat = NUM2ULL(repeat);
76
88
 
77
89
  Data_Get_Struct(self, rbuv_timer_t, rbuv_timer);
78
- rbuv_timer->cb = block;
90
+ rbuv_timer->cb_on_timeout = block;
79
91
 
80
- RBUV_DEBUG_LOG_DETAIL("rbuv_timer: %p, uv_handle: %p, _uv_timer_on_timeout: %p, timer: %ld",
81
- rbuv_timer, rbuv_timer->uv_handle, _uv_timer_on_timeout, self);
92
+ RBUV_DEBUG_LOG_DETAIL("rbuv_timer: %p, uv_handle: %p, _uv_timer_on_timeout: %p, timer: %s",
93
+ rbuv_timer, rbuv_timer->uv_handle, _uv_timer_on_timeout,
94
+ RSTRING_PTR(rb_inspect(self)));
82
95
  uv_timer_start(rbuv_timer->uv_handle, _uv_timer_on_timeout,
83
96
  uv_timeout, uv_repeat);
84
97
 
@@ -123,11 +136,19 @@ VALUE rbuv_timer_repeat_set(VALUE self, VALUE repeat) {
123
136
  }
124
137
 
125
138
  void _uv_timer_on_timeout(uv_timer_t *uv_timer, int status) {
139
+ _uv_timer_on_timeout_no_gvl_arg_t reg = { .uv_timer = uv_timer, .status = status };
140
+ rb_thread_call_with_gvl((rbuv_rb_blocking_function_t)_uv_timer_on_timeout_no_gvl, &reg);
141
+ }
142
+
143
+ void _uv_timer_on_timeout_no_gvl(_uv_timer_on_timeout_no_gvl_arg_t *arg) {
144
+ uv_timer_t *uv_timer = arg->uv_timer;
145
+ int status = arg->status;
146
+
126
147
  VALUE timer;
127
148
  rbuv_timer_t *rbuv_timer;
128
149
 
129
150
  timer = (VALUE)uv_timer->data;
130
151
  Data_Get_Struct(timer, struct rbuv_timer_s, rbuv_timer);
131
152
 
132
- rb_funcall(rbuv_timer->cb, id_call, 1, timer);
133
- }
153
+ rb_funcall(rbuv_timer->cb_on_timeout, id_call, 1, timer);
154
+ }
@@ -2,7 +2,6 @@
2
2
  #define RBUV_TIMER_H_
3
3
 
4
4
  #include "rbuv.h"
5
- #include "handle.h"
6
5
 
7
6
  typedef struct rbuv_timer_s rbuv_timer_t;
8
7
 
@@ -10,4 +9,4 @@ extern VALUE cRbuvTimer;
10
9
 
11
10
  void Init_rbuv_timer();
12
11
 
13
- #endif /* RBUV_TIMER_H_ */
12
+ #endif /* RBUV_TIMER_H_ */
@@ -0,0 +1,10 @@
1
+ module Rbuv
2
+ class Signal
3
+ ::Signal.list.each do |signame, signum|
4
+ const_set signame, signum
5
+ end
6
+ def self.start(*args)
7
+ self.new.start(*args) { |*block_args| yield(*block_args) }
8
+ end
9
+ end
10
+ end
data/lib/rbuv/timer.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Rbuv
2
2
  class Timer
3
3
  def self.start(*args)
4
- Timer.new.start(*args) { |timer| yield timer }
4
+ self.new.start(*args) { |*block_args| yield(*block_args) }
5
5
  end
6
6
  end
7
7
  end
data/lib/rbuv/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rbuv
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/rbuv.rb CHANGED
@@ -1,27 +1,30 @@
1
1
  require 'rbuv/rbuv'
2
2
  require 'rbuv/version'
3
3
  require 'rbuv/timer'
4
+ require 'rbuv/signal'
4
5
 
5
6
  module Rbuv
6
- def self.run_loop
7
- Loop.run
8
- end
7
+ class << self
9
8
 
10
- def self.stop_loop
11
- Loop.stop
12
- end
9
+ def run_loop
10
+ Loop.run
11
+ end
13
12
 
14
- def self.run
15
- Timer.start 0, 0 do
16
- yield
13
+ def stop_loop
14
+ Loop.stop
17
15
  end
18
- self.run_loop
19
- end
20
16
 
21
- def self.run_block
22
- Timer.start 0, 0 do
23
- yield
17
+ alias stop stop_loop
18
+
19
+ def run
20
+ Timer.start(0, 0) { yield }
21
+ self.run_loop
22
+ end
23
+
24
+ def run_block
25
+ Timer.start(0, 0) { yield }
26
+ Loop.run_once
24
27
  end
25
- Loop.run_once
28
+
26
29
  end
27
30
  end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rbuv::Signal do
4
+ it "#start" do
5
+ block = mock
6
+ block.should_receive(:call).once
7
+
8
+ Rbuv.run do
9
+ sig = Rbuv::Signal.new
10
+ sig.start 2 do
11
+ block.call
12
+ sig.close
13
+ end
14
+
15
+ Process.kill(2, Process.pid)
16
+ end
17
+ end
18
+
19
+ it "#stop" do
20
+ block = mock
21
+ block.should_receive(:call).once
22
+
23
+ Rbuv.run do
24
+ sig = Rbuv::Signal.new
25
+ sig.start 2 do
26
+ block.call
27
+ sig.stop
28
+ end
29
+
30
+ Process.kill(2, Process.pid)
31
+ end
32
+ end
33
+ end
data/spec/tcp_spec.rb ADDED
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+ require 'socket'
3
+
4
+ def port_in_use?(port, host='127.0.0.1')
5
+ s = TCPServer.new host, port
6
+ s.close
7
+ false
8
+ rescue Errno::EADDRINUSE
9
+ true
10
+ end
11
+
12
+ def port_alive?(port, host='127.0.0.1')
13
+ s = TCPSocket.new host, port
14
+ s.close
15
+ false
16
+ rescue Errno::ECONNREFUSED
17
+ true
18
+ end
19
+
20
+ describe Rbuv::Tcp do
21
+ it { should be_a_kind_of Rbuv::Stream }
22
+
23
+ it "#bind" do
24
+ port_in_use?(60000).should be_false
25
+
26
+ Rbuv.run do
27
+ pending "this spec does't pass on linux machines, see #1 on github"
28
+ begin
29
+ tcp = Rbuv::Tcp.new
30
+ tcp.bind '127.0.0.1', 60000
31
+
32
+ port_in_use?(60000).should be_true
33
+ ensure
34
+ tcp.close
35
+ end
36
+
37
+ port_in_use?(60000).should be_false
38
+ end
39
+ end
40
+
41
+ context "#listen" do
42
+ it "when address not in use" do
43
+ port_in_use?(60000).should be_false
44
+
45
+ Rbuv.run do
46
+ begin
47
+ tcp = Rbuv::Tcp.new
48
+ tcp.bind '127.0.0.1', 60000
49
+ tcp.listen(10) { Rbuv.stop_loop }
50
+
51
+ port_in_use?(60000).should be_true
52
+ ensure
53
+ tcp.close
54
+ end
55
+
56
+ port_in_use?(60000).should be_false
57
+ end
58
+ end
59
+
60
+ it "when address already in use" do
61
+ port_in_use?(60000).should be_false
62
+
63
+ Rbuv.run do
64
+ begin
65
+ s = TCPServer.new '127.0.0.1', 60000
66
+
67
+ tcp = Rbuv::Tcp.new
68
+ tcp.bind '127.0.0.1', 60000
69
+ expect { tcp.listen(10) {} }.to raise_error
70
+ ensure
71
+ s.close
72
+ tcp.close
73
+ end
74
+ end
75
+ end
76
+
77
+ it "should call the on_connection callback when connection coming" do
78
+ on_connection = mock
79
+ on_connection.should_receive(:call).once
80
+
81
+ Rbuv.run do
82
+ tcp = Rbuv::Tcp.new
83
+ tcp.bind '127.0.0.1', 60000
84
+
85
+ tcp.listen(10) do
86
+ on_connection.call
87
+ tcp.close
88
+ end
89
+
90
+ sock = TCPSocket.new '127.0.0.1', 60000
91
+ sock.close
92
+ end
93
+ end
94
+ end
95
+
96
+ it "#accept" do
97
+ port_in_use?(60000).should be_false
98
+
99
+ Rbuv.run do
100
+ tcp = Rbuv::Tcp.new
101
+ tcp.bind '127.0.0.1', 60000
102
+
103
+ sock = nil
104
+
105
+ tcp.listen(10) do |s|
106
+ c = Rbuv::Tcp.new
107
+ expect { s.accept(c) }.not_to raise_error
108
+ sock.close
109
+ tcp.close
110
+ end
111
+
112
+ sock = TCPSocket.new '127.0.0.1', 60000
113
+ end
114
+ end
115
+
116
+ context "#close" do
117
+ it "affect #closing?" do
118
+ Rbuv.run do
119
+ tcp = Rbuv::Tcp.new
120
+ tcp.close do
121
+ tcp.closing?.should be_true
122
+ end
123
+ tcp.closing?.should be_true
124
+ end
125
+ end
126
+
127
+ it "call once" do
128
+ on_close = mock
129
+ on_close.should_receive(:call).once
130
+
131
+ Rbuv.run do
132
+ tcp = Rbuv::Tcp.new
133
+
134
+ tcp.close do
135
+ on_close.call
136
+ end
137
+ end
138
+ end
139
+
140
+ it "call multi-times" do
141
+ on_close = mock
142
+ on_close.should_receive(:call).once
143
+
144
+ no_on_close = mock
145
+ no_on_close.should_not_receive(:call)
146
+
147
+ Rbuv.run do
148
+ tcp = Rbuv::Tcp.new
149
+
150
+ tcp.close do
151
+ on_close.call
152
+ end
153
+
154
+ tcp.close do
155
+ no_on_close.call
156
+ end
157
+ end
158
+ end
159
+
160
+ end
161
+ end