kqueue 0.0.1
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 +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +143 -0
- data/Rakefile +9 -0
- data/examples/kqueue_server.rb +76 -0
- data/ext/kqueue/core.c +238 -0
- data/ext/kqueue/extconf.rb +10 -0
- data/kqueue.gemspec +21 -0
- data/test/test_kqueue.rb +149 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 06a7b8bdf52804dcef98ac5f11bcf3164ef7d334
|
4
|
+
data.tar.gz: d0151bed17fd2237b52b9d556c999e1005a3704e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc8b761373f7eb7228e4c1917112bf984c43dc4e31a7e99c649013b419e633535818d9a93692dd3884e5fb017bb11346c758447bb7a16f5c33a5cef6d3b159cb
|
7
|
+
data.tar.gz: 1ec0ad58c3c8f5c748c2d4d94d32a18444736299809ad47a60b065527a697b6797cfb6cf3a23bb741bc1d3138e1d9ab84295ece794073dc23f9e024df652d73d
|
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
|
+
kqueue (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
power_assert (0.2.2)
|
10
|
+
rake (10.4.0)
|
11
|
+
rake-compiler (0.9.3)
|
12
|
+
rake
|
13
|
+
test-unit (3.0.7)
|
14
|
+
power_assert
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
ruby
|
18
|
+
|
19
|
+
DEPENDENCIES
|
20
|
+
bundler
|
21
|
+
kqueue!
|
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,143 @@
|
|
1
|
+
kqueue
|
2
|
+
===
|
3
|
+
|
4
|
+
[](https://travis-ci.org/ksss/kqueue)
|
5
|
+
|
6
|
+
A binding of kqueue on Ruby.
|
7
|
+
|
8
|
+
**kqueue** can use BSD system only. (because must be installed sys/event.h)
|
9
|
+
|
10
|
+
# Usage
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require 'kqueue'
|
14
|
+
|
15
|
+
# Inheritance
|
16
|
+
# class Kqueue < IO
|
17
|
+
# # ident: file descriptor identifier for this event
|
18
|
+
# # filter: filter for event(just one)
|
19
|
+
# # flags: general flags(complex fragment)
|
20
|
+
# # fflags: filter-specific flags(complex fragment)
|
21
|
+
# # data: filter-specific data
|
22
|
+
# # udata: opaque user data(can set any object)
|
23
|
+
# class Event < Struct.new(:ident, :filter, :flags, :fflags, :data, :udata)
|
24
|
+
# # Event::Constants include all kqueue constants
|
25
|
+
# include Event::Constants
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
|
29
|
+
# create kqueue object and ensure close
|
30
|
+
Kqueue.open do |kq|
|
31
|
+
# ...
|
32
|
+
end
|
33
|
+
|
34
|
+
# or
|
35
|
+
begin
|
36
|
+
kq = Kqueue.new
|
37
|
+
# ...
|
38
|
+
ensure
|
39
|
+
kq.close
|
40
|
+
end
|
41
|
+
|
42
|
+
# watch event
|
43
|
+
IO.pipe do |r, w|
|
44
|
+
Kqueue.open do |kq|
|
45
|
+
kev = Kqueue::Event.new(r.fileno, EVFILT_READ, EV_ADD, 0, 0, proc {
|
46
|
+
puts "you can read #{r.fileno} with non block!"
|
47
|
+
})
|
48
|
+
# set a event
|
49
|
+
kq.kevent([kev], 0)
|
50
|
+
|
51
|
+
# wait a event
|
52
|
+
# wait until event occur
|
53
|
+
kq.kevent(nil, 1)
|
54
|
+
|
55
|
+
# wait 0.1 sec
|
56
|
+
kq.kevent(nil, 1, 0.1) #=> []
|
57
|
+
|
58
|
+
w.write 'ok go'
|
59
|
+
w.flush
|
60
|
+
|
61
|
+
# get a notified event
|
62
|
+
kevs = kq.kevent(nil, 1) #=> [#<a Event object>]
|
63
|
+
|
64
|
+
# udata have one any object
|
65
|
+
kevs.first.udata.call #=> you can read 7 with non block!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
## filter
|
71
|
+
|
72
|
+
filter|description
|
73
|
+
---|---
|
74
|
+
EVFILT_READ|on read
|
75
|
+
EVFILT_WRITE|on write
|
76
|
+
EVFILT_AIO|attached to aio requests
|
77
|
+
EVFILT_VNODE|attached to vnodes
|
78
|
+
EVFILT_PROC|attached to struct proc
|
79
|
+
EVFILT_SIGNAL|attached to signal
|
80
|
+
EVFILT_TIMER|timers
|
81
|
+
EVFILT_MACHPORT|Mach portsets
|
82
|
+
|
83
|
+
## flags
|
84
|
+
|
85
|
+
flags|description
|
86
|
+
---|---
|
87
|
+
EV_ADD|add event to kq (implies enable)
|
88
|
+
EV_DELETE|delete event from kq
|
89
|
+
EV_ENABLE|enable event
|
90
|
+
EV_DISABLE|disable event (not reported)
|
91
|
+
EV_ONESHOT|only report one occurrence
|
92
|
+
EV_CLEAR|clear event state after reporting
|
93
|
+
EV_RECEIPT|force EV_ERROR on success, data == 0
|
94
|
+
EV_ERROR|error, data contains errno(returned value)
|
95
|
+
EV_EOF|EOF detected(returned value)
|
96
|
+
|
97
|
+
# fflags
|
98
|
+
|
99
|
+
fflags|description
|
100
|
+
---|---
|
101
|
+
NOTE_DELETE|vnode was removed
|
102
|
+
NOTE_WRITE|data contents changed
|
103
|
+
NOTE_EXTEND|size increased
|
104
|
+
NOTE_ATTRIB|attributes changed
|
105
|
+
NOTE_LINK|link count changed
|
106
|
+
NOTE_RENAME|vnode was renamed
|
107
|
+
NOTE_REVOKE|vnode access was revoked
|
108
|
+
NOTE_EXIT|process exited
|
109
|
+
NOTE_EXITSTATUS|exit status to be returned, valid for child process only
|
110
|
+
NOTE_FORK|process forked
|
111
|
+
NOTE_EXEC|process exec'd
|
112
|
+
NOTE_SIGNAL|shared with EVFILT_SIGNAL
|
113
|
+
NOTE_REAP|process reaped
|
114
|
+
|
115
|
+
## Installation
|
116
|
+
|
117
|
+
Add this line to your application's Gemfile:
|
118
|
+
|
119
|
+
gem 'kqueue'
|
120
|
+
|
121
|
+
And then execute:
|
122
|
+
|
123
|
+
$ bundle
|
124
|
+
|
125
|
+
Or install it yourself as:
|
126
|
+
|
127
|
+
$ gem install kqueue
|
128
|
+
|
129
|
+
# Pro Tips
|
130
|
+
|
131
|
+
- Support call without GVL in CRuby (use rb\_thread\_call\_without\_gvl())
|
132
|
+
|
133
|
+
# Fork Me !
|
134
|
+
|
135
|
+
This is experimental implementation.
|
136
|
+
I'm waiting for your idea and Pull Request !
|
137
|
+
|
138
|
+
# see also
|
139
|
+
|
140
|
+
- man kqueue
|
141
|
+
- http://people.freebsd.org/~jlemon/papers/kqueue_freenix.pdf
|
142
|
+
- https://www.freebsd.org/cgi/man.cgi?query=kqueue
|
143
|
+
- your /usr/include/sys/event.h
|
data/Rakefile
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'kqueue'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
class KqServer
|
7
|
+
include Kqueue::Event::Constants
|
8
|
+
|
9
|
+
def self.start(port)
|
10
|
+
kqs = new(port)
|
11
|
+
yield kqs
|
12
|
+
loop do
|
13
|
+
kqs.wait.each do |ev|
|
14
|
+
ev.udata && ev.udata[1].call(ev.udata[0])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(port)
|
20
|
+
@kq = Kqueue.new
|
21
|
+
@server = TCPServer.open(port)
|
22
|
+
set_event @server, EVFILT_READ do
|
23
|
+
socket = @server.accept
|
24
|
+
set_event socket, EVFILT_READ do |socket|
|
25
|
+
loop do
|
26
|
+
chunk = socket.recv(1024)
|
27
|
+
@on_data && @on_data.call(chunk)
|
28
|
+
break if chunk.length < 4 || chunk[-4..-1] == "\r\n\r\n"
|
29
|
+
end
|
30
|
+
set_event socket, EVFILT_WRITE do |socket|
|
31
|
+
@on_end && @on_end.call(socket)
|
32
|
+
del_event(socket, EVFILT_READ)
|
33
|
+
del_event(socket, EVFILT_WRITE)
|
34
|
+
socket.close
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_event(io, filter, &block)
|
41
|
+
@kq.kevent([Kqueue::Event.new(io.fileno, filter, EV_ADD, 0, 0, [io, block])], 0)
|
42
|
+
end
|
43
|
+
|
44
|
+
def del_event(io, filter)
|
45
|
+
@kq.kevent([Kqueue::Event.new(io.fileno, filter, EV_DELETE, 0, 0, nil)], 0)
|
46
|
+
end
|
47
|
+
|
48
|
+
def wait
|
49
|
+
@kq.kevent(nil, 128, 5)
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_data(&block)
|
53
|
+
@on_data = block
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_end(&block)
|
57
|
+
@on_end = block
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
puts "run http://127.0.0.1:4000/"
|
62
|
+
|
63
|
+
KqServer.start(4000) do |s|
|
64
|
+
s.on_data do |chunk|
|
65
|
+
print chunk
|
66
|
+
end
|
67
|
+
s.on_end do |socket|
|
68
|
+
socket.write [
|
69
|
+
"HTTP/1.0 200 OK\r\n",
|
70
|
+
"Content-Length: 11\r\n",
|
71
|
+
"Content-Type: text/html\r\n",
|
72
|
+
"\r\n",
|
73
|
+
"Hello World\r\n",
|
74
|
+
].join("")
|
75
|
+
end
|
76
|
+
end
|
data/ext/kqueue/core.c
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "ruby/io.h"
|
3
|
+
#include "ruby/thread.h"
|
4
|
+
|
5
|
+
#if defined(HAVE_SYS_TYPES_H) && defined(HAVE_SYS_EVENT_H) && defined(HAVE_SYS_TIME_H)
|
6
|
+
|
7
|
+
#include <fcntl.h>
|
8
|
+
#include <alloca.h>
|
9
|
+
#include <sys/types.h>
|
10
|
+
#include <sys/event.h>
|
11
|
+
#include <sys/time.h>
|
12
|
+
|
13
|
+
VALUE cKqueue;
|
14
|
+
VALUE mKqueue_Event_Constants;
|
15
|
+
VALUE cKqueue_Event;
|
16
|
+
ID id_udata_marks;
|
17
|
+
|
18
|
+
#define IVAR_GET(self) rb_ivar_get((self), id_udata_marks)
|
19
|
+
#define IVAR_SET(self, obj) rb_ivar_set((self), id_udata_marks, (obj))
|
20
|
+
|
21
|
+
enum enumEvents {
|
22
|
+
IDENT,
|
23
|
+
FILTER,
|
24
|
+
FLAGS,
|
25
|
+
FFLAGS,
|
26
|
+
DATA,
|
27
|
+
UDATA
|
28
|
+
};
|
29
|
+
|
30
|
+
static VALUE
|
31
|
+
rb_kqueue_initialize(VALUE self)
|
32
|
+
{
|
33
|
+
rb_io_t *fp;
|
34
|
+
int fd;
|
35
|
+
|
36
|
+
fd = kqueue();
|
37
|
+
if (fd == -1)
|
38
|
+
rb_sys_fail("kqueue");
|
39
|
+
rb_update_max_fd(fd);
|
40
|
+
|
41
|
+
MakeOpenFile(self, fp);
|
42
|
+
fp->fd = fd;
|
43
|
+
fp->mode = FMODE_READABLE|FMODE_BINMODE;
|
44
|
+
rb_io_ascii8bit_binmode(self);
|
45
|
+
|
46
|
+
IVAR_SET(self, rb_hash_new());
|
47
|
+
|
48
|
+
return self;
|
49
|
+
}
|
50
|
+
|
51
|
+
struct kqueue_kevent_args {
|
52
|
+
int fd;
|
53
|
+
struct kevent *chl;
|
54
|
+
int ch_len;
|
55
|
+
struct kevent *evl;
|
56
|
+
int ev_len;
|
57
|
+
struct timespec *ts;
|
58
|
+
};
|
59
|
+
|
60
|
+
static void *
|
61
|
+
rb_kqueue_kevent_func(void *ptr)
|
62
|
+
{
|
63
|
+
const struct kqueue_kevent_args *args = ptr;
|
64
|
+
return (void*)(long)kevent(args->fd, args->chl, args->ch_len, args->evl, args->ev_len, args->ts);
|
65
|
+
}
|
66
|
+
|
67
|
+
static VALUE
|
68
|
+
rb_kqueue_kevent(int argc, VALUE *argv, VALUE self)
|
69
|
+
{
|
70
|
+
VALUE ch_list, max_events, timeout, ev, ready_evlist;
|
71
|
+
struct kqueue_kevent_args args;
|
72
|
+
struct timespec ts;
|
73
|
+
struct timespec *pts = NULL;
|
74
|
+
struct timeval tv;
|
75
|
+
int i;
|
76
|
+
int ev_len = 0, ch_len = 0;
|
77
|
+
int ready;
|
78
|
+
struct kevent *chl = NULL, *evl = NULL;
|
79
|
+
rb_io_t *fptr, *fptr_io;
|
80
|
+
|
81
|
+
fptr = RFILE(self)->fptr;
|
82
|
+
rb_io_check_initialized(fptr);
|
83
|
+
|
84
|
+
rb_scan_args(argc, argv, "21", &ch_list, &max_events, &timeout);
|
85
|
+
if (!NIL_P(max_events)) {
|
86
|
+
ev_len = FIX2INT(max_events);
|
87
|
+
}
|
88
|
+
|
89
|
+
if (!NIL_P(timeout)) {
|
90
|
+
tv = rb_time_timeval(timeout);
|
91
|
+
ts.tv_sec = (time_t)tv.tv_sec;
|
92
|
+
ts.tv_nsec = (long)tv.tv_usec * 1000;
|
93
|
+
pts = &ts;
|
94
|
+
}
|
95
|
+
|
96
|
+
if (!NIL_P(ch_list)) {
|
97
|
+
Check_Type(ch_list, T_ARRAY);
|
98
|
+
ch_len = RARRAY_LEN(ch_list);
|
99
|
+
|
100
|
+
chl = alloca(sizeof(struct kevent) * ch_len);
|
101
|
+
for (i = 0; i < ch_len; i++) {
|
102
|
+
ev = RARRAY_AREF(ch_list, i);
|
103
|
+
if (!rb_obj_is_kind_of(ev, cKqueue_Event)) {
|
104
|
+
rb_raise(rb_eTypeError, "must be set Array of Event object");
|
105
|
+
}
|
106
|
+
|
107
|
+
chl[i].ident = FIX2INT(RSTRUCT_GET(ev, IDENT));
|
108
|
+
chl[i].filter = FIX2INT(RSTRUCT_GET(ev, FILTER));
|
109
|
+
chl[i].flags = FIX2UINT(RSTRUCT_GET(ev, FLAGS));
|
110
|
+
chl[i].fflags = FIX2UINT(RSTRUCT_GET(ev, FFLAGS));
|
111
|
+
chl[i].data = NUM2LONG(RSTRUCT_GET(ev, DATA));
|
112
|
+
{
|
113
|
+
VALUE fileno = RSTRUCT_GET(ev, IDENT);
|
114
|
+
VALUE filter = RSTRUCT_GET(ev, FILTER);
|
115
|
+
VALUE udata = RSTRUCT_GET(ev, UDATA);
|
116
|
+
VALUE ivar = IVAR_GET(self);
|
117
|
+
VALUE h = rb_hash_aref(ivar, fileno);
|
118
|
+
if ((chl[i].flags & EV_DELETE) == EV_DELETE) {
|
119
|
+
if (chl[i].flags & ~EV_DELETE) {
|
120
|
+
rb_raise(rb_eArgError, "EV_DELETE cannot set with other");
|
121
|
+
}
|
122
|
+
if (NIL_P(h)) {
|
123
|
+
rb_raise(rb_eArgError, "delete udata not found");
|
124
|
+
}
|
125
|
+
rb_hash_delete(h, filter);
|
126
|
+
if (RHASH_EMPTY_P(h)) {
|
127
|
+
rb_hash_delete(ivar, fileno);
|
128
|
+
}
|
129
|
+
udata = (VALUE)0;
|
130
|
+
}
|
131
|
+
else {
|
132
|
+
if (NIL_P(h)) {
|
133
|
+
h = rb_hash_aset(ivar, fileno, rb_hash_new());
|
134
|
+
}
|
135
|
+
rb_hash_aset(h, filter, udata);
|
136
|
+
}
|
137
|
+
IVAR_SET(self, ivar);
|
138
|
+
chl[i].udata = (void*)udata;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
if (0 < ev_len) {
|
144
|
+
evl = alloca(sizeof(struct kevent) * ev_len);
|
145
|
+
}
|
146
|
+
else if (ev_len < 0) {
|
147
|
+
rb_raise(rb_eArgError, "negative size");
|
148
|
+
}
|
149
|
+
|
150
|
+
args.fd = fptr->fd;
|
151
|
+
args.chl = chl;
|
152
|
+
args.ch_len = ch_len;
|
153
|
+
args.evl = evl;
|
154
|
+
args.ev_len = ev_len;
|
155
|
+
args.ts = pts;
|
156
|
+
|
157
|
+
RETRY:
|
158
|
+
ready = (int)(long)rb_thread_call_without_gvl(rb_kqueue_kevent_func, &args, RUBY_UBF_IO, 0);
|
159
|
+
if (ready == -1) {
|
160
|
+
if (errno == EINTR)
|
161
|
+
goto RETRY;
|
162
|
+
else
|
163
|
+
rb_sys_fail("kevent");
|
164
|
+
}
|
165
|
+
|
166
|
+
ready_evlist = rb_ary_new_capa(ready);
|
167
|
+
for (i = 0; i < ready; i++) {
|
168
|
+
ev = rb_obj_alloc(cKqueue_Event);
|
169
|
+
RSTRUCT_SET(ev, IDENT, INT2FIX(evl[i].ident));
|
170
|
+
RSTRUCT_SET(ev, FILTER, INT2FIX(evl[i].filter));
|
171
|
+
RSTRUCT_SET(ev, FLAGS, INT2FIX(evl[i].flags));
|
172
|
+
RSTRUCT_SET(ev, FFLAGS, INT2FIX(evl[i].fflags));
|
173
|
+
RSTRUCT_SET(ev, FLAGS, INT2FIX(evl[i].flags));
|
174
|
+
RSTRUCT_SET(ev, DATA, INT2FIX(evl[i].data));
|
175
|
+
RSTRUCT_SET(ev, UDATA, (VALUE)evl[i].udata);
|
176
|
+
rb_ary_store(ready_evlist, i, ev);
|
177
|
+
}
|
178
|
+
return ready_evlist;
|
179
|
+
}
|
180
|
+
#endif
|
181
|
+
|
182
|
+
void
|
183
|
+
Init_kqueue()
|
184
|
+
{
|
185
|
+
#if defined(HAVE_SYS_TYPES_H) && defined(HAVE_SYS_EVENT_H) && defined(HAVE_SYS_TIME_H)
|
186
|
+
|
187
|
+
id_udata_marks = rb_intern("@udata_marks");
|
188
|
+
|
189
|
+
cKqueue = rb_define_class("Kqueue", rb_cIO);
|
190
|
+
rb_define_method(cKqueue, "initialize", rb_kqueue_initialize, 0);
|
191
|
+
rb_define_method(cKqueue, "kevent", rb_kqueue_kevent, -1);
|
192
|
+
|
193
|
+
cKqueue_Event = rb_struct_define_under(cKqueue, "Event", "ident", "filter", "flags", "fflags", "data", "udata", NULL);
|
194
|
+
|
195
|
+
mKqueue_Event_Constants = rb_define_module_under(cKqueue_Event, "Constants");
|
196
|
+
|
197
|
+
rb_include_module(cKqueue_Event, mKqueue_Event_Constants);
|
198
|
+
|
199
|
+
/* actions */
|
200
|
+
rb_define_const(mKqueue_Event_Constants, "EV_ADD", INT2FIX((u_short)EV_ADD));
|
201
|
+
rb_define_const(mKqueue_Event_Constants, "EV_ENABLE", INT2FIX((u_short)EV_ENABLE));
|
202
|
+
rb_define_const(mKqueue_Event_Constants, "EV_DISABLE", INT2FIX((u_short)EV_DISABLE));
|
203
|
+
rb_define_const(mKqueue_Event_Constants, "EV_DELETE", INT2FIX((u_short)EV_DELETE));
|
204
|
+
rb_define_const(mKqueue_Event_Constants, "EV_RECEIPT", INT2FIX((u_short)EV_RECEIPT));
|
205
|
+
/* flags */
|
206
|
+
rb_define_const(mKqueue_Event_Constants, "EV_ONESHOT", INT2FIX((u_short)EV_ONESHOT));
|
207
|
+
rb_define_const(mKqueue_Event_Constants, "EV_CLEAR", INT2FIX((u_short)EV_CLEAR));
|
208
|
+
/* returned values */
|
209
|
+
rb_define_const(mKqueue_Event_Constants, "EV_EOF", INT2FIX((u_short)EV_EOF));
|
210
|
+
rb_define_const(mKqueue_Event_Constants, "EV_ERROR", INT2FIX((u_short)EV_ERROR));
|
211
|
+
|
212
|
+
/* for filter */
|
213
|
+
rb_define_const(mKqueue_Event_Constants, "EVFILT_READ", INT2FIX((short)EVFILT_READ));
|
214
|
+
rb_define_const(mKqueue_Event_Constants, "EVFILT_WRITE", INT2FIX((short)EVFILT_WRITE));
|
215
|
+
rb_define_const(mKqueue_Event_Constants, "EVFILT_AIO", INT2FIX((short)EVFILT_AIO));
|
216
|
+
rb_define_const(mKqueue_Event_Constants, "EVFILT_VNODE", INT2FIX((short)EVFILT_VNODE));
|
217
|
+
rb_define_const(mKqueue_Event_Constants, "EVFILT_PROC", INT2FIX((short)EVFILT_PROC));
|
218
|
+
rb_define_const(mKqueue_Event_Constants, "EVFILT_SIGNAL", INT2FIX((short)EVFILT_SIGNAL));
|
219
|
+
rb_define_const(mKqueue_Event_Constants, "EVFILT_MACHPORT", INT2FIX((short)EVFILT_MACHPORT));
|
220
|
+
|
221
|
+
// for EVFILT_VNODE
|
222
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_DELETE", INT2FIX((u_int)NOTE_DELETE));
|
223
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_WRITE", INT2FIX((u_int)NOTE_WRITE));
|
224
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_EXTEND", INT2FIX((u_int)NOTE_EXTEND));
|
225
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_ATTRIB", INT2FIX((u_int)NOTE_ATTRIB));
|
226
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_LINK", INT2FIX((u_int)NOTE_LINK));
|
227
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_RENAME", INT2FIX((u_int)NOTE_RENAME));
|
228
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_REVOKE", INT2FIX((u_int)NOTE_REVOKE));
|
229
|
+
|
230
|
+
// for EVFILT_PROC
|
231
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_EXIT", INT2FIX((u_int)NOTE_EXIT));
|
232
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_EXITSTATUS", INT2FIX((u_int)NOTE_EXITSTATUS));
|
233
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_FORK", INT2FIX((u_int)NOTE_FORK));
|
234
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_EXEC", INT2FIX((u_int)NOTE_EXEC));
|
235
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_SIGNAL", INT2FIX((u_int)NOTE_SIGNAL));
|
236
|
+
rb_define_const(mKqueue_Event_Constants, "NOTE_REAP", INT2FIX((u_int)NOTE_REAP));
|
237
|
+
#endif
|
238
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
headers = %w(sys/types.h sys/event.h sys/time.h)
|
4
|
+
|
5
|
+
unless headers.all?{|h| have_header(h)}
|
6
|
+
puts "[31m*** complie error: gem 'kqueue' must be installed #{headers.to_s}. ***[m"
|
7
|
+
puts "[31m*** you can require 'kqueue'. But, you can not use Kqueue APIs. ***[m"
|
8
|
+
end
|
9
|
+
|
10
|
+
create_makefile('kqueue')
|
data/kqueue.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "kqueue"
|
3
|
+
spec.version = "0.0.1"
|
4
|
+
spec.authors = ["ksss"]
|
5
|
+
spec.email = ["co000ri@gmail.com"]
|
6
|
+
spec.summary = %q{A Ruby binding for kqueue(2)}
|
7
|
+
spec.description = %q{A Ruby binding for kqueue(2)}
|
8
|
+
spec.homepage = "https://github.com/ksss/kqueue"
|
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/kqueue/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
|
data/test/test_kqueue.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'timeout'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'kqueue'
|
5
|
+
|
6
|
+
class TestIOKqueue < Test::Unit::TestCase
|
7
|
+
include Kqueue::Event::Constants
|
8
|
+
|
9
|
+
def test_initalize
|
10
|
+
kq = Kqueue.new
|
11
|
+
assert_instance_of(Kqueue, kq)
|
12
|
+
assert_kind_of(IO, kq)
|
13
|
+
assert { true == kq.close_on_exec? }
|
14
|
+
kq.close
|
15
|
+
|
16
|
+
Kqueue.open do |kq|
|
17
|
+
assert_instance_of(Kqueue, kq)
|
18
|
+
assert_instance_of(Kqueue::Event, Kqueue::Event.new)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_kevent
|
23
|
+
Tempfile.open("test") do |f|
|
24
|
+
Kqueue.open do |kq|
|
25
|
+
kev_r = Kqueue::Event.new(f.fileno, EVFILT_READ, EV_ADD, 0, 0, nil)
|
26
|
+
kev_w = Kqueue::Event.new(f.fileno, EVFILT_WRITE, EV_ADD, 0, 0, nil)
|
27
|
+
assert { [] == kq.kevent([kev_r, kev_w], 0) }
|
28
|
+
assert { [] == kq.kevent(nil, 0) }
|
29
|
+
assert { [Kqueue::Event.new(f.fileno, EVFILT_WRITE, EV_ADD, 0, 1, nil)] == kq.kevent(nil, 2) }
|
30
|
+
assert { [Kqueue::Event.new(f.fileno, EVFILT_WRITE, EV_ADD, 0, 1, nil)] == kq.kevent(nil, 2) }
|
31
|
+
message = 'hello world'
|
32
|
+
f.write message
|
33
|
+
f.rewind
|
34
|
+
|
35
|
+
expect = [
|
36
|
+
Kqueue::Event.new(f.fileno, EVFILT_WRITE, EV_ADD, 0, 1, nil),
|
37
|
+
Kqueue::Event.new(f.fileno, EVFILT_READ, EV_ADD, 0, message.length, nil)
|
38
|
+
]
|
39
|
+
assert { expect == kq.kevent(nil, 2) }
|
40
|
+
f.close
|
41
|
+
assert { [] == kq.kevent(nil, 2, 0) }
|
42
|
+
assert_raise(ArgumentError) { kq.kevent(nil, -1) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_kevent_delete
|
48
|
+
IO.pipe do |r, w|
|
49
|
+
Kqueue.open do |kq|
|
50
|
+
kev_w = Kqueue::Event.new(w.fileno, EVFILT_WRITE, EV_ADD, 0, 0, nil)
|
51
|
+
kq.kevent([kev_w], 0)
|
52
|
+
assert { [Kqueue::Event.new(w.fileno, EVFILT_WRITE, EV_ADD, 0, EV_ERROR, nil)] == kq.kevent(nil, 2, 0) }
|
53
|
+
|
54
|
+
kev_del = Kqueue::Event.new(w.fileno, EVFILT_WRITE, EV_DELETE, 0, 0, nil)
|
55
|
+
kq.kevent([kev_del], 0)
|
56
|
+
GC.start
|
57
|
+
assert { [] == kq.kevent(nil, 2, 0) }
|
58
|
+
|
59
|
+
kev_del = Kqueue::Event.new(w.fileno, EVFILT_WRITE, EV_ADD|EV_DELETE, 0, 0, nil)
|
60
|
+
assert_raise(ArgumentError) { kq.kevent([kev_del], 0) }
|
61
|
+
|
62
|
+
kev_del = Kqueue::Event.new(w.fileno, EVFILT_READ, EV_DELETE, 0, 0, nil)
|
63
|
+
assert_raise(ArgumentError) { kq.kevent([kev_del], 0) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_kevent_have_udata
|
69
|
+
IO.pipe do |r, w|
|
70
|
+
Kqueue.open do |kq|
|
71
|
+
obj = Object.new
|
72
|
+
object_id = obj.object_id
|
73
|
+
kev_w = Kqueue::Event.new(w.fileno, EVFILT_WRITE, EV_ADD, 0, 0, obj)
|
74
|
+
ret = kq.kevent([kev_w], 1)
|
75
|
+
GC.start
|
76
|
+
assert { object_id == ret.first.udata.object_id }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
IO.pipe do |r, w|
|
81
|
+
Kqueue.open do |kq|
|
82
|
+
str = "in_string"
|
83
|
+
ary = [:in_array]
|
84
|
+
prc = Proc.new{ :in_proc }
|
85
|
+
kev_w = Kqueue::Event.new(w.fileno, EVFILT_WRITE, EV_ADD, 0, 0, [str, ary, prc])
|
86
|
+
ret = kq.kevent([kev_w], 1)
|
87
|
+
GC.start
|
88
|
+
assert { [Kqueue::Event.new(w.fileno, EVFILT_WRITE, EV_ADD, 0, EV_ERROR, [str, ary, prc])] == ret }
|
89
|
+
assert { "in_string" == ret.first.udata[0] }
|
90
|
+
assert { [:in_array] == ret.first.udata[1] }
|
91
|
+
assert { prc == ret.first.udata[2] }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_kevent_timeout
|
97
|
+
IO.pipe do |r, w|
|
98
|
+
Kqueue.open do |kq|
|
99
|
+
kev_r = Kqueue::Event.new(r.fileno, EVFILT_READ, EV_ADD, 0, 0, nil)
|
100
|
+
assert { [] == kq.kevent([kev_r], 1, 0) }
|
101
|
+
assert { [] == kq.kevent([kev_r], 1, 0.01) }
|
102
|
+
assert_raise(Timeout::Error) do
|
103
|
+
timeout(0.01) { kq.kevent(nil, 1, 0.02) }
|
104
|
+
end
|
105
|
+
assert_raise(Errno::EINVAL) do
|
106
|
+
kq.kevent(nil, 0, -1)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_kevent_with_thread
|
113
|
+
IO.pipe do |r, w|
|
114
|
+
Kqueue.open do |kq|
|
115
|
+
message = 'ok go'
|
116
|
+
kev_r = Kqueue::Event.new(r.fileno, EVFILT_READ, EV_ADD, 0, 0, nil)
|
117
|
+
w.write message
|
118
|
+
w.close
|
119
|
+
GC.start
|
120
|
+
t = Thread.new {
|
121
|
+
kq.kevent([kev_r], 1)
|
122
|
+
}
|
123
|
+
assert { [Kqueue::Event.new(r.fileno, EVFILT_READ, EV_ADD|EV_EOF, 0, message.length, nil)] == t.value }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_file
|
129
|
+
Tempfile.create("test") do |f|
|
130
|
+
Kqueue.open do |kq|
|
131
|
+
kevs = [
|
132
|
+
Kqueue::Event.new(f.fileno, EVFILT_WRITE, EV_ADD|EV_ENABLE|EV_CLEAR, 0, 0, [1]),
|
133
|
+
Kqueue::Event.new(f.fileno, EVFILT_READ, EV_ADD|EV_ENABLE|EV_CLEAR, 0, 0, [2]),
|
134
|
+
Kqueue::Event.new(f.fileno, EVFILT_WRITE, EV_ADD, 0, 0, [3]),
|
135
|
+
Kqueue::Event.new(f.fileno, EVFILT_READ, EV_ADD, 0, 0, [4]),
|
136
|
+
]
|
137
|
+
kq.kevent(kevs, 0)
|
138
|
+
|
139
|
+
GC.start
|
140
|
+
assert { [[3]] == kq.kevent(nil, 4).map(&:udata) }
|
141
|
+
|
142
|
+
f.write 'ok'
|
143
|
+
f.rewind
|
144
|
+
GC.start
|
145
|
+
assert { [[4], [3]] == kq.kevent(nil, 4).map(&:udata) }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kqueue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ksss
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-26 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 kqueue(2)
|
70
|
+
email:
|
71
|
+
- co000ri@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions:
|
74
|
+
- ext/kqueue/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
|
+
- examples/kqueue_server.rb
|
85
|
+
- ext/kqueue/core.c
|
86
|
+
- ext/kqueue/extconf.rb
|
87
|
+
- kqueue.gemspec
|
88
|
+
- test/test_kqueue.rb
|
89
|
+
homepage: https://github.com/ksss/kqueue
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.4.1
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: A Ruby binding for kqueue(2)
|
113
|
+
test_files:
|
114
|
+
- test/test_kqueue.rb
|