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 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