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.
@@ -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
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ *.rbc
3
+ *.bundle
4
+ *.so
5
+ *.o
6
+ tmp
7
+ pkg
@@ -0,0 +1,7 @@
1
+ language: objective-c # osx hack
2
+ rvm:
3
+ - 2.1.5
4
+ os:
5
+ - osx
6
+ script:
7
+ - "bundle exec rake clean compile test"
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -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
@@ -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.
@@ -0,0 +1,143 @@
1
+ kqueue
2
+ ===
3
+
4
+ [![Build Status](https://travis-ci.org/ksss/kqueue.svg?branch=master)](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
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/extensiontask'
3
+ require 'rake/testtask'
4
+
5
+ task :default => [:compile, :test]
6
+
7
+ Rake::ExtensionTask.new('kqueue') do |ext|
8
+ end
9
+ Rake::TestTask.new {|t| t.libs << 'test'}
@@ -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
@@ -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 "*** complie error: gem 'kqueue' must be installed #{headers.to_s}. ***"
7
+ puts "*** you can require 'kqueue'. But, you can not use Kqueue APIs. ***"
8
+ end
9
+
10
+ create_makefile('kqueue')
@@ -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
@@ -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