nio4r 2.5.2 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/workflow.yml +61 -0
- data/.mailmap +16 -0
- data/.rubocop.yml +30 -11
- data/Gemfile +6 -6
- data/{CHANGES.md → changes.md} +78 -1
- data/examples/echo_server.rb +9 -2
- data/ext/libev/Changes +71 -2
- data/ext/libev/ev.c +611 -198
- data/ext/libev/ev.h +25 -22
- data/ext/libev/ev_epoll.c +16 -14
- data/ext/libev/ev_iouring.c +694 -0
- data/ext/libev/ev_kqueue.c +4 -4
- data/ext/libev/ev_linuxaio.c +78 -100
- data/ext/libev/ev_poll.c +6 -6
- data/ext/libev/ev_port.c +3 -3
- data/ext/libev/ev_select.c +6 -6
- data/ext/libev/ev_vars.h +34 -0
- data/ext/libev/ev_win32.c +2 -2
- data/ext/libev/ev_wrap.h +56 -0
- data/ext/nio4r/.clang-format +16 -0
- data/ext/nio4r/bytebuffer.c +101 -65
- data/ext/nio4r/extconf.rb +26 -0
- data/ext/nio4r/libev.h +1 -3
- data/ext/nio4r/monitor.c +81 -53
- data/ext/nio4r/nio4r.h +6 -15
- data/ext/nio4r/nio4r_ext.c +1 -1
- data/ext/nio4r/org/nio4r/ByteBuffer.java +2 -0
- data/ext/nio4r/org/nio4r/Monitor.java +1 -0
- data/ext/nio4r/org/nio4r/Selector.java +8 -10
- data/ext/nio4r/selector.c +132 -93
- data/lib/nio/bytebuffer.rb +10 -0
- data/lib/nio/monitor.rb +8 -1
- data/lib/nio/selector.rb +27 -10
- data/lib/nio/version.rb +6 -1
- data/lib/nio.rb +29 -1
- data/lib/nio4r.rb +5 -0
- data/license.md +77 -0
- data/nio4r.gemspec +6 -5
- data/rakelib/extension.rake +1 -2
- data/readme.md +91 -0
- data/spec/nio/acceptables_spec.rb +4 -0
- data/spec/nio/bytebuffer_spec.rb +6 -1
- data/spec/nio/monitor_spec.rb +7 -0
- data/spec/nio/selectables/pipe_spec.rb +6 -0
- data/spec/nio/selectables/ssl_socket_spec.rb +7 -0
- data/spec/nio/selectables/tcp_socket_spec.rb +7 -0
- data/spec/nio/selectables/udp_socket_spec.rb +9 -2
- data/spec/nio/selector_spec.rb +16 -1
- data/spec/spec_helper.rb +7 -2
- data/spec/support/selectable_examples.rb +8 -0
- metadata +20 -16
- data/.travis.yml +0 -44
- data/Guardfile +0 -10
- data/README.md +0 -150
- data/appveyor.yml +0 -40
data/ext/nio4r/selector.c
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
#include "nio4r.h"
|
7
7
|
#ifdef HAVE_RUBYSIG_H
|
8
|
-
#
|
8
|
+
#include "rubysig.h"
|
9
9
|
#endif
|
10
10
|
|
11
11
|
#ifdef HAVE_UNISTD_H
|
@@ -14,18 +14,19 @@
|
|
14
14
|
#include <io.h>
|
15
15
|
#endif
|
16
16
|
|
17
|
-
#include <fcntl.h>
|
18
17
|
#include <assert.h>
|
18
|
+
#include <fcntl.h>
|
19
19
|
|
20
20
|
static VALUE mNIO = Qnil;
|
21
|
-
static VALUE cNIO_Monitor
|
21
|
+
static VALUE cNIO_Monitor = Qnil;
|
22
22
|
static VALUE cNIO_Selector = Qnil;
|
23
23
|
|
24
24
|
/* Allocator/deallocator */
|
25
25
|
static VALUE NIO_Selector_allocate(VALUE klass);
|
26
|
-
static void NIO_Selector_mark(
|
26
|
+
static void NIO_Selector_mark(void *data);
|
27
27
|
static void NIO_Selector_shutdown(struct NIO_Selector *selector);
|
28
|
-
static void NIO_Selector_free(
|
28
|
+
static void NIO_Selector_free(void *data);
|
29
|
+
static size_t NIO_Selector_memsize(const void *data);
|
29
30
|
|
30
31
|
/* Class methods */
|
31
32
|
static VALUE NIO_Selector_supported_backends(VALUE klass);
|
@@ -43,13 +44,13 @@ static VALUE NIO_Selector_closed(VALUE self);
|
|
43
44
|
static VALUE NIO_Selector_is_empty(VALUE self);
|
44
45
|
|
45
46
|
/* Internal functions */
|
46
|
-
static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE
|
47
|
+
static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE arg), VALUE arg);
|
47
48
|
static VALUE NIO_Selector_unlock(VALUE lock);
|
48
|
-
static VALUE NIO_Selector_register_synchronized(VALUE
|
49
|
-
static VALUE NIO_Selector_deregister_synchronized(VALUE
|
50
|
-
static VALUE NIO_Selector_select_synchronized(VALUE
|
51
|
-
static VALUE NIO_Selector_close_synchronized(VALUE
|
52
|
-
static VALUE NIO_Selector_closed_synchronized(VALUE
|
49
|
+
static VALUE NIO_Selector_register_synchronized(VALUE arg);
|
50
|
+
static VALUE NIO_Selector_deregister_synchronized(VALUE arg);
|
51
|
+
static VALUE NIO_Selector_select_synchronized(VALUE arg);
|
52
|
+
static VALUE NIO_Selector_close_synchronized(VALUE arg);
|
53
|
+
static VALUE NIO_Selector_closed_synchronized(VALUE arg);
|
53
54
|
|
54
55
|
static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout);
|
55
56
|
static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents);
|
@@ -62,7 +63,7 @@ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *
|
|
62
63
|
#define BUSYWAIT_INTERVAL 0.01
|
63
64
|
|
64
65
|
/* Selectors wait for events */
|
65
|
-
void Init_NIO_Selector()
|
66
|
+
void Init_NIO_Selector(void)
|
66
67
|
{
|
67
68
|
mNIO = rb_define_module("NIO");
|
68
69
|
cNIO_Selector = rb_define_class_under(mNIO, "Selector", rb_cObject);
|
@@ -80,9 +81,21 @@ void Init_NIO_Selector()
|
|
80
81
|
rb_define_method(cNIO_Selector, "closed?", NIO_Selector_closed, 0);
|
81
82
|
rb_define_method(cNIO_Selector, "empty?", NIO_Selector_is_empty, 0);
|
82
83
|
|
83
|
-
cNIO_Monitor = rb_define_class_under(mNIO, "Monitor",
|
84
|
+
cNIO_Monitor = rb_define_class_under(mNIO, "Monitor", rb_cObject);
|
84
85
|
}
|
85
86
|
|
87
|
+
static const rb_data_type_t NIO_Selector_type = {
|
88
|
+
"NIO::Selector",
|
89
|
+
{
|
90
|
+
NIO_Selector_mark,
|
91
|
+
NIO_Selector_free,
|
92
|
+
NIO_Selector_memsize,
|
93
|
+
},
|
94
|
+
0,
|
95
|
+
0,
|
96
|
+
RUBY_TYPED_WB_PROTECTED // Don't free immediately because of shutdown
|
97
|
+
};
|
98
|
+
|
86
99
|
/* Create the libev event loop and incoming event buffer */
|
87
100
|
static VALUE NIO_Selector_allocate(VALUE klass)
|
88
101
|
{
|
@@ -95,18 +108,16 @@ static VALUE NIO_Selector_allocate(VALUE klass)
|
|
95
108
|
safety. Pipes are nice and safe to use between threads.
|
96
109
|
|
97
110
|
Note that Java NIO uses this same mechanism */
|
98
|
-
if(pipe(fds) < 0) {
|
111
|
+
if (pipe(fds) < 0) {
|
99
112
|
rb_sys_fail("pipe");
|
100
113
|
}
|
101
114
|
|
102
115
|
/* Use non-blocking reads/writes during wakeup, in case the buffer is full */
|
103
|
-
if(fcntl(fds[0], F_SETFL, O_NONBLOCK) < 0 ||
|
104
|
-
fcntl(fds[1], F_SETFL, O_NONBLOCK) < 0) {
|
116
|
+
if (fcntl(fds[0], F_SETFL, O_NONBLOCK) < 0 || fcntl(fds[1], F_SETFL, O_NONBLOCK) < 0) {
|
105
117
|
rb_sys_fail("fcntl");
|
106
118
|
}
|
107
119
|
|
108
|
-
|
109
|
-
|
120
|
+
VALUE obj = TypedData_Make_Struct(klass, struct NIO_Selector, &NIO_Selector_type, selector);
|
110
121
|
/* Defer initializing the loop to #initialize */
|
111
122
|
selector->ev_loop = 0;
|
112
123
|
|
@@ -119,15 +130,22 @@ static VALUE NIO_Selector_allocate(VALUE klass)
|
|
119
130
|
selector->wakeup.data = (void *)selector;
|
120
131
|
|
121
132
|
selector->closed = selector->selecting = selector->wakeup_fired = selector->ready_count = 0;
|
122
|
-
selector->ready_array
|
133
|
+
RB_OBJ_WRITE(obj, &selector->ready_array, Qnil);
|
134
|
+
return obj;
|
135
|
+
}
|
123
136
|
|
124
|
-
|
137
|
+
struct NIO_Selector *NIO_Selector_unwrap(VALUE self)
|
138
|
+
{
|
139
|
+
struct NIO_Selector *selector;
|
140
|
+
TypedData_Get_Struct(self, struct NIO_Selector, &NIO_Selector_type, selector);
|
141
|
+
return selector;
|
125
142
|
}
|
126
143
|
|
127
144
|
/* NIO selectors store all Ruby objects in instance variables so mark is a stub */
|
128
|
-
static void NIO_Selector_mark(
|
145
|
+
static void NIO_Selector_mark(void *data)
|
129
146
|
{
|
130
|
-
|
147
|
+
struct NIO_Selector *selector = (struct NIO_Selector *)data;
|
148
|
+
if (selector->ready_array != Qnil) {
|
131
149
|
rb_gc_mark(selector->ready_array);
|
132
150
|
}
|
133
151
|
}
|
@@ -136,14 +154,14 @@ static void NIO_Selector_mark(struct NIO_Selector *selector)
|
|
136
154
|
Called by both NIO::Selector#close and the finalizer below */
|
137
155
|
static void NIO_Selector_shutdown(struct NIO_Selector *selector)
|
138
156
|
{
|
139
|
-
if(selector->closed) {
|
157
|
+
if (selector->closed) {
|
140
158
|
return;
|
141
159
|
}
|
142
160
|
|
143
161
|
close(selector->wakeup_reader);
|
144
162
|
close(selector->wakeup_writer);
|
145
163
|
|
146
|
-
if(selector->ev_loop) {
|
164
|
+
if (selector->ev_loop) {
|
147
165
|
ev_loop_destroy(selector->ev_loop);
|
148
166
|
selector->ev_loop = 0;
|
149
167
|
}
|
@@ -152,37 +170,52 @@ static void NIO_Selector_shutdown(struct NIO_Selector *selector)
|
|
152
170
|
}
|
153
171
|
|
154
172
|
/* Ruby finalizer for selector objects */
|
155
|
-
static void NIO_Selector_free(
|
173
|
+
static void NIO_Selector_free(void *data)
|
156
174
|
{
|
175
|
+
struct NIO_Selector *selector = (struct NIO_Selector *)data;
|
157
176
|
NIO_Selector_shutdown(selector);
|
158
177
|
xfree(selector);
|
159
178
|
}
|
160
179
|
|
180
|
+
static size_t NIO_Selector_memsize(const void *data)
|
181
|
+
{
|
182
|
+
return sizeof(struct NIO_Selector);
|
183
|
+
}
|
184
|
+
|
161
185
|
/* Return an array of symbols for supported backends */
|
162
|
-
static VALUE NIO_Selector_supported_backends(VALUE klass)
|
186
|
+
static VALUE NIO_Selector_supported_backends(VALUE klass)
|
187
|
+
{
|
163
188
|
unsigned int backends = ev_supported_backends();
|
164
189
|
VALUE result = rb_ary_new();
|
165
190
|
|
166
|
-
if(backends & EVBACKEND_EPOLL) {
|
191
|
+
if (backends & EVBACKEND_EPOLL) {
|
167
192
|
rb_ary_push(result, ID2SYM(rb_intern("epoll")));
|
168
193
|
}
|
169
194
|
|
170
|
-
if(backends & EVBACKEND_POLL) {
|
195
|
+
if (backends & EVBACKEND_POLL) {
|
171
196
|
rb_ary_push(result, ID2SYM(rb_intern("poll")));
|
172
197
|
}
|
173
198
|
|
174
|
-
if(backends & EVBACKEND_KQUEUE) {
|
199
|
+
if (backends & EVBACKEND_KQUEUE) {
|
175
200
|
rb_ary_push(result, ID2SYM(rb_intern("kqueue")));
|
176
201
|
}
|
177
202
|
|
178
|
-
if(backends & EVBACKEND_SELECT) {
|
203
|
+
if (backends & EVBACKEND_SELECT) {
|
179
204
|
rb_ary_push(result, ID2SYM(rb_intern("select")));
|
180
205
|
}
|
181
206
|
|
182
|
-
if(backends & EVBACKEND_PORT) {
|
207
|
+
if (backends & EVBACKEND_PORT) {
|
183
208
|
rb_ary_push(result, ID2SYM(rb_intern("port")));
|
184
209
|
}
|
185
210
|
|
211
|
+
if (backends & EVBACKEND_LINUXAIO) {
|
212
|
+
rb_ary_push(result, ID2SYM(rb_intern("linuxaio")));
|
213
|
+
}
|
214
|
+
|
215
|
+
if (backends & EVBACKEND_IOURING) {
|
216
|
+
rb_ary_push(result, ID2SYM(rb_intern("io_uring")));
|
217
|
+
}
|
218
|
+
|
186
219
|
return result;
|
187
220
|
}
|
188
221
|
|
@@ -197,31 +230,33 @@ static VALUE NIO_Selector_initialize(int argc, VALUE *argv, VALUE self)
|
|
197
230
|
struct NIO_Selector *selector;
|
198
231
|
unsigned int flags = 0;
|
199
232
|
|
200
|
-
|
233
|
+
TypedData_Get_Struct(self, struct NIO_Selector, &NIO_Selector_type, selector);
|
201
234
|
|
202
235
|
rb_scan_args(argc, argv, "01", &backend);
|
203
236
|
|
204
|
-
if(backend != Qnil) {
|
205
|
-
if(!rb_ary_includes(NIO_Selector_supported_backends(CLASS_OF(self)), backend)) {
|
206
|
-
rb_raise(rb_eArgError, "unsupported backend: %s",
|
207
|
-
RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0)));
|
237
|
+
if (backend != Qnil) {
|
238
|
+
if (!rb_ary_includes(NIO_Selector_supported_backends(CLASS_OF(self)), backend)) {
|
239
|
+
rb_raise(rb_eArgError, "unsupported backend: %s", RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0)));
|
208
240
|
}
|
209
241
|
|
210
242
|
backend_id = SYM2ID(backend);
|
211
243
|
|
212
|
-
if(backend_id == rb_intern("epoll")) {
|
244
|
+
if (backend_id == rb_intern("epoll")) {
|
213
245
|
flags = EVBACKEND_EPOLL;
|
214
|
-
} else if(backend_id == rb_intern("poll")) {
|
246
|
+
} else if (backend_id == rb_intern("poll")) {
|
215
247
|
flags = EVBACKEND_POLL;
|
216
|
-
} else if(backend_id == rb_intern("kqueue")) {
|
248
|
+
} else if (backend_id == rb_intern("kqueue")) {
|
217
249
|
flags = EVBACKEND_KQUEUE;
|
218
|
-
} else if(backend_id == rb_intern("select")) {
|
250
|
+
} else if (backend_id == rb_intern("select")) {
|
219
251
|
flags = EVBACKEND_SELECT;
|
220
|
-
} else if(backend_id == rb_intern("port")) {
|
252
|
+
} else if (backend_id == rb_intern("port")) {
|
221
253
|
flags = EVBACKEND_PORT;
|
254
|
+
} else if (backend_id == rb_intern("linuxaio")) {
|
255
|
+
flags = EVBACKEND_LINUXAIO;
|
256
|
+
} else if (backend_id == rb_intern("io_uring")) {
|
257
|
+
flags = EVBACKEND_IOURING;
|
222
258
|
} else {
|
223
|
-
rb_raise(rb_eArgError, "unsupported backend: %s",
|
224
|
-
RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0)));
|
259
|
+
rb_raise(rb_eArgError, "unsupported backend: %s", RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0)));
|
225
260
|
}
|
226
261
|
}
|
227
262
|
|
@@ -229,7 +264,7 @@ static VALUE NIO_Selector_initialize(int argc, VALUE *argv, VALUE self)
|
|
229
264
|
assert(!selector->ev_loop);
|
230
265
|
|
231
266
|
selector->ev_loop = ev_loop_new(flags);
|
232
|
-
if(!selector->ev_loop) {
|
267
|
+
if (!selector->ev_loop) {
|
233
268
|
rb_raise(rb_eIOError, "error initializing event loop");
|
234
269
|
}
|
235
270
|
|
@@ -245,11 +280,12 @@ static VALUE NIO_Selector_initialize(int argc, VALUE *argv, VALUE self)
|
|
245
280
|
return Qnil;
|
246
281
|
}
|
247
282
|
|
248
|
-
static VALUE NIO_Selector_backend(VALUE self)
|
283
|
+
static VALUE NIO_Selector_backend(VALUE self)
|
284
|
+
{
|
249
285
|
struct NIO_Selector *selector;
|
250
286
|
|
251
|
-
|
252
|
-
if(selector->closed) {
|
287
|
+
TypedData_Get_Struct(self, struct NIO_Selector, &NIO_Selector_type, selector);
|
288
|
+
if (selector->closed) {
|
253
289
|
rb_raise(rb_eIOError, "selector is closed");
|
254
290
|
}
|
255
291
|
|
@@ -264,29 +300,33 @@ static VALUE NIO_Selector_backend(VALUE self) {
|
|
264
300
|
return ID2SYM(rb_intern("select"));
|
265
301
|
case EVBACKEND_PORT:
|
266
302
|
return ID2SYM(rb_intern("port"));
|
303
|
+
case EVBACKEND_LINUXAIO:
|
304
|
+
return ID2SYM(rb_intern("linuxaio"));
|
305
|
+
case EVBACKEND_IOURING:
|
306
|
+
return ID2SYM(rb_intern("io_uring"));
|
267
307
|
}
|
268
308
|
|
269
309
|
return ID2SYM(rb_intern("unknown"));
|
270
310
|
}
|
271
311
|
|
272
312
|
/* Synchronize around a reentrant selector lock */
|
273
|
-
static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE
|
313
|
+
static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE arg), VALUE arg)
|
274
314
|
{
|
275
315
|
VALUE current_thread, lock_holder, lock;
|
276
316
|
|
277
317
|
current_thread = rb_thread_current();
|
278
318
|
lock_holder = rb_ivar_get(self, rb_intern("lock_holder"));
|
279
319
|
|
280
|
-
if(lock_holder != current_thread) {
|
320
|
+
if (lock_holder != current_thread) {
|
281
321
|
lock = rb_ivar_get(self, rb_intern("lock"));
|
282
322
|
rb_funcall(lock, rb_intern("lock"), 0);
|
283
323
|
rb_ivar_set(self, rb_intern("lock_holder"), current_thread);
|
284
324
|
|
285
325
|
/* We've acquired the lock, so ensure we unlock it */
|
286
|
-
return rb_ensure(func, (VALUE)
|
326
|
+
return rb_ensure(func, (VALUE)arg, NIO_Selector_unlock, self);
|
287
327
|
} else {
|
288
328
|
/* We already hold the selector lock, so no need to unlock it */
|
289
|
-
return func(
|
329
|
+
return func(arg);
|
290
330
|
}
|
291
331
|
}
|
292
332
|
|
@@ -307,29 +347,30 @@ static VALUE NIO_Selector_unlock(VALUE self)
|
|
307
347
|
static VALUE NIO_Selector_register(VALUE self, VALUE io, VALUE interests)
|
308
348
|
{
|
309
349
|
VALUE args[3] = {self, io, interests};
|
310
|
-
return NIO_Selector_synchronize(self, NIO_Selector_register_synchronized, args);
|
350
|
+
return NIO_Selector_synchronize(self, NIO_Selector_register_synchronized, (VALUE)args);
|
311
351
|
}
|
312
352
|
|
313
353
|
/* Internal implementation of register after acquiring mutex */
|
314
|
-
static VALUE NIO_Selector_register_synchronized(VALUE
|
354
|
+
static VALUE NIO_Selector_register_synchronized(VALUE _args)
|
315
355
|
{
|
316
356
|
VALUE self, io, interests, selectables, monitor;
|
317
357
|
VALUE monitor_args[3];
|
318
358
|
struct NIO_Selector *selector;
|
319
359
|
|
360
|
+
VALUE *args = (VALUE *)_args;
|
320
361
|
self = args[0];
|
321
362
|
io = args[1];
|
322
363
|
interests = args[2];
|
323
364
|
|
324
|
-
|
325
|
-
if(selector->closed) {
|
365
|
+
TypedData_Get_Struct(self, struct NIO_Selector, &NIO_Selector_type, selector);
|
366
|
+
if (selector->closed) {
|
326
367
|
rb_raise(rb_eIOError, "selector is closed");
|
327
368
|
}
|
328
369
|
|
329
370
|
selectables = rb_ivar_get(self, rb_intern("selectables"));
|
330
371
|
monitor = rb_hash_lookup(selectables, io);
|
331
372
|
|
332
|
-
if(monitor != Qnil)
|
373
|
+
if (monitor != Qnil)
|
333
374
|
rb_raise(rb_eArgError, "this IO is already registered with selector");
|
334
375
|
|
335
376
|
/* Create a new NIO::Monitor */
|
@@ -347,21 +388,22 @@ static VALUE NIO_Selector_register_synchronized(VALUE *args)
|
|
347
388
|
static VALUE NIO_Selector_deregister(VALUE self, VALUE io)
|
348
389
|
{
|
349
390
|
VALUE args[2] = {self, io};
|
350
|
-
return NIO_Selector_synchronize(self, NIO_Selector_deregister_synchronized, args);
|
391
|
+
return NIO_Selector_synchronize(self, NIO_Selector_deregister_synchronized, (VALUE)args);
|
351
392
|
}
|
352
393
|
|
353
394
|
/* Internal implementation of register after acquiring mutex */
|
354
|
-
static VALUE NIO_Selector_deregister_synchronized(VALUE
|
395
|
+
static VALUE NIO_Selector_deregister_synchronized(VALUE _args)
|
355
396
|
{
|
356
397
|
VALUE self, io, selectables, monitor;
|
357
398
|
|
399
|
+
VALUE *args = (VALUE *)_args;
|
358
400
|
self = args[0];
|
359
401
|
io = args[1];
|
360
402
|
|
361
403
|
selectables = rb_ivar_get(self, rb_intern("selectables"));
|
362
404
|
monitor = rb_hash_delete(selectables, io);
|
363
405
|
|
364
|
-
if(monitor != Qnil) {
|
406
|
+
if (monitor != Qnil) {
|
365
407
|
rb_funcall(monitor, rb_intern("close"), 1, Qfalse);
|
366
408
|
}
|
367
409
|
|
@@ -381,53 +423,52 @@ static VALUE NIO_Selector_is_registered(VALUE self, VALUE io)
|
|
381
423
|
static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self)
|
382
424
|
{
|
383
425
|
VALUE timeout;
|
384
|
-
VALUE args[2];
|
385
426
|
|
386
427
|
rb_scan_args(argc, argv, "01", &timeout);
|
387
428
|
|
388
|
-
if(timeout != Qnil && NUM2DBL(timeout) < 0) {
|
429
|
+
if (timeout != Qnil && NUM2DBL(timeout) < 0) {
|
389
430
|
rb_raise(rb_eArgError, "time interval must be positive");
|
390
431
|
}
|
391
432
|
|
392
|
-
args[
|
393
|
-
|
394
|
-
|
395
|
-
return NIO_Selector_synchronize(self, NIO_Selector_select_synchronized, args);
|
433
|
+
VALUE args[2] = {self, timeout};
|
434
|
+
return NIO_Selector_synchronize(self, NIO_Selector_select_synchronized, (VALUE)args);
|
396
435
|
}
|
397
436
|
|
398
437
|
/* Internal implementation of select with the selector lock held */
|
399
|
-
static VALUE NIO_Selector_select_synchronized(VALUE
|
438
|
+
static VALUE NIO_Selector_select_synchronized(VALUE _args)
|
400
439
|
{
|
401
440
|
int ready;
|
402
441
|
VALUE ready_array;
|
403
442
|
struct NIO_Selector *selector;
|
404
443
|
|
405
|
-
|
444
|
+
VALUE *args = (VALUE *)_args;
|
445
|
+
|
446
|
+
TypedData_Get_Struct(args[0], struct NIO_Selector, &NIO_Selector_type, selector);
|
406
447
|
|
407
|
-
if(selector->closed) {
|
448
|
+
if (selector->closed) {
|
408
449
|
rb_raise(rb_eIOError, "selector is closed");
|
409
450
|
}
|
410
451
|
|
411
|
-
if(!rb_block_given_p()) {
|
412
|
-
selector->ready_array
|
452
|
+
if (!rb_block_given_p()) {
|
453
|
+
RB_OBJ_WRITE(args[0], &selector->ready_array, rb_ary_new());
|
413
454
|
}
|
414
455
|
|
415
456
|
ready = NIO_Selector_run(selector, args[1]);
|
416
457
|
|
417
458
|
/* Timeout */
|
418
|
-
if(ready < 0) {
|
419
|
-
if(!rb_block_given_p()) {
|
420
|
-
selector->ready_array
|
459
|
+
if (ready < 0) {
|
460
|
+
if (!rb_block_given_p()) {
|
461
|
+
RB_OBJ_WRITE(args[0], &selector->ready_array, Qnil);
|
421
462
|
}
|
422
463
|
|
423
464
|
return Qnil;
|
424
465
|
}
|
425
466
|
|
426
|
-
if(rb_block_given_p()) {
|
467
|
+
if (rb_block_given_p()) {
|
427
468
|
return INT2NUM(ready);
|
428
469
|
} else {
|
429
470
|
ready_array = selector->ready_array;
|
430
|
-
selector->ready_array
|
471
|
+
RB_OBJ_WRITE(args[0], &selector->ready_array, Qnil);
|
431
472
|
return ready_array;
|
432
473
|
}
|
433
474
|
}
|
@@ -441,12 +482,12 @@ static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout)
|
|
441
482
|
selector->selecting = 1;
|
442
483
|
selector->wakeup_fired = 0;
|
443
484
|
|
444
|
-
if(timeout == Qnil) {
|
485
|
+
if (timeout == Qnil) {
|
445
486
|
/* Don't fire a wakeup timeout if we weren't passed one */
|
446
487
|
ev_timer_stop(selector->ev_loop, &selector->timer);
|
447
488
|
} else {
|
448
489
|
timeout_val = NUM2DBL(timeout);
|
449
|
-
if(timeout_val == 0) {
|
490
|
+
if (timeout_val == 0) {
|
450
491
|
/* If we've been given an explicit timeout of 0, perform a non-blocking
|
451
492
|
select operation */
|
452
493
|
ev_run_flags = EVRUN_NOWAIT;
|
@@ -462,7 +503,7 @@ static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout)
|
|
462
503
|
result = selector->ready_count;
|
463
504
|
selector->selecting = selector->ready_count = 0;
|
464
505
|
|
465
|
-
if(result > 0 || selector->wakeup_fired) {
|
506
|
+
if (result > 0 || selector->wakeup_fired) {
|
466
507
|
selector->wakeup_fired = 0;
|
467
508
|
return result;
|
468
509
|
} else {
|
@@ -474,9 +515,9 @@ static int NIO_Selector_run(struct NIO_Selector *selector, VALUE timeout)
|
|
474
515
|
static VALUE NIO_Selector_wakeup(VALUE self)
|
475
516
|
{
|
476
517
|
struct NIO_Selector *selector;
|
477
|
-
|
518
|
+
TypedData_Get_Struct(self, struct NIO_Selector, &NIO_Selector_type, selector);
|
478
519
|
|
479
|
-
if(selector->closed) {
|
520
|
+
if (selector->closed) {
|
480
521
|
rb_raise(rb_eIOError, "selector is closed");
|
481
522
|
}
|
482
523
|
|
@@ -489,15 +530,14 @@ static VALUE NIO_Selector_wakeup(VALUE self)
|
|
489
530
|
/* Close the selector and free system resources */
|
490
531
|
static VALUE NIO_Selector_close(VALUE self)
|
491
532
|
{
|
492
|
-
|
493
|
-
return NIO_Selector_synchronize(self, NIO_Selector_close_synchronized, args);
|
533
|
+
return NIO_Selector_synchronize(self, NIO_Selector_close_synchronized, self);
|
494
534
|
}
|
495
535
|
|
496
|
-
static VALUE NIO_Selector_close_synchronized(VALUE
|
536
|
+
static VALUE NIO_Selector_close_synchronized(VALUE self)
|
497
537
|
{
|
498
538
|
struct NIO_Selector *selector;
|
499
|
-
|
500
|
-
|
539
|
+
|
540
|
+
TypedData_Get_Struct(self, struct NIO_Selector, &NIO_Selector_type, selector);
|
501
541
|
|
502
542
|
NIO_Selector_shutdown(selector);
|
503
543
|
|
@@ -507,15 +547,14 @@ static VALUE NIO_Selector_close_synchronized(VALUE *args)
|
|
507
547
|
/* Is the selector closed? */
|
508
548
|
static VALUE NIO_Selector_closed(VALUE self)
|
509
549
|
{
|
510
|
-
|
511
|
-
return NIO_Selector_synchronize(self, NIO_Selector_closed_synchronized, args);
|
550
|
+
return NIO_Selector_synchronize(self, NIO_Selector_closed_synchronized, self);
|
512
551
|
}
|
513
552
|
|
514
|
-
static VALUE NIO_Selector_closed_synchronized(VALUE
|
553
|
+
static VALUE NIO_Selector_closed_synchronized(VALUE self)
|
515
554
|
{
|
516
555
|
struct NIO_Selector *selector;
|
517
|
-
|
518
|
-
|
556
|
+
|
557
|
+
TypedData_Get_Struct(self, struct NIO_Selector, &NIO_Selector_type, selector);
|
519
558
|
|
520
559
|
return selector->closed ? Qtrue : Qfalse;
|
521
560
|
}
|
@@ -528,7 +567,6 @@ static VALUE NIO_Selector_is_empty(VALUE self)
|
|
528
567
|
return rb_funcall(selectables, rb_intern("empty?"), 0) == Qtrue ? Qtrue : Qfalse;
|
529
568
|
}
|
530
569
|
|
531
|
-
|
532
570
|
/* Called whenever a timeout fires on the event loop */
|
533
571
|
static void NIO_Selector_timeout_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
|
534
572
|
{
|
@@ -542,7 +580,8 @@ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *
|
|
542
580
|
selector->selecting = 0;
|
543
581
|
|
544
582
|
/* Drain the wakeup pipe, giving us level-triggered behavior */
|
545
|
-
while(read(selector->wakeup_reader, buffer, 128) > 0)
|
583
|
+
while (read(selector->wakeup_reader, buffer, 128) > 0)
|
584
|
+
;
|
546
585
|
}
|
547
586
|
|
548
587
|
/* libev callback fired whenever a monitor gets an event */
|
@@ -558,7 +597,7 @@ void NIO_Selector_monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, in
|
|
558
597
|
selector->ready_count++;
|
559
598
|
monitor_data->revents = revents;
|
560
599
|
|
561
|
-
if(rb_block_given_p()) {
|
600
|
+
if (rb_block_given_p()) {
|
562
601
|
rb_yield(monitor);
|
563
602
|
} else {
|
564
603
|
assert(selector->ready_array != Qnil);
|
data/lib/nio/bytebuffer.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2016, by Upekshe Jayasekera.
|
5
|
+
# Copyright, 2016-2017, by Tony Arcieri.
|
6
|
+
# Copyright, 2020, by Thomas Dziedzic.
|
7
|
+
# Copyright, 2023, by Samuel Williams.
|
8
|
+
|
3
9
|
module NIO
|
4
10
|
# Efficient byte buffers for performant I/O operations
|
5
11
|
class ByteBuffer
|
@@ -24,6 +30,7 @@ module NIO
|
|
24
30
|
# @return [NIO::ByteBuffer]
|
25
31
|
def initialize(capacity)
|
26
32
|
raise TypeError, "no implicit conversion of #{capacity.class} to Integer" unless capacity.is_a?(Integer)
|
33
|
+
|
27
34
|
@capacity = capacity
|
28
35
|
clear
|
29
36
|
end
|
@@ -119,9 +126,11 @@ module NIO
|
|
119
126
|
# @return [self]
|
120
127
|
def put(str)
|
121
128
|
raise TypeError, "expected String, got #{str.class}" unless str.respond_to?(:to_str)
|
129
|
+
|
122
130
|
str = str.to_str
|
123
131
|
|
124
132
|
raise OverflowError, "buffer is full" if str.length > @limit - @position
|
133
|
+
|
125
134
|
@buffer[@position...str.length] = str
|
126
135
|
@position += str.length
|
127
136
|
self
|
@@ -188,6 +197,7 @@ module NIO
|
|
188
197
|
# @raise [NIO::ByteBuffer::MarkUnsetError] mark has not been set (call `#mark` first)
|
189
198
|
def reset
|
190
199
|
raise MarkUnsetError, "mark has not been set" unless @mark
|
200
|
+
|
191
201
|
@position = @mark
|
192
202
|
self
|
193
203
|
end
|
data/lib/nio/monitor.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2011-2018, by Tony Arcieri.
|
5
|
+
# Copyright, 2015, by Upekshe Jayasekera.
|
6
|
+
# Copyright, 2015, by Vladimir Kochnev.
|
7
|
+
# Copyright, 2018-2023, by Samuel Williams.
|
8
|
+
# Copyright, 2019-2020, by Gregory Longtin.
|
9
|
+
|
3
10
|
module NIO
|
4
11
|
# Monitors watch IO objects for specific events
|
5
12
|
class Monitor
|
@@ -8,7 +15,7 @@ module NIO
|
|
8
15
|
|
9
16
|
# :nodoc:
|
10
17
|
def initialize(io, interests, selector)
|
11
|
-
unless io.is_a?
|
18
|
+
unless defined?(::OpenSSL) && io.is_a?(::OpenSSL::SSL::SSLSocket)
|
12
19
|
unless io.is_a?(IO)
|
13
20
|
if IO.respond_to? :try_convert
|
14
21
|
io = IO.try_convert(io)
|
data/lib/nio/selector.rb
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2011-2017, by Tony Arcieri.
|
5
|
+
# Copyright, 2012, by Logan Bowers.
|
6
|
+
# Copyright, 2013, by Sadayuki Furuhashi.
|
7
|
+
# Copyright, 2013, by Stephen von Takach.
|
8
|
+
# Copyright, 2013, by Tim Carey-Smith.
|
9
|
+
# Copyright, 2013, by Ravil Bayramgalin.
|
10
|
+
# Copyright, 2014, by Sergey Avseyev.
|
11
|
+
# Copyright, 2014, by John Thornton.
|
12
|
+
# Copyright, 2015, by Vladimir Kochnev.
|
13
|
+
# Copyright, 2015, by Upekshe Jayasekera.
|
14
|
+
# Copyright, 2019-2020, by Gregory Longtin.
|
15
|
+
# Copyright, 2020-2021, by Joao Fernandes.
|
16
|
+
# Copyright, 2023, by Samuel Williams.
|
17
|
+
|
3
18
|
require "set"
|
4
19
|
|
5
20
|
module NIO
|
@@ -14,7 +29,7 @@ module NIO
|
|
14
29
|
|
15
30
|
# Create a new NIO::Selector
|
16
31
|
def initialize(backend = :ruby)
|
17
|
-
raise ArgumentError, "unsupported backend: #{backend}" unless
|
32
|
+
raise ArgumentError, "unsupported backend: #{backend}" unless [:ruby, nil].include?(backend)
|
18
33
|
|
19
34
|
@selectables = {}
|
20
35
|
@lock = Mutex.new
|
@@ -26,14 +41,16 @@ module NIO
|
|
26
41
|
|
27
42
|
# Return a symbol representing the backend I/O multiplexing mechanism used.
|
28
43
|
# Supported backends are:
|
29
|
-
# * :ruby
|
30
|
-
# * :java
|
31
|
-
# * :epoll
|
32
|
-
# * :poll
|
33
|
-
# * :kqueue
|
34
|
-
# * :select
|
35
|
-
# * :port
|
36
|
-
# * :
|
44
|
+
# * :ruby - pure Ruby (i.e IO.select)
|
45
|
+
# * :java - Java NIO on JRuby
|
46
|
+
# * :epoll - libev w\ Linux epoll
|
47
|
+
# * :poll - libev w\ POSIX poll
|
48
|
+
# * :kqueue - libev w\ BSD kqueue
|
49
|
+
# * :select - libev w\ SysV select
|
50
|
+
# * :port - libev w\ I/O completion ports
|
51
|
+
# * :linuxaio - libev w\ Linux AIO io_submit (experimental)
|
52
|
+
# * :io_uring - libev w\ Linux io_uring (experimental)
|
53
|
+
# * :unknown - libev w\ unknown backend
|
37
54
|
def backend
|
38
55
|
:ruby
|
39
56
|
end
|
@@ -44,7 +61,7 @@ module NIO
|
|
44
61
|
# * :w - is the IO writeable?
|
45
62
|
# * :rw - is the IO either readable or writeable?
|
46
63
|
def register(io, interest)
|
47
|
-
unless io.is_a?
|
64
|
+
unless defined?(::OpenSSL) && io.is_a?(::OpenSSL::SSL::SSLSocket)
|
48
65
|
io = IO.try_convert(io)
|
49
66
|
end
|
50
67
|
|
data/lib/nio/version.rb
CHANGED