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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f1d5379f3b1a1f7630975f462c9580f0156d53b0
4
- data.tar.gz: f9627c3a392a839c64b204aeae939d1fbf8da753
3
+ metadata.gz: 87c51dc70ed31c8d3a34012ce002a0c9c19c7982
4
+ data.tar.gz: 92e3c83055ba130342883cb1bb15f9d55bf6c6fd
5
5
  SHA512:
6
- metadata.gz: 10dfbf4366e5c54dbad66a2bf33d822851d9e2481eda47007348b7446c7939433efd0272bb65a918bec2c6c4486b57a9f1a067c045222d84eeb9831852bf1cd4
7
- data.tar.gz: 0bf8d0e53cfb0bd207ed962341481c30c561301752b8f50e4a7767adef959950cd80f4d55644eddcdd7a09711804ffa6e8c2e8b3c1cb7aa5ac7fced2c23e46f5
6
+ metadata.gz: bd0dd5bdd8e4085c8b6be91ba5ddd218d448981cd3365c498c4bd936196da3d9827d639ff1afeccc5c1ecad72f29de6a16933aab90f6c7bef1f5f678b931a857
7
+ data.tar.gz: 1eec9c05b0d6e9c6b22fe399992389b62dd92672e65255bcf34d0c1dc4acb8bb6ed46aad0f4a1c1df8424a4d5e567cbee5e226e2c8b2db8e4a4935053192061b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- io-epoll (0.0.2)
4
+ io-epoll (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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
- IO.epoll([io1, io2, io3], Epoll::IN) do |ev|
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 epoll_ctl option in CTL_ADD, CTL_MOD and CTL_DEL.
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
- # IO::Epoll#wait(timeout=-1)
61
- # call epoll_wait(2)
62
- # timeout = -1: block until receive event or signals
63
- # timeout = 0: return all io's can I/O on non block
64
- # timeout > 0: block when timeout pass miri second or receive events or signals
65
- # return: Array of IO::Epoll::Event
66
- evlist = epoll.wait
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) close(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
- if (!ptr) {
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) close(ptr->epfd);
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) != EPOLL_CTL_ADD && FIX2INT(flag) != EPOLL_CTL_MOD)
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(cEvent);
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
- if (close(ptr->epfd) == -1) {
200
- rb_raise(rb_eIOError, "file descriptor duplicate close %ld", INT2FIX(ptr->epfd));
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "io-epoll"
3
- spec.version = "0.0.2"
3
+ spec.version = "0.1.0"
4
4
  spec.authors = ["ksss"]
5
5
  spec.email = ["co000ri@gmail.com"]
6
6
  spec.summary = %q{An experimental binding of epoll(7)}
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
- assert { 0 < IO::Epoll.create.fileno }
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
- assert { [IO::Epoll::Event.new(io, IO::Epoll::OUT)] == ep.wait }
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.2
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-10-31 00:00:00.000000000 Z
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