io-epoll 0.0.2 → 0.1.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 +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
|