epoll 0.3.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 +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +119 -0
- data/Rakefile +11 -0
- data/epoll.gemspec +21 -0
- data/examples/epoll_server.rb +46 -0
- data/ext/epoll/core.c +194 -0
- data/ext/epoll/extconf.rb +8 -0
- data/lib/epoll.rb +2 -0
- data/lib/epoll/ext.rb +26 -0
- data/test/test_epoll.rb +213 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: af3e6c1172e4e5d592bc3684a61f2dd25ba9613b
|
4
|
+
data.tar.gz: bffa9bab1512afd08c4deb1367e3b0fc87e1fe98
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9f8249e738f8e48e519a086a1508c64ec6cbd0d03e27893480836410e4e4af3d18449ccd7b2ea4689686b606e11aab152b633eb548906134bfbdc2164a462ef7
|
7
|
+
data.tar.gz: 189aa3dcf3f0ea548ed3eb0111d8fe16b417c76ed5f21daf56a7788a9f3dceca9384340d0bde472b16e0276363749e2308c92b84a9ea07c0015944bb43710a70
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
epoll (0.3.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
power_assert (0.1.3)
|
10
|
+
rake (10.3.2)
|
11
|
+
rake-compiler (0.8.3)
|
12
|
+
rake
|
13
|
+
test-unit (3.0.1)
|
14
|
+
power_assert
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
ruby
|
18
|
+
|
19
|
+
DEPENDENCIES
|
20
|
+
bundler
|
21
|
+
epoll!
|
22
|
+
rake
|
23
|
+
rake-compiler
|
24
|
+
test-unit
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 ksss
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
epoll
|
2
|
+
===
|
3
|
+
|
4
|
+
[](https://travis-ci.org/ksss/epoll)
|
5
|
+
|
6
|
+
A binding of epoll(7) on Ruby.
|
7
|
+
|
8
|
+
**epoll(7)** can use Linux only. (because must be installed sys/epoll.h)
|
9
|
+
|
10
|
+
# Usage
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require 'epoll'
|
14
|
+
|
15
|
+
# Epoll < IO
|
16
|
+
|
17
|
+
# Epoll.create
|
18
|
+
# call epoll_create(2)
|
19
|
+
# it's just alias of `open`
|
20
|
+
# Epoll object stock a File Descriptor returned by epoll_create(2)
|
21
|
+
# return: instance of Epoll (kind of IO)
|
22
|
+
epoll = Epoll.create
|
23
|
+
|
24
|
+
# IO object add to interest list
|
25
|
+
# call epoll_ctl(2)
|
26
|
+
epoll.add(io, Epoll::IN) # same way to epoll.ctl(Epoll::CTL_ADD, io, Epoll::IN)
|
27
|
+
|
28
|
+
# change waiting events
|
29
|
+
# call epoll_ctl(2)
|
30
|
+
epoll.mod(io, Epoll::OUT) # same way to epoll.ctl(Epoll::CTL_MOD, io, Epoll::OUT)
|
31
|
+
|
32
|
+
# remove from interest list
|
33
|
+
# call epoll_ctl(2)
|
34
|
+
epoll.del(io) # same way to epoll.ctl(Epoll::CTL_DEL, io)
|
35
|
+
|
36
|
+
loop do
|
37
|
+
# Epoll#wait(timeout=-1)
|
38
|
+
# call epoll_wait(2)
|
39
|
+
# timeout = -1: block until receive event or signals
|
40
|
+
# timeout = 0: return all io's can I/O on non block
|
41
|
+
# timeout > 0: block when timeout pass miri second or receive events or signals
|
42
|
+
# return: Array of Epoll::Event
|
43
|
+
evlist = epoll.wait
|
44
|
+
|
45
|
+
# ev is instance of Epoll::Event like `struct epoll_event`
|
46
|
+
# it's instance of `class Epoll::Event < Struct.new(:data, :events); end`
|
47
|
+
evlist.each do |ev|
|
48
|
+
# Epoll::Event#events is event flag bits (Fixnum)
|
49
|
+
if (ev.events & Epoll::IN) != 0
|
50
|
+
# Epoll::Event#data is notified IO (IO)
|
51
|
+
# e.g. it's expect to I/O readable
|
52
|
+
puts ev.data.read
|
53
|
+
elsif (ev.events & Epoll::HUP|Epoll::ERR) != 0
|
54
|
+
ev.data.close
|
55
|
+
break
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# you can close File Descriptor for epoll when finish to use
|
61
|
+
epoll.close #=> nil
|
62
|
+
|
63
|
+
# and you can check closed
|
64
|
+
epoll.closed? #=> true
|
65
|
+
|
66
|
+
# and very useful way is that call `create` with block like `IO.open`
|
67
|
+
# return: block result
|
68
|
+
Epoll.create do |epoll|
|
69
|
+
# ensure automatic call `epoll.close` when out block
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
## ctl options
|
74
|
+
|
75
|
+
ctl options|description
|
76
|
+
---|---
|
77
|
+
**Epoll::CTL_ADD**|add to interest list for created epoll fd
|
78
|
+
**Epoll::CTL_MOD**|change io events
|
79
|
+
**Epoll::CTL_DEL**|delete in interest list
|
80
|
+
|
81
|
+
## Event flags
|
82
|
+
|
83
|
+
event flags|ctl|wait|description
|
84
|
+
---|---|---|---
|
85
|
+
**Epoll::IN**|o|o|readable
|
86
|
+
**Epoll::PRI**|o|o|high priority read
|
87
|
+
**Epoll::HUP**|o|o|peer socket was shutdown
|
88
|
+
**Epoll::OUT**|o|o|writable
|
89
|
+
**Epoll::ET**|o|x|use edge trigger
|
90
|
+
**Epoll::ONESHOT**|o|x|auto watching stop when notified(but stay in list)
|
91
|
+
**Epoll::ERR**|x|o|raise error
|
92
|
+
**Epoll::HUP**|x|o|raise hang up
|
93
|
+
|
94
|
+
see also **man epoll(7)**
|
95
|
+
|
96
|
+
## Installation
|
97
|
+
|
98
|
+
Add this line to your application's Gemfile:
|
99
|
+
|
100
|
+
gem 'epoll'
|
101
|
+
|
102
|
+
And then execute:
|
103
|
+
|
104
|
+
$ bundle
|
105
|
+
|
106
|
+
Or install it yourself as:
|
107
|
+
|
108
|
+
$ gem install epoll
|
109
|
+
|
110
|
+
# Pro Tips
|
111
|
+
|
112
|
+
- Support call without GVL in CRuby (use rb\_thread\_call\_without\_gvl())
|
113
|
+
- Close on exec flag set by default if you can use (use epoll_create1(EPOLL_CLOEXEC))
|
114
|
+
- Epoll#wait max return array size is 256 on one time (of course, overflowing and then carried next)
|
115
|
+
|
116
|
+
# Fork Me !
|
117
|
+
|
118
|
+
This is experimental implementation.
|
119
|
+
I'm waiting for your idea and Pull Request !
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake/extensiontask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
task :default => [:compile, :test]
|
6
|
+
|
7
|
+
Rake::ExtensionTask.new('core') do |ext|
|
8
|
+
ext.ext_dir = 'ext/epoll'
|
9
|
+
ext.lib_dir = 'lib/epoll'
|
10
|
+
end
|
11
|
+
Rake::TestTask.new {|t| t.libs << 'test'}
|
data/epoll.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "epoll"
|
3
|
+
spec.version = "0.3.0"
|
4
|
+
spec.authors = ["ksss"]
|
5
|
+
spec.email = ["co000ri@gmail.com"]
|
6
|
+
spec.summary = %q{A Ruby binding for epoll(7)}
|
7
|
+
spec.description = %q{A Ruby binding for epoll(7)}
|
8
|
+
spec.homepage = "https://github.com/ksss/epoll"
|
9
|
+
spec.license = "MIT"
|
10
|
+
|
11
|
+
spec.files = `git ls-files`.split($/)
|
12
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
13
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
|
+
spec.require_paths = ["lib"]
|
15
|
+
spec.extensions = ["ext/epoll/extconf.rb"]
|
16
|
+
|
17
|
+
spec.add_development_dependency "bundler"
|
18
|
+
spec.add_development_dependency "rake"
|
19
|
+
spec.add_development_dependency 'rake-compiler'
|
20
|
+
spec.add_development_dependency 'test-unit'
|
21
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'epoll'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
server = TCPServer.open(4000)
|
7
|
+
puts "run http://127.0.0.1:4000/"
|
8
|
+
|
9
|
+
response = [
|
10
|
+
"HTTP/1.0 200 OK\r\n",
|
11
|
+
"Content-Length: 5\r\n",
|
12
|
+
"Content-Type: text/html\r\n",
|
13
|
+
"\r\n",
|
14
|
+
"HELLO\r\n",
|
15
|
+
].join("")
|
16
|
+
|
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
|
data/ext/epoll/core.c
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "ruby/io.h"
|
3
|
+
#include "ruby/thread.h"
|
4
|
+
|
5
|
+
#ifdef HAVE_SYS_EPOLL_H
|
6
|
+
|
7
|
+
#include <sys/epoll.h>
|
8
|
+
#include <fcntl.h>
|
9
|
+
|
10
|
+
#define EPOLL_WAIT_MAX_EVENTS 256
|
11
|
+
|
12
|
+
VALUE cEpoll;
|
13
|
+
VALUE cEpoll_Constants;
|
14
|
+
VALUE cEpoll_Event;
|
15
|
+
|
16
|
+
static VALUE
|
17
|
+
rb_epoll_initialize(VALUE self)
|
18
|
+
{
|
19
|
+
rb_io_t *fp;
|
20
|
+
int fd;
|
21
|
+
|
22
|
+
#if defined(HAVE_EPOLL_CREATE1) && defined(EPOLL_CLOEXEC)
|
23
|
+
fd = epoll_create1(EPOLL_CLOEXEC);
|
24
|
+
if (fd == -1)
|
25
|
+
rb_sys_fail("epoll_create1(2) was failed");
|
26
|
+
#else
|
27
|
+
fd = epoll_create(1024);
|
28
|
+
if (fd == -1)
|
29
|
+
rb_sys_fail("epoll_create(2) was failed");
|
30
|
+
#endif
|
31
|
+
rb_update_max_fd(fd);
|
32
|
+
|
33
|
+
MakeOpenFile(self, fp);
|
34
|
+
fp->fd = fd;
|
35
|
+
fp->mode = FMODE_READABLE|FMODE_BINMODE;
|
36
|
+
rb_io_ascii8bit_binmode(self);
|
37
|
+
|
38
|
+
/**
|
39
|
+
* It's also using GC mark.
|
40
|
+
* So, I don't know other way how to GC guard io objects.
|
41
|
+
*/
|
42
|
+
rb_ivar_set(self, rb_intern("@evlist"), rb_ary_new());
|
43
|
+
return self;
|
44
|
+
}
|
45
|
+
|
46
|
+
static VALUE
|
47
|
+
rb_epoll_ctl(int argc, VALUE *argv, VALUE self)
|
48
|
+
{
|
49
|
+
struct epoll_event ev;
|
50
|
+
VALUE flag;
|
51
|
+
VALUE io;
|
52
|
+
VALUE events;
|
53
|
+
VALUE evlist;
|
54
|
+
rb_io_t *fptr;
|
55
|
+
rb_io_t *fptr_io;
|
56
|
+
int fd;
|
57
|
+
|
58
|
+
fptr = RFILE(self)->fptr;
|
59
|
+
rb_io_check_initialized(fptr);
|
60
|
+
|
61
|
+
switch (rb_scan_args(argc, argv, "21", &flag, &io, &events)) {
|
62
|
+
case 2:
|
63
|
+
if (FIX2INT(flag) != EPOLL_CTL_DEL)
|
64
|
+
rb_raise(rb_eArgError, "too few argument for CTL_ADD or CTL_MOD");
|
65
|
+
break;
|
66
|
+
case 3:
|
67
|
+
if (FIX2INT(flag) == EPOLL_CTL_DEL)
|
68
|
+
rb_raise(rb_eArgError, "too many argument for CTL_DEL");
|
69
|
+
|
70
|
+
if ((FIX2LONG(events) & (EPOLLIN|EPOLLPRI|EPOLLRDHUP|EPOLLOUT|EPOLLET|EPOLLONESHOT)) == 0)
|
71
|
+
rb_raise(rb_eIOError, "undefined events");
|
72
|
+
|
73
|
+
ev.events = FIX2LONG(events);
|
74
|
+
ev.data.ptr = (void*)io;
|
75
|
+
break;
|
76
|
+
}
|
77
|
+
|
78
|
+
GetOpenFile(rb_io_get_io(io), fptr_io);
|
79
|
+
fd = fptr_io->fd;
|
80
|
+
|
81
|
+
if (epoll_ctl(fptr->fd, FIX2INT(flag), fd, &ev) == -1) {
|
82
|
+
char buf[128];
|
83
|
+
sprintf(buf, "epoll_ctl(2) was failed(epoll fd:%d, io fd:%d)", fptr->fd, fd);
|
84
|
+
rb_sys_fail(buf);
|
85
|
+
}
|
86
|
+
|
87
|
+
switch (FIX2INT(flag)) {
|
88
|
+
case EPOLL_CTL_ADD:
|
89
|
+
evlist = rb_ivar_get(self, rb_intern("@evlist"));
|
90
|
+
rb_ary_push(evlist, io);
|
91
|
+
rb_ivar_set(self, rb_intern("@evlist"), evlist);
|
92
|
+
break;
|
93
|
+
case EPOLL_CTL_DEL:
|
94
|
+
evlist = rb_ivar_get(self, rb_intern("@evlist"));
|
95
|
+
rb_ary_delete(evlist, io);
|
96
|
+
rb_ivar_set(self, rb_intern("@evlist"), evlist);
|
97
|
+
break;
|
98
|
+
}
|
99
|
+
|
100
|
+
return self;
|
101
|
+
}
|
102
|
+
|
103
|
+
struct epoll_wait_args {
|
104
|
+
int fd;
|
105
|
+
int ev_len;
|
106
|
+
struct epoll_event *evlist;
|
107
|
+
int timeout;
|
108
|
+
};
|
109
|
+
|
110
|
+
static void *
|
111
|
+
rb_epoll_wait_func(void *ptr)
|
112
|
+
{
|
113
|
+
const struct epoll_wait_args *data = ptr;
|
114
|
+
return (void*)(long)epoll_wait(data->fd, data->evlist, data->ev_len, data->timeout);
|
115
|
+
}
|
116
|
+
|
117
|
+
static VALUE
|
118
|
+
rb_epoll_wait(int argc, VALUE *argv, VALUE self)
|
119
|
+
{
|
120
|
+
struct epoll_event evlist[EPOLL_WAIT_MAX_EVENTS];
|
121
|
+
struct epoll_wait_args data;
|
122
|
+
int i, ready, timeout, ev_len;
|
123
|
+
VALUE ready_evlist;
|
124
|
+
VALUE event;
|
125
|
+
rb_io_t *fptr;
|
126
|
+
GetOpenFile(self, fptr);
|
127
|
+
|
128
|
+
switch (argc) {
|
129
|
+
case 0:
|
130
|
+
timeout = -1;
|
131
|
+
break;
|
132
|
+
case 1:
|
133
|
+
timeout = FIX2INT(argv[0]);
|
134
|
+
break;
|
135
|
+
default:
|
136
|
+
rb_raise(rb_eArgError, "too many argument");
|
137
|
+
break;
|
138
|
+
}
|
139
|
+
|
140
|
+
ev_len = RARRAY_LEN(rb_ivar_get(self, rb_intern("@evlist")));
|
141
|
+
if (ev_len <= 0)
|
142
|
+
rb_raise(rb_eIOError, "empty interest list");
|
143
|
+
|
144
|
+
data.fd = fptr->fd;
|
145
|
+
data.ev_len = ev_len < EPOLL_WAIT_MAX_EVENTS ? ev_len : EPOLL_WAIT_MAX_EVENTS;
|
146
|
+
data.evlist = evlist;
|
147
|
+
data.timeout = timeout;
|
148
|
+
|
149
|
+
RETRY:
|
150
|
+
ready = (int)(long)rb_thread_call_without_gvl(rb_epoll_wait_func, &data, RUBY_UBF_IO, 0);
|
151
|
+
if (ready == -1) {
|
152
|
+
if (errno == EINTR)
|
153
|
+
goto RETRY;
|
154
|
+
else
|
155
|
+
rb_sys_fail("epoll_wait(2) was failed");
|
156
|
+
}
|
157
|
+
|
158
|
+
ready_evlist = rb_ary_new_capa(ready);
|
159
|
+
for (i = 0; i < ready; i++) {
|
160
|
+
event = rb_obj_alloc(cEpoll_Event);
|
161
|
+
RSTRUCT_SET(event, 0, (VALUE) evlist[i].data.ptr);
|
162
|
+
RSTRUCT_SET(event, 1, LONG2FIX(evlist[i].events));
|
163
|
+
rb_ary_store(ready_evlist, i, event);
|
164
|
+
}
|
165
|
+
return ready_evlist;
|
166
|
+
}
|
167
|
+
|
168
|
+
#endif // HAVE_SYS_EPOLL_H
|
169
|
+
|
170
|
+
void
|
171
|
+
Init_core()
|
172
|
+
{
|
173
|
+
#ifdef HAVE_SYS_EPOLL_H
|
174
|
+
cEpoll = rb_define_class("Epoll", rb_cIO);
|
175
|
+
rb_define_method(cEpoll, "initialize", rb_epoll_initialize, 0);
|
176
|
+
rb_define_method(cEpoll, "ctl", rb_epoll_ctl, -1);
|
177
|
+
rb_define_method(cEpoll, "wait", rb_epoll_wait, -1);
|
178
|
+
|
179
|
+
cEpoll_Constants = rb_define_module_under(cEpoll, "Constants");
|
180
|
+
rb_define_const(cEpoll_Constants, "IN", INT2FIX(EPOLLIN));
|
181
|
+
rb_define_const(cEpoll_Constants, "PRI", INT2FIX(EPOLLPRI));
|
182
|
+
rb_define_const(cEpoll_Constants, "RDHUP", INT2FIX(EPOLLRDHUP));
|
183
|
+
rb_define_const(cEpoll_Constants, "OUT", INT2FIX(EPOLLOUT));
|
184
|
+
rb_define_const(cEpoll_Constants, "ET", INT2FIX(EPOLLET));
|
185
|
+
rb_define_const(cEpoll_Constants, "ONESHOT", INT2FIX(EPOLLONESHOT));
|
186
|
+
rb_define_const(cEpoll_Constants, "ERR", INT2FIX(EPOLLERR));
|
187
|
+
rb_define_const(cEpoll_Constants, "HUP", INT2FIX(EPOLLHUP));
|
188
|
+
rb_define_const(cEpoll_Constants, "CTL_ADD", INT2FIX(EPOLL_CTL_ADD));
|
189
|
+
rb_define_const(cEpoll_Constants, "CTL_MOD", INT2FIX(EPOLL_CTL_MOD));
|
190
|
+
rb_define_const(cEpoll_Constants, "CTL_DEL", INT2FIX(EPOLL_CTL_DEL));
|
191
|
+
|
192
|
+
cEpoll_Event = rb_struct_define_under(cEpoll, "Event", "data", "events", NULL);
|
193
|
+
#endif
|
194
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
if !have_header("sys/epoll.h")
|
4
|
+
puts "[31m*** complie error: gem 'epoll' must be installed <sys/epoll.h>. ***[m"
|
5
|
+
puts "[31m*** you can require 'io/epoll'. But, you can not use IO::Epoll APIs. ***[m"
|
6
|
+
end
|
7
|
+
have_func("epoll_create1", "sys/epoll.h")
|
8
|
+
create_makefile('epoll/core')
|
data/lib/epoll.rb
ADDED
data/lib/epoll/ext.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
class Epoll
|
2
|
+
attr_accessor :evlist
|
3
|
+
|
4
|
+
include Epoll::Constants
|
5
|
+
|
6
|
+
class << self
|
7
|
+
alias create open
|
8
|
+
end
|
9
|
+
|
10
|
+
def size
|
11
|
+
@evlist.size
|
12
|
+
end
|
13
|
+
alias length size
|
14
|
+
|
15
|
+
def add(io, events)
|
16
|
+
ctl CTL_ADD, io, events
|
17
|
+
end
|
18
|
+
|
19
|
+
def mod(io, events)
|
20
|
+
ctl CTL_MOD, io, events
|
21
|
+
end
|
22
|
+
|
23
|
+
def del(io)
|
24
|
+
ctl CTL_DEL, io
|
25
|
+
end
|
26
|
+
end
|
data/test/test_epoll.rb
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'timeout'
|
3
|
+
require_relative '../lib/epoll'
|
4
|
+
|
5
|
+
class TestIOEpoll < Test::Unit::TestCase
|
6
|
+
def test_initalize
|
7
|
+
assert_instance_of(Epoll, Epoll.new);
|
8
|
+
assert_instance_of(Epoll, Epoll.create);
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_create_with_block
|
12
|
+
instance = nil
|
13
|
+
ret_val = Epoll.create do |ep|
|
14
|
+
instance = ep
|
15
|
+
assert { instance.closed? == false }
|
16
|
+
:block_end
|
17
|
+
end
|
18
|
+
assert { instance.kind_of?(Epoll) == true }
|
19
|
+
assert { instance.closed? == true }
|
20
|
+
assert { ret_val == :block_end }
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_create_with_block_ensure_close
|
24
|
+
instance = nil
|
25
|
+
catch do |ok|
|
26
|
+
Epoll.create do |ep|
|
27
|
+
instance = ep
|
28
|
+
throw ok
|
29
|
+
end
|
30
|
+
end
|
31
|
+
assert { true == instance.closed? }
|
32
|
+
|
33
|
+
assert_nothing_raised do
|
34
|
+
Epoll.create do |ep|
|
35
|
+
ep.close
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_inspect
|
41
|
+
Epoll.create do |ep|
|
42
|
+
fd = ep.fileno
|
43
|
+
assert { "#<Epoll:fd #{fd}>" == ep.inspect }
|
44
|
+
ep.close
|
45
|
+
assert { "#<Epoll:(closed)>" == ep.inspect }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_fileno
|
50
|
+
ep = Epoll.create
|
51
|
+
assert { 0 < ep.fileno }
|
52
|
+
ep.close
|
53
|
+
assert_raise(IOError) { ep.fileno }
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_ctl
|
57
|
+
Epoll.create do |ep|
|
58
|
+
io = IO.new(1, 'w')
|
59
|
+
assert { ep == ep.ctl(Epoll::CTL_ADD, io , Epoll::OUT) }
|
60
|
+
assert_raise(ArgumentError) { ep.ctl }
|
61
|
+
assert_raise(ArgumentError) { ep.ctl(Epoll::CTL_ADD) }
|
62
|
+
assert_raise(ArgumentError) { ep.ctl(Epoll::CTL_ADD, io) }
|
63
|
+
assert_raise(ArgumentError) { ep.ctl(-1, io) }
|
64
|
+
assert_raise(TypeError) { ep.ctl(nil, nil, nil) }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_add
|
69
|
+
ep = Epoll.create
|
70
|
+
io = IO.new(1, 'w')
|
71
|
+
assert_raise(IOError) { ep.add(io, 0) }
|
72
|
+
assert { ep == ep.add(io, Epoll::IN|Epoll::PRI|Epoll::RDHUP|Epoll::ET|Epoll::OUT) }
|
73
|
+
ep.close
|
74
|
+
assert_raise(Errno::EBADF) { ep.add(io, Epoll::OUT) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_mod
|
78
|
+
ep = Epoll.create
|
79
|
+
io = IO.new(1, 'w')
|
80
|
+
assert_raise(Errno::ENOENT) { ep.mod(io, Epoll::IN) }
|
81
|
+
ep == ep.add(io, Epoll::OUT)
|
82
|
+
assert { ep == ep.mod(io, Epoll::IN) }
|
83
|
+
ep.close
|
84
|
+
assert_raise(Errno::EBADF) { ep.add(io, Epoll::OUT) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_del
|
88
|
+
ep = Epoll.create
|
89
|
+
io = IO.new(1, 'w')
|
90
|
+
assert_raise(Errno::ENOENT) { ep.del(io) }
|
91
|
+
ep.add(io, Epoll::OUT)
|
92
|
+
assert { ep == ep.del(io) }
|
93
|
+
ep.close
|
94
|
+
assert_raise(Errno::EBADF) { ep.del(io) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_wait
|
98
|
+
Epoll.create do |ep|
|
99
|
+
IO.pipe do |r, w|
|
100
|
+
ep.add(r, Epoll::IN)
|
101
|
+
ep.add(w, Epoll::OUT)
|
102
|
+
evlist = ep.wait
|
103
|
+
assert { [Epoll::Event.new(w, Epoll::OUT)] == evlist }
|
104
|
+
|
105
|
+
w.write('ok')
|
106
|
+
assert { 2 == ep.wait.length }
|
107
|
+
|
108
|
+
assert_raise(IOError) { Epoll.create.wait }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_wait_with_timeout
|
114
|
+
Epoll.create do |ep|
|
115
|
+
io = IO.new(1, 'w')
|
116
|
+
ep.add(io, Epoll::IN)
|
117
|
+
assert { [] == ep.wait(0) }
|
118
|
+
assert { [] == ep.wait(1) }
|
119
|
+
assert_raise(TimeoutError) do
|
120
|
+
timeout(0.01) { ep.wait(-1) }
|
121
|
+
end
|
122
|
+
assert_raise(TypeError) { ep.wait(nil) }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_size
|
127
|
+
Epoll.create do |ep|
|
128
|
+
IO.pipe do |r, w|
|
129
|
+
ep.add(r, Epoll::IN)
|
130
|
+
assert { 1 == ep.size }
|
131
|
+
ep.add(w, Epoll::OUT)
|
132
|
+
assert { 2 == ep.size }
|
133
|
+
ep.del(w)
|
134
|
+
assert { 1 == ep.size }
|
135
|
+
ep.del(r)
|
136
|
+
assert { 0 == ep.size }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_close
|
142
|
+
assert_nothing_raised do
|
143
|
+
fileno = nil
|
144
|
+
10.times do
|
145
|
+
ep = Epoll.create
|
146
|
+
fileno = ep.fileno unless fileno
|
147
|
+
assert { fileno == ep.fileno }
|
148
|
+
ep.close
|
149
|
+
|
150
|
+
Epoll.create do
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
ep = Epoll.create
|
156
|
+
ep.close
|
157
|
+
assert_raise(IOError){ ep.close }
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_closed?
|
161
|
+
ep = Epoll.create
|
162
|
+
assert { false == ep.closed? }
|
163
|
+
ep.close
|
164
|
+
assert { true == ep.closed? }
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_dup
|
168
|
+
Epoll.create do |ep|
|
169
|
+
IO.pipe do |r, w|
|
170
|
+
ep.add(r, Epoll::IN)
|
171
|
+
ep.add(w, Epoll::OUT)
|
172
|
+
dup = ep.dup
|
173
|
+
assert { ep != dup }
|
174
|
+
assert { ep.fileno != dup.fileno }
|
175
|
+
assert { 2 == dup.size }
|
176
|
+
assert { 1 == dup.wait.length }
|
177
|
+
w.write 'ok'
|
178
|
+
assert { 2 == dup.wait.length }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_close_on_exec
|
184
|
+
if defined? Fcntl::FD_CLOEXEC
|
185
|
+
Epoll.create do |ep|
|
186
|
+
assert { true == ep.close_on_exec? }
|
187
|
+
ep.close_on_exec = false
|
188
|
+
assert { false == ep.close_on_exec? }
|
189
|
+
ep.close_on_exec = true
|
190
|
+
assert { true == ep.close_on_exec? }
|
191
|
+
ep.close_on_exec = false
|
192
|
+
assert { false == ep.close_on_exec? }
|
193
|
+
ep.close
|
194
|
+
assert_raise { ep.close_on_exec = true }
|
195
|
+
assert_raise { ep.close_on_exec? }
|
196
|
+
end
|
197
|
+
else
|
198
|
+
pend "Fcntl::FD_CLOEXEC undefined"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_thread
|
203
|
+
Epoll.create do |ep|
|
204
|
+
io = IO.new(1, 'w')
|
205
|
+
ep.add(io, Epoll::OUT)
|
206
|
+
ret = nil
|
207
|
+
Thread.start {
|
208
|
+
ret = ep.wait
|
209
|
+
}.join
|
210
|
+
assert { io == ret[0].data }
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: epoll
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ksss
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake-compiler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: test-unit
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A Ruby binding for epoll(7)
|
70
|
+
email:
|
71
|
+
- co000ri@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions:
|
74
|
+
- ext/epoll/extconf.rb
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- Gemfile.lock
|
81
|
+
- LICENSE.txt
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- epoll.gemspec
|
85
|
+
- examples/epoll_server.rb
|
86
|
+
- ext/epoll/core.c
|
87
|
+
- ext/epoll/extconf.rb
|
88
|
+
- lib/epoll.rb
|
89
|
+
- lib/epoll/ext.rb
|
90
|
+
- test/test_epoll.rb
|
91
|
+
homepage: https://github.com/ksss/epoll
|
92
|
+
licenses:
|
93
|
+
- MIT
|
94
|
+
metadata: {}
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 2.2.2
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: A Ruby binding for epoll(7)
|
115
|
+
test_files:
|
116
|
+
- test/test_epoll.rb
|