io-epoll 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +33 -30
- data/examples/epoll_server.rb +47 -0
- data/ext/io/epoll/epoll.c +77 -13
- data/io-epoll.gemspec +1 -1
- data/lib/io/epoll.rb +0 -33
- data/test/test_epoll.rb +8 -33
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87c51dc70ed31c8d3a34012ce002a0c9c19c7982
|
4
|
+
data.tar.gz: 92e3c83055ba130342883cb1bb15f9d55bf6c6fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd0dd5bdd8e4085c8b6be91ba5ddd218d448981cd3365c498c4bd936196da3d9827d639ff1afeccc5c1ecad72f29de6a16933aab90f6c7bef1f5f678b931a857
|
7
|
+
data.tar.gz: 1eec9c05b0d6e9c6b22fe399992389b62dd92672e65255bcf34d0c1dc4acb8bb6ed46aad0f4a1c1df8424a4d5e567cbee5e226e2c8b2db8e4a4935053192061b
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -12,25 +12,7 @@ An experimental binding of epoll(7).
|
|
12
12
|
```ruby
|
13
13
|
require 'io/epoll'
|
14
14
|
|
15
|
-
|
16
|
-
# ev is IO::Epoll::Event object like `struct epoll_event`
|
17
|
-
# it's have data and events properties
|
18
|
-
|
19
|
-
# IO::Epoll::Event#events is event flag bits (Fixnum)
|
20
|
-
events = ev.events
|
21
|
-
|
22
|
-
# IO::Epoll::Event#data is notified IO (IO)
|
23
|
-
data = ev.data
|
24
|
-
end
|
25
|
-
|
26
|
-
# or
|
27
|
-
|
28
|
-
# evlist is Array of IO::Epoll::Event
|
29
|
-
# it's just short hand for epoll_create(2) -> epoll_ctl(2) -> epoll_wait(2)
|
30
|
-
evlist = IO.epoll([io1, io2, io3], Epoll::IN)
|
31
|
-
|
32
|
-
# on other way, you can make instance of IO::Epoll
|
33
|
-
|
15
|
+
# Recommend short hand
|
34
16
|
Epoll = IO::Epoll
|
35
17
|
|
36
18
|
# IO::Epoll.create
|
@@ -42,10 +24,7 @@ epoll = Epoll.create
|
|
42
24
|
|
43
25
|
# IO::Epoll#ctl(option, io, flag)
|
44
26
|
# call epoll_ctl(2)
|
45
|
-
# option: you can choice
|
46
|
-
# CTL_ADD: add io list to watching for created epoll fd
|
47
|
-
# CTL_MOD: you can change io events
|
48
|
-
# CTL_DEL: delete io in watching list
|
27
|
+
# option: you can choice options (see ctl options).
|
49
28
|
# io: set an IO object for watching.
|
50
29
|
# flag: set flag bits like Epoll::IN|Epoll::OUT|Epoll::ONESHOT etc...
|
51
30
|
# see also man epoll_ctl(2)
|
@@ -57,13 +36,29 @@ epoll.add(io, Epoll::IN) # same way to epoll.ctl(Epoll::CTL_ADD, io, Epoll::IN)
|
|
57
36
|
epoll.mod(io, Epoll::OUT) # same way to epoll.ctl(Epoll::CTL_MOD, io, Epoll::IN)
|
58
37
|
epoll.del(io) # same way to epoll.ctl(Epoll::CTL_DEL, io)
|
59
38
|
|
60
|
-
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# timeout =
|
64
|
-
# timeout
|
65
|
-
#
|
66
|
-
|
39
|
+
loop do
|
40
|
+
# IO::Epoll#wait(timeout=-1)
|
41
|
+
# call epoll_wait(2)
|
42
|
+
# timeout = -1: block until receive event or signals
|
43
|
+
# timeout = 0: return all io's can I/O on non block
|
44
|
+
# timeout > 0: block when timeout pass miri second or receive events or signals
|
45
|
+
# return: Array of IO::Epoll::Event
|
46
|
+
evlist = epoll.wait
|
47
|
+
|
48
|
+
# ev is instance of IO::Epoll::Event like `struct epoll_event`
|
49
|
+
# it's same as `class Event < Struct.new(:data, :events); end`
|
50
|
+
evlist.each do |ev|
|
51
|
+
# IO::Epoll::Event#events is event flag bits (Fixnum)
|
52
|
+
if (ev.events & Epoll::IN) != 0
|
53
|
+
# IO::Epoll::Event#data is notified IO (IO)
|
54
|
+
# e.g. it's expect to I/O readable
|
55
|
+
puts ev.data.read
|
56
|
+
elsif (ev.events & Epoll::HUP|Epoll::ERR) != 0
|
57
|
+
ev.data.close
|
58
|
+
break
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
67
62
|
|
68
63
|
# you can close File Descriptor for epoll when finish to use
|
69
64
|
epoll.close #=> nil
|
@@ -72,6 +67,14 @@ epoll.close #=> nil
|
|
72
67
|
epoll.closed? #=> true
|
73
68
|
```
|
74
69
|
|
70
|
+
## ctl options
|
71
|
+
|
72
|
+
ctl options|description
|
73
|
+
---|---
|
74
|
+
**IO::Epoll::CTL_ADD**|add to interest list for created epoll fd
|
75
|
+
**IO::Epoll::CTL_MOD**|change io events
|
76
|
+
**IO::Epoll::CTL_DEL**|delete in interest list
|
77
|
+
|
75
78
|
## Event flags
|
76
79
|
|
77
80
|
event flags|ctl|wait|description
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'io/epoll'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
server = TCPServer.open(4000)
|
7
|
+
|
8
|
+
response = [
|
9
|
+
"HTTP/1.0 200 OK\r\n",
|
10
|
+
"Content-Length: 5\r\n",
|
11
|
+
"Content-Type: text/html\r\n",
|
12
|
+
"\r\n",
|
13
|
+
"HELLO\r\n",
|
14
|
+
].join("")
|
15
|
+
|
16
|
+
Epoll = IO::Epoll
|
17
|
+
ep = Epoll.create
|
18
|
+
ep.add server, Epoll::IN
|
19
|
+
|
20
|
+
Signal.trap(:INT) {
|
21
|
+
ep.close
|
22
|
+
server.close
|
23
|
+
}
|
24
|
+
|
25
|
+
loop do
|
26
|
+
ep.wait.each do |ev|
|
27
|
+
data = ev.data
|
28
|
+
events = ev.events
|
29
|
+
|
30
|
+
if data == server
|
31
|
+
socket = server.accept
|
32
|
+
ep.add socket, Epoll::IN|Epoll::ET
|
33
|
+
elsif (events & Epoll::IN) != 0
|
34
|
+
data.recv(1024)
|
35
|
+
ep.mod data, Epoll::OUT|Epoll::ET
|
36
|
+
elsif (events & Epoll::OUT) != 0
|
37
|
+
data.puts response
|
38
|
+
ep.del data
|
39
|
+
data.close
|
40
|
+
elsif (events & (Epoll::HUP|Epoll::ERR)) != 0
|
41
|
+
p "Epoll::HUP|Epoll::ERR"
|
42
|
+
else
|
43
|
+
raise IOError
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
data/ext/io/epoll/epoll.c
CHANGED
@@ -4,18 +4,25 @@
|
|
4
4
|
#include <sys/epoll.h>
|
5
5
|
|
6
6
|
VALUE cIO_Epoll;
|
7
|
+
VALUE cIO_Epoll_Event;
|
7
8
|
|
8
9
|
struct Epoll {
|
9
10
|
int epfd;
|
10
11
|
int ev_len;
|
11
12
|
};
|
12
13
|
|
14
|
+
static void
|
15
|
+
epoll_fd_close(int epfd)
|
16
|
+
{
|
17
|
+
rb_thread_fd_close(epfd);
|
18
|
+
}
|
19
|
+
|
13
20
|
static void
|
14
21
|
rb_epoll_free(void *p)
|
15
22
|
{
|
16
23
|
struct Epoll *ptr = p;
|
17
24
|
if (ptr) {
|
18
|
-
if (0 <= ptr->epfd)
|
25
|
+
if (0 <= ptr->epfd) epoll_fd_close(ptr->epfd);
|
19
26
|
ruby_xfree(ptr);
|
20
27
|
}
|
21
28
|
}
|
@@ -38,15 +45,30 @@ static const rb_data_type_t epoll_data_type = {
|
|
38
45
|
NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
|
39
46
|
};
|
40
47
|
|
48
|
+
void
|
49
|
+
epoll_check_initialized(struct Epoll *ptr)
|
50
|
+
{
|
51
|
+
if (!ptr) {
|
52
|
+
rb_raise(rb_eIOError, "uninitialized stream");
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
void
|
57
|
+
epoll_check_closed(struct Epoll *ptr)
|
58
|
+
{
|
59
|
+
epoll_check_initialized(ptr);
|
60
|
+
if (ptr->epfd < 0) {
|
61
|
+
rb_raise(rb_eIOError, "closed stream");
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
41
65
|
static struct Epoll*
|
42
66
|
get_epoll(VALUE self)
|
43
67
|
{
|
44
68
|
struct Epoll *ptr;
|
45
69
|
rb_check_frozen(self);
|
46
70
|
TypedData_Get_Struct(self, struct Epoll, &epoll_data_type, ptr);
|
47
|
-
|
48
|
-
rb_raise(rb_eIOError, "uninitialized stream");
|
49
|
-
}
|
71
|
+
epoll_check_initialized(ptr);
|
50
72
|
return ptr;
|
51
73
|
}
|
52
74
|
|
@@ -64,13 +86,21 @@ rb_epoll_initialize(VALUE self)
|
|
64
86
|
int epfd;
|
65
87
|
|
66
88
|
TypedData_Get_Struct(self, struct Epoll, &epoll_data_type, ptr);
|
67
|
-
if (ptr->epfd < 0)
|
89
|
+
if (ptr->epfd < 0) epoll_fd_close(ptr->epfd);
|
68
90
|
epfd = epoll_create(1);
|
69
91
|
if (epfd == -1) {
|
70
92
|
rb_sys_fail("epoll_create was failed");
|
71
93
|
}
|
72
94
|
ptr->epfd = epfd;
|
73
95
|
ptr->ev_len = 0;
|
96
|
+
|
97
|
+
/**
|
98
|
+
* FIXME: I want to delete instance variable `evlist` !
|
99
|
+
* It's just only using for GC mark.
|
100
|
+
* So, I don't know how to GC guard io objects.
|
101
|
+
*/
|
102
|
+
rb_ivar_set(self, rb_intern("evlist"), rb_ary_new());
|
103
|
+
|
74
104
|
return self;
|
75
105
|
}
|
76
106
|
|
@@ -78,9 +108,26 @@ static VALUE
|
|
78
108
|
rb_epoll_fileno(VALUE self)
|
79
109
|
{
|
80
110
|
struct Epoll *ptr = get_epoll(self);
|
111
|
+
epoll_check_closed(ptr);
|
81
112
|
return INT2FIX(ptr->epfd);
|
82
113
|
}
|
83
114
|
|
115
|
+
inline static void
|
116
|
+
rb_epoll_evlist_add(VALUE self, VALUE io)
|
117
|
+
{
|
118
|
+
VALUE evlist = rb_ivar_get(self, rb_intern("evlist"));
|
119
|
+
rb_ary_push(evlist, io);
|
120
|
+
rb_ivar_set(self, rb_intern("evlist"), evlist);
|
121
|
+
}
|
122
|
+
|
123
|
+
inline static void
|
124
|
+
rb_epoll_evlist_del(VALUE self, VALUE io)
|
125
|
+
{
|
126
|
+
VALUE evlist = rb_ivar_get(self, rb_intern("evlist"));
|
127
|
+
rb_ary_delete(evlist, io);
|
128
|
+
rb_ivar_set(self, rb_intern("evlist"), evlist);
|
129
|
+
}
|
130
|
+
|
84
131
|
static VALUE
|
85
132
|
rb_epoll_ctl(int argc, VALUE *argv, VALUE self)
|
86
133
|
{
|
@@ -97,11 +144,21 @@ rb_epoll_ctl(int argc, VALUE *argv, VALUE self)
|
|
97
144
|
if (FIX2INT(flag) != EPOLL_CTL_DEL)
|
98
145
|
rb_raise(rb_eArgError, "too few argument for CTL_ADD or CTL_MOD");
|
99
146
|
break;
|
147
|
+
rb_epoll_evlist_del(self, io);
|
100
148
|
case 3:
|
101
|
-
if (FIX2INT(flag)
|
149
|
+
if (FIX2INT(flag) == EPOLL_CTL_ADD) {
|
150
|
+
rb_epoll_evlist_add(self, io);
|
151
|
+
}
|
152
|
+
else if (FIX2INT(flag) == EPOLL_CTL_MOD) {
|
153
|
+
/* nothing */
|
154
|
+
}
|
155
|
+
else {
|
102
156
|
rb_raise(rb_eArgError, "too many argument for CTL_DEL");
|
157
|
+
}
|
158
|
+
|
103
159
|
if ((FIX2LONG(events) & (EPOLLIN|EPOLLPRI|EPOLLRDHUP|EPOLLOUT|EPOLLET|EPOLLONESHOT)) == 0)
|
104
160
|
rb_raise(rb_eIOError, "undefined events");
|
161
|
+
|
105
162
|
ev.events = FIX2LONG(events);
|
106
163
|
ev.data.ptr = (void*)io;
|
107
164
|
break;
|
@@ -150,7 +207,6 @@ rb_epoll_wait(int argc, VALUE *argv, VALUE self)
|
|
150
207
|
{
|
151
208
|
struct Epoll *ptr = get_epoll(self);
|
152
209
|
VALUE ready_evlist;
|
153
|
-
VALUE cEvent;
|
154
210
|
VALUE event;
|
155
211
|
struct epoll_event *evlist;
|
156
212
|
int i, ready;
|
@@ -159,6 +215,7 @@ rb_epoll_wait(int argc, VALUE *argv, VALUE self)
|
|
159
215
|
|
160
216
|
if (argc == 1)
|
161
217
|
timeout = FIX2INT(argv[0]);
|
218
|
+
|
162
219
|
if (ptr->ev_len <= 0)
|
163
220
|
rb_raise(rb_eIOError, "empty interest list");
|
164
221
|
|
@@ -181,9 +238,8 @@ RETRY:
|
|
181
238
|
}
|
182
239
|
|
183
240
|
ready_evlist = rb_ary_new_capa(ready);
|
184
|
-
cEvent = rb_path2class("IO::Epoll::Event");
|
185
241
|
for (i = 0; i < ready; i++) {
|
186
|
-
event = rb_obj_alloc(
|
242
|
+
event = rb_obj_alloc(cIO_Epoll_Event);
|
187
243
|
RSTRUCT_SET(event, 0, (VALUE) evlist[i].data.ptr);
|
188
244
|
RSTRUCT_SET(event, 1, LONG2FIX(evlist[i].events));
|
189
245
|
rb_ary_store(ready_evlist, i, event);
|
@@ -196,13 +252,19 @@ static VALUE
|
|
196
252
|
rb_epoll_close(VALUE self)
|
197
253
|
{
|
198
254
|
struct Epoll *ptr = get_epoll(self);
|
199
|
-
|
200
|
-
|
201
|
-
}
|
255
|
+
epoll_check_closed(ptr);
|
256
|
+
epoll_fd_close(ptr->epfd);
|
202
257
|
ptr->epfd = -1;
|
203
258
|
return Qnil;
|
204
259
|
}
|
205
260
|
|
261
|
+
static VALUE
|
262
|
+
rb_epoll_closed_p(VALUE self)
|
263
|
+
{
|
264
|
+
struct Epoll *ptr = get_epoll(self);
|
265
|
+
return 0 <= ptr->epfd ? Qfalse : Qtrue;
|
266
|
+
}
|
267
|
+
|
206
268
|
static VALUE
|
207
269
|
rb_epoll_length(VALUE self)
|
208
270
|
{
|
@@ -214,13 +276,15 @@ void
|
|
214
276
|
Init_epoll()
|
215
277
|
{
|
216
278
|
cIO_Epoll = rb_define_class_under(rb_cIO, "Epoll", rb_cObject);
|
279
|
+
cIO_Epoll_Event = rb_struct_define_under(cIO_Epoll, "Event", "data", "events", NULL);
|
217
280
|
rb_define_alloc_func(cIO_Epoll, rb_epoll_allocate);
|
218
281
|
|
219
282
|
rb_define_method(cIO_Epoll, "initialize", rb_epoll_initialize, 0);
|
220
|
-
rb_define_method(cIO_Epoll, "fileno", rb_epoll_fileno, 0);
|
221
283
|
rb_define_method(cIO_Epoll, "ctl", rb_epoll_ctl, -1);
|
222
284
|
rb_define_method(cIO_Epoll, "wait", rb_epoll_wait, -1);
|
285
|
+
rb_define_method(cIO_Epoll, "fileno", rb_epoll_fileno, 0);
|
223
286
|
rb_define_method(cIO_Epoll, "close", rb_epoll_close, 0);
|
287
|
+
rb_define_method(cIO_Epoll, "closed?", rb_epoll_closed_p, 0);
|
224
288
|
rb_define_method(cIO_Epoll, "length", rb_epoll_length, 0);
|
225
289
|
rb_define_alias(cIO_Epoll, "size", "length");
|
226
290
|
rb_define_const(cIO_Epoll, "IN", INT2FIX(EPOLLIN));
|
data/io-epoll.gemspec
CHANGED
data/lib/io/epoll.rb
CHANGED
@@ -2,9 +2,6 @@ require 'io/epoll/epoll'
|
|
2
2
|
|
3
3
|
class IO
|
4
4
|
class Epoll
|
5
|
-
class Event < Struct.new(:data, :events)
|
6
|
-
end
|
7
|
-
|
8
5
|
class << self
|
9
6
|
alias create new
|
10
7
|
end
|
@@ -20,35 +17,5 @@ class IO
|
|
20
17
|
def del(io)
|
21
18
|
ctl(CTL_DEL, io)
|
22
19
|
end
|
23
|
-
|
24
|
-
def closed?
|
25
|
-
fileno < 0
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.epoll(ios, events)
|
30
|
-
ios = ios.to_a
|
31
|
-
open_len = ios.length
|
32
|
-
ep = Epoll.create
|
33
|
-
ios.each do |io|
|
34
|
-
ep.add(io, events)
|
35
|
-
end
|
36
|
-
if block_given?
|
37
|
-
while 0 < open_len
|
38
|
-
evlist = ep.wait
|
39
|
-
evlist.each do |ev|
|
40
|
-
yield ev
|
41
|
-
if ev.events & (Epoll::HUP|Epoll::ERR)
|
42
|
-
open_len -= 1
|
43
|
-
ev.data.close
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
ep.close
|
48
|
-
else
|
49
|
-
evlist = ep.wait
|
50
|
-
ep.close
|
51
|
-
evlist
|
52
|
-
end
|
53
20
|
end
|
54
21
|
end
|
data/test/test_epoll.rb
CHANGED
@@ -9,7 +9,10 @@ class TestIOEpoll < Test::Unit::TestCase
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_fileno
|
12
|
-
|
12
|
+
ep = IO::Epoll.create
|
13
|
+
assert { 0 < ep.fileno }
|
14
|
+
ep.close
|
15
|
+
assert_raise(IOError) { ep.fileno }
|
13
16
|
end
|
14
17
|
|
15
18
|
def test_ctl
|
@@ -56,7 +59,10 @@ class TestIOEpoll < Test::Unit::TestCase
|
|
56
59
|
ep = IO::Epoll.create
|
57
60
|
io = IO.new(1, 'w')
|
58
61
|
ep.add(io, IO::Epoll::IN|IO::Epoll::PRI|IO::Epoll::RDHUP|IO::Epoll::ET|IO::Epoll::OUT)
|
59
|
-
|
62
|
+
evlist = ep.wait
|
63
|
+
assert { [IO::Epoll::Event.new(io, IO::Epoll::OUT)] == evlist }
|
64
|
+
assert_instance_of(IO, evlist[0].data)
|
65
|
+
assert_instance_of(Fixnum, evlist[0].events)
|
60
66
|
assert_raise(TypeError) { ep.wait(nil) }
|
61
67
|
assert_raise(IOError) { IO::Epoll.create.wait }
|
62
68
|
end
|
@@ -89,37 +95,6 @@ class TestIOEpoll < Test::Unit::TestCase
|
|
89
95
|
assert { true == ep.closed? }
|
90
96
|
end
|
91
97
|
|
92
|
-
def test_epoll
|
93
|
-
r, w = IO.pipe
|
94
|
-
fork {
|
95
|
-
r.close
|
96
|
-
w.write('ok')
|
97
|
-
}
|
98
|
-
w.close
|
99
|
-
ret = []
|
100
|
-
evs = IO.epoll([r], IO::Epoll::IN)
|
101
|
-
assert { 'ok' == evs[0].data.read }
|
102
|
-
assert { false == evs[0].data.closed? }
|
103
|
-
assert_raise(IOError) { IO.epoll(nil, nil) }
|
104
|
-
assert_raise(IOError) { IO.epoll([], nil) }
|
105
|
-
assert_raise(TypeError) { IO.epoll([nil], nil) }
|
106
|
-
end
|
107
|
-
|
108
|
-
def test_epoll_with_block
|
109
|
-
r, w = IO.pipe
|
110
|
-
fork {
|
111
|
-
r.close
|
112
|
-
w.write('ok')
|
113
|
-
}
|
114
|
-
w.close
|
115
|
-
ret = []
|
116
|
-
IO.epoll([r], IO::Epoll::IN) do |ev|
|
117
|
-
ret << ev
|
118
|
-
assert { 'ok' == ev.data.read }
|
119
|
-
end
|
120
|
-
assert { true == ret[0].data.closed? }
|
121
|
-
end
|
122
|
-
|
123
98
|
def test_thread
|
124
99
|
ep = IO::Epoll.create
|
125
100
|
io = IO.new(1, 'w')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io-epoll
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ksss
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- LICENSE.txt
|
82
82
|
- README.md
|
83
83
|
- Rakefile
|
84
|
+
- examples/epoll_server.rb
|
84
85
|
- ext/io/epoll/epoll.c
|
85
86
|
- ext/io/epoll/extconf.rb
|
86
87
|
- io-epoll.gemspec
|