event 0.2.1 → 0.4.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 +4 -4
- data/ext/event/Makefile +266 -0
- data/ext/event/backend/backend.h +2 -0
- data/ext/event/backend/epoll.c +83 -14
- data/ext/event/backend/kqueue.c +184 -50
- data/ext/event/backend/uring.c +191 -80
- data/ext/event/backend/uring.h +1 -0
- data/ext/event/epoll.o +0 -0
- data/ext/event/event.o +0 -0
- data/ext/event/event.so +0 -0
- data/ext/event/extconf.h +5 -0
- data/ext/event/extconf.rb +2 -0
- data/ext/event/mkmf.log +84 -0
- data/ext/event/uring.o +0 -0
- data/lib/event.rb +1 -1
- data/lib/event/backend.rb +49 -0
- data/lib/event/backend/select.rb +14 -1
- data/lib/event/debug/selector.rb +9 -0
- data/lib/event/version.rb +1 -1
- metadata +11 -3
data/ext/event/backend/kqueue.c
CHANGED
@@ -28,14 +28,7 @@
|
|
28
28
|
static VALUE Event_Backend_KQueue = Qnil;
|
29
29
|
static ID id_fileno, id_transfer;
|
30
30
|
|
31
|
-
|
32
|
-
READABLE = 1,
|
33
|
-
PRIORITY = 2,
|
34
|
-
WRITABLE = 4,
|
35
|
-
ERROR = 8,
|
36
|
-
HANGUP = 16;
|
37
|
-
|
38
|
-
static const unsigned KQUEUE_MAX_EVENTS = 1024;
|
31
|
+
enum {KQUEUE_MAX_EVENTS = 64};
|
39
32
|
|
40
33
|
struct Event_Backend_KQueue {
|
41
34
|
VALUE loop;
|
@@ -48,13 +41,19 @@ void Event_Backend_KQueue_Type_mark(void *_data)
|
|
48
41
|
rb_gc_mark(data->loop);
|
49
42
|
}
|
50
43
|
|
44
|
+
static
|
45
|
+
void close_internal(struct Event_Backend_KQueue *data) {
|
46
|
+
if (data->descriptor >= 0) {
|
47
|
+
close(data->descriptor);
|
48
|
+
data->descriptor = -1;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
51
52
|
void Event_Backend_KQueue_Type_free(void *_data)
|
52
53
|
{
|
53
54
|
struct Event_Backend_KQueue *data = _data;
|
54
55
|
|
55
|
-
|
56
|
-
close(data->descriptor);
|
57
|
-
}
|
56
|
+
close_internal(data);
|
58
57
|
|
59
58
|
free(data);
|
60
59
|
}
|
@@ -104,50 +103,121 @@ VALUE Event_Backend_KQueue_initialize(VALUE self, VALUE loop) {
|
|
104
103
|
return self;
|
105
104
|
}
|
106
105
|
|
107
|
-
|
108
|
-
|
109
|
-
|
106
|
+
VALUE Event_Backend_KQueue_close(VALUE self) {
|
107
|
+
struct Event_Backend_KQueue *data = NULL;
|
108
|
+
TypedData_Get_Struct(self, struct Event_Backend_KQueue, &Event_Backend_KQueue_Type, data);
|
110
109
|
|
111
|
-
|
112
|
-
if (events & PRIORITY) filter |= EV_OOBAND;
|
113
|
-
if (events & WRITABLE) filter |= EVFILT_WRITE;
|
110
|
+
close_internal(data);
|
114
111
|
|
115
|
-
return
|
112
|
+
return Qnil;
|
116
113
|
}
|
117
114
|
|
118
|
-
static
|
119
|
-
int
|
120
|
-
int
|
115
|
+
static
|
116
|
+
int io_add_filters(int descriptor, int ident, int events, VALUE fiber) {
|
117
|
+
int count = 0;
|
118
|
+
struct kevent kevents[2] = {0};
|
121
119
|
|
122
|
-
if (
|
123
|
-
|
124
|
-
|
120
|
+
if (events & READABLE) {
|
121
|
+
kevents[count].ident = ident;
|
122
|
+
kevents[count].filter = EVFILT_READ;
|
123
|
+
kevents[count].flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
|
124
|
+
kevents[count].udata = (void*)fiber;
|
125
|
+
|
126
|
+
// #ifdef EV_OOBAND
|
127
|
+
// if (events & PRIORITY) {
|
128
|
+
// kevents[count].flags |= EV_OOBAND;
|
129
|
+
// }
|
130
|
+
// #endif
|
131
|
+
|
132
|
+
count++;
|
133
|
+
}
|
134
|
+
|
135
|
+
if (events & WRITABLE) {
|
136
|
+
kevents[count].ident = ident;
|
137
|
+
kevents[count].filter = EVFILT_WRITE;
|
138
|
+
kevents[count].flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
|
139
|
+
kevents[count].udata = (void*)fiber;
|
140
|
+
count++;
|
141
|
+
}
|
125
142
|
|
126
|
-
|
143
|
+
int result = kevent(descriptor, kevents, count, NULL, 0, NULL);
|
144
|
+
|
145
|
+
if (result == -1) {
|
146
|
+
rb_sys_fail("kevent(register)");
|
147
|
+
}
|
148
|
+
|
149
|
+
return events;
|
127
150
|
}
|
128
151
|
|
129
|
-
|
130
|
-
|
131
|
-
|
152
|
+
static
|
153
|
+
void io_remove_filters(int descriptor, int ident, int events) {
|
154
|
+
int count = 0;
|
155
|
+
struct kevent kevents[2] = {0};
|
132
156
|
|
133
|
-
|
157
|
+
if (events & READABLE) {
|
158
|
+
kevents[count].ident = ident;
|
159
|
+
kevents[count].filter = EVFILT_READ;
|
160
|
+
kevents[count].flags = EV_DELETE;
|
161
|
+
|
162
|
+
count++;
|
163
|
+
}
|
134
164
|
|
135
|
-
|
165
|
+
if (events & WRITABLE) {
|
166
|
+
kevents[count].ident = ident;
|
167
|
+
kevents[count].filter = EVFILT_WRITE;
|
168
|
+
kevents[count].flags = EV_DELETE;
|
169
|
+
count++;
|
170
|
+
}
|
136
171
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
172
|
+
// Ignore the result.
|
173
|
+
kevent(descriptor, kevents, count, NULL, 0, NULL);
|
174
|
+
}
|
175
|
+
|
176
|
+
struct io_wait_arguments {
|
177
|
+
struct Event_Backend_KQueue *data;
|
178
|
+
int events;
|
179
|
+
int descriptor;
|
180
|
+
};
|
181
|
+
|
182
|
+
static
|
183
|
+
VALUE io_wait_rescue(VALUE _arguments, VALUE exception) {
|
184
|
+
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
141
185
|
|
142
|
-
|
143
|
-
int result = kevent(data->descriptor, &event, 1, NULL, 0, NULL);
|
186
|
+
io_remove_filters(arguments->data->descriptor, arguments->descriptor, arguments->events);
|
144
187
|
|
145
|
-
|
146
|
-
|
147
|
-
|
188
|
+
rb_exc_raise(exception);
|
189
|
+
};
|
190
|
+
|
191
|
+
static inline
|
192
|
+
int events_from_kqueue_filter(int filter) {
|
193
|
+
if (filter == EVFILT_READ) return READABLE;
|
194
|
+
if (filter == EVFILT_WRITE) return WRITABLE;
|
195
|
+
|
196
|
+
return 0;
|
197
|
+
}
|
198
|
+
|
199
|
+
static
|
200
|
+
VALUE io_wait_transfer(VALUE _arguments) {
|
201
|
+
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
202
|
+
|
203
|
+
VALUE result = rb_funcall(arguments->data->loop, id_transfer, 0);
|
148
204
|
|
149
|
-
VALUE result = rb_funcall(data->loop, id_transfer, 0);
|
150
205
|
return INT2NUM(events_from_kqueue_filter(NUM2INT(result)));
|
206
|
+
};
|
207
|
+
|
208
|
+
VALUE Event_Backend_KQueue_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
|
209
|
+
struct Event_Backend_KQueue *data = NULL;
|
210
|
+
TypedData_Get_Struct(self, struct Event_Backend_KQueue, &Event_Backend_KQueue_Type, data);
|
211
|
+
|
212
|
+
int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
|
213
|
+
|
214
|
+
struct io_wait_arguments io_wait_arguments = {
|
215
|
+
.events = io_add_filters(data->descriptor, descriptor, NUM2INT(events), fiber),
|
216
|
+
.data = data,
|
217
|
+
.descriptor = descriptor,
|
218
|
+
};
|
219
|
+
|
220
|
+
return rb_rescue(io_wait_transfer, (VALUE)&io_wait_arguments, io_wait_rescue, (VALUE)&io_wait_arguments);
|
151
221
|
}
|
152
222
|
|
153
223
|
static
|
@@ -165,7 +235,7 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
|
|
165
235
|
|
166
236
|
else if (RB_FLOAT_TYPE_P(duration)) {
|
167
237
|
double value = RFLOAT_VALUE(duration);
|
168
|
-
time_t seconds =
|
238
|
+
time_t seconds = value;
|
169
239
|
|
170
240
|
storage->tv_sec = seconds;
|
171
241
|
storage->tv_nsec = (value - seconds) * 1000000000L;
|
@@ -176,26 +246,89 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
|
|
176
246
|
rb_raise(rb_eRuntimeError, "unable to convert timeout");
|
177
247
|
}
|
178
248
|
|
249
|
+
static
|
250
|
+
int timeout_nonblocking(struct timespec * timespec) {
|
251
|
+
return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
|
252
|
+
}
|
253
|
+
|
254
|
+
struct select_arguments {
|
255
|
+
struct Event_Backend_KQueue *data;
|
256
|
+
|
257
|
+
int count;
|
258
|
+
struct kevent events[KQUEUE_MAX_EVENTS];
|
259
|
+
|
260
|
+
struct timespec storage;
|
261
|
+
struct timespec *timeout;
|
262
|
+
};
|
263
|
+
|
264
|
+
static
|
265
|
+
void * select_internal(void *_arguments) {
|
266
|
+
struct select_arguments * arguments = (struct select_arguments *)_arguments;
|
267
|
+
|
268
|
+
arguments->count = kevent(arguments->data->descriptor, NULL, 0, arguments->events, arguments->count, arguments->timeout);
|
269
|
+
|
270
|
+
return NULL;
|
271
|
+
}
|
272
|
+
|
273
|
+
static
|
274
|
+
void select_internal_without_gvl(struct select_arguments *arguments) {
|
275
|
+
rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
276
|
+
|
277
|
+
if (arguments->count == -1) {
|
278
|
+
rb_sys_fail("select_internal_without_gvl:kevent");
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
static
|
283
|
+
void select_internal_with_gvl(struct select_arguments *arguments) {
|
284
|
+
select_internal((void *)arguments);
|
285
|
+
|
286
|
+
if (arguments->count == -1) {
|
287
|
+
rb_sys_fail("select_internal_with_gvl:kevent");
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
179
291
|
VALUE Event_Backend_KQueue_select(VALUE self, VALUE duration) {
|
180
292
|
struct Event_Backend_KQueue *data = NULL;
|
181
293
|
TypedData_Get_Struct(self, struct Event_Backend_KQueue, &Event_Backend_KQueue_Type, data);
|
182
294
|
|
183
|
-
struct
|
184
|
-
|
295
|
+
struct select_arguments arguments = {
|
296
|
+
.data = data,
|
297
|
+
.count = KQUEUE_MAX_EVENTS,
|
298
|
+
.storage = {
|
299
|
+
.tv_sec = 0,
|
300
|
+
.tv_nsec = 0
|
301
|
+
}
|
302
|
+
};
|
185
303
|
|
186
|
-
|
304
|
+
// We break this implementation into two parts.
|
305
|
+
// (1) count = kevent(..., timeout = 0)
|
306
|
+
// (2) without gvl: kevent(..., timeout = 0) if count == 0 and timeout != 0
|
307
|
+
// This allows us to avoid releasing and reacquiring the GVL.
|
308
|
+
// Non-comprehensive testing shows this gives a 1.5x speedup.
|
309
|
+
arguments.timeout = &arguments.storage;
|
187
310
|
|
188
|
-
|
189
|
-
|
311
|
+
// First do the syscall with no timeout to get any immediately available events:
|
312
|
+
select_internal_with_gvl(&arguments);
|
313
|
+
|
314
|
+
// If there were no pending events, if we have a timeout, wait for more events:
|
315
|
+
if (arguments.count == 0) {
|
316
|
+
arguments.timeout = make_timeout(duration, &arguments.storage);
|
317
|
+
|
318
|
+
if (!timeout_nonblocking(arguments.timeout)) {
|
319
|
+
arguments.count = KQUEUE_MAX_EVENTS;
|
320
|
+
|
321
|
+
select_internal_without_gvl(&arguments);
|
322
|
+
}
|
190
323
|
}
|
191
324
|
|
192
|
-
for (int i = 0; i < count; i += 1) {
|
193
|
-
VALUE fiber = (VALUE)events[i].udata;
|
194
|
-
VALUE result = INT2NUM(events[i].filter);
|
325
|
+
for (int i = 0; i < arguments.count; i += 1) {
|
326
|
+
VALUE fiber = (VALUE)arguments.events[i].udata;
|
327
|
+
VALUE result = INT2NUM(arguments.events[i].filter);
|
195
328
|
rb_funcall(fiber, id_transfer, 1, result);
|
196
329
|
}
|
197
330
|
|
198
|
-
return INT2NUM(count);
|
331
|
+
return INT2NUM(arguments.count);
|
199
332
|
}
|
200
333
|
|
201
334
|
void Init_Event_Backend_KQueue(VALUE Event_Backend) {
|
@@ -206,6 +339,7 @@ void Init_Event_Backend_KQueue(VALUE Event_Backend) {
|
|
206
339
|
|
207
340
|
rb_define_alloc_func(Event_Backend_KQueue, Event_Backend_KQueue_allocate);
|
208
341
|
rb_define_method(Event_Backend_KQueue, "initialize", Event_Backend_KQueue_initialize, 1);
|
342
|
+
rb_define_method(Event_Backend_KQueue, "close", Event_Backend_KQueue_close, 0);
|
209
343
|
|
210
344
|
rb_define_method(Event_Backend_KQueue, "io_wait", Event_Backend_KQueue_io_wait, 3);
|
211
345
|
rb_define_method(Event_Backend_KQueue, "select", Event_Backend_KQueue_select, 1);
|
data/ext/event/backend/uring.c
CHANGED
@@ -28,12 +28,12 @@
|
|
28
28
|
static VALUE Event_Backend_URing = Qnil;
|
29
29
|
static ID id_fileno, id_transfer;
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
enum {URING_ENTRIES = 128};
|
32
|
+
enum {URING_MAX_EVENTS = 128};
|
33
33
|
|
34
34
|
struct Event_Backend_URing {
|
35
35
|
VALUE loop;
|
36
|
-
struct io_uring
|
36
|
+
struct io_uring ring;
|
37
37
|
};
|
38
38
|
|
39
39
|
void Event_Backend_URing_Type_mark(void *_data)
|
@@ -42,14 +42,19 @@ void Event_Backend_URing_Type_mark(void *_data)
|
|
42
42
|
rb_gc_mark(data->loop);
|
43
43
|
}
|
44
44
|
|
45
|
+
static
|
46
|
+
void close_internal(struct Event_Backend_URing *data) {
|
47
|
+
if (data->ring.ring_fd >= 0) {
|
48
|
+
io_uring_queue_exit(&data->ring);
|
49
|
+
data->ring.ring_fd = -1;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
45
53
|
void Event_Backend_URing_Type_free(void *_data)
|
46
54
|
{
|
47
55
|
struct Event_Backend_URing *data = _data;
|
48
56
|
|
49
|
-
|
50
|
-
io_uring_queue_exit(data->ring);
|
51
|
-
xfree(data->ring);
|
52
|
-
}
|
57
|
+
close_internal(data);
|
53
58
|
|
54
59
|
free(data);
|
55
60
|
}
|
@@ -75,7 +80,7 @@ VALUE Event_Backend_URing_allocate(VALUE self) {
|
|
75
80
|
VALUE instance = TypedData_Make_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
|
76
81
|
|
77
82
|
data->loop = Qnil;
|
78
|
-
data->ring =
|
83
|
+
data->ring.ring_fd = -1;
|
79
84
|
|
80
85
|
return instance;
|
81
86
|
}
|
@@ -85,17 +90,27 @@ VALUE Event_Backend_URing_initialize(VALUE self, VALUE loop) {
|
|
85
90
|
TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
|
86
91
|
|
87
92
|
data->loop = loop;
|
88
|
-
data->ring = xmalloc(sizeof(struct io_uring));
|
89
93
|
|
90
|
-
int result = io_uring_queue_init(URING_ENTRIES, data->ring, 0);
|
94
|
+
int result = io_uring_queue_init(URING_ENTRIES, &data->ring, 0);
|
91
95
|
|
92
|
-
if (result
|
93
|
-
|
96
|
+
if (result < 0) {
|
97
|
+
rb_syserr_fail(-result, "io_uring_queue_init");
|
94
98
|
}
|
95
99
|
|
100
|
+
rb_update_max_fd(data->ring.ring_fd);
|
101
|
+
|
96
102
|
return self;
|
97
103
|
}
|
98
104
|
|
105
|
+
VALUE Event_Backend_URing_close(VALUE self) {
|
106
|
+
struct Event_Backend_URing *data = NULL;
|
107
|
+
TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
|
108
|
+
|
109
|
+
close_internal(data);
|
110
|
+
|
111
|
+
return Qnil;
|
112
|
+
}
|
113
|
+
|
99
114
|
static inline
|
100
115
|
short poll_flags_from_events(int events) {
|
101
116
|
short flags = 0;
|
@@ -121,63 +136,84 @@ int events_from_poll_flags(short flags) {
|
|
121
136
|
return events;
|
122
137
|
}
|
123
138
|
|
124
|
-
|
125
|
-
struct Event_Backend_URing *data
|
126
|
-
|
139
|
+
struct io_wait_arguments {
|
140
|
+
struct Event_Backend_URing *data;
|
141
|
+
VALUE fiber;
|
142
|
+
short flags;
|
143
|
+
};
|
144
|
+
|
145
|
+
struct io_uring_sqe * io_get_sqe(struct Event_Backend_URing *data) {
|
146
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&data->ring);
|
127
147
|
|
128
|
-
|
129
|
-
|
148
|
+
while (sqe == NULL) {
|
149
|
+
sqe = io_uring_get_sqe(&data->ring);
|
150
|
+
}
|
130
151
|
|
131
|
-
|
152
|
+
return sqe;
|
153
|
+
}
|
154
|
+
|
155
|
+
static
|
156
|
+
VALUE io_wait_rescue(VALUE _arguments, VALUE exception) {
|
157
|
+
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
158
|
+
struct Event_Backend_URing *data = arguments->data;
|
132
159
|
|
133
|
-
|
160
|
+
struct io_uring_sqe *sqe = io_get_sqe(data);
|
134
161
|
|
135
|
-
|
136
|
-
|
137
|
-
|
162
|
+
// fprintf(stderr, "poll_remove(%p, %p)\n", sqe, (void*)arguments->fiber);
|
163
|
+
|
164
|
+
io_uring_prep_poll_remove(sqe, (void*)arguments->fiber);
|
165
|
+
io_uring_submit(&data->ring);
|
166
|
+
|
167
|
+
rb_exc_raise(exception);
|
168
|
+
};
|
169
|
+
|
170
|
+
static
|
171
|
+
VALUE io_wait_transfer(VALUE _arguments) {
|
172
|
+
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
173
|
+
struct Event_Backend_URing *data = arguments->data;
|
138
174
|
|
139
175
|
VALUE result = rb_funcall(data->loop, id_transfer, 0);
|
140
176
|
|
141
177
|
// We explicitly filter the resulting events based on the requested events.
|
142
178
|
// In some cases, poll will report events we didn't ask for.
|
143
|
-
flags
|
179
|
+
short flags = arguments->flags & NUM2INT(result);
|
144
180
|
|
145
181
|
return INT2NUM(events_from_poll_flags(flags));
|
146
|
-
}
|
182
|
+
};
|
147
183
|
|
148
|
-
|
149
|
-
struct
|
150
|
-
|
151
|
-
return NULL;
|
152
|
-
}
|
184
|
+
VALUE Event_Backend_URing_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
|
185
|
+
struct Event_Backend_URing *data = NULL;
|
186
|
+
TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
|
153
187
|
|
154
|
-
|
155
|
-
|
156
|
-
storage->tv_nsec = 0;
|
157
|
-
|
158
|
-
return storage;
|
159
|
-
}
|
188
|
+
int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
|
189
|
+
struct io_uring_sqe *sqe = io_get_sqe(data);
|
160
190
|
|
161
|
-
|
162
|
-
double value = RFLOAT_VALUE(duration);
|
163
|
-
time_t seconds = duration;
|
164
|
-
|
165
|
-
storage->tv_sec = seconds;
|
166
|
-
storage->tv_nsec = (value - seconds) * 1000000000L;
|
167
|
-
|
168
|
-
return storage;
|
169
|
-
}
|
191
|
+
if (!sqe) return INT2NUM(0);
|
170
192
|
|
171
|
-
|
193
|
+
short flags = poll_flags_from_events(NUM2INT(events));
|
194
|
+
|
195
|
+
// fprintf(stderr, "poll_add(%p, %d, %d, %p)\n", sqe, descriptor, flags, (void*)fiber);
|
196
|
+
|
197
|
+
io_uring_prep_poll_add(sqe, descriptor, flags);
|
198
|
+
io_uring_sqe_set_data(sqe, (void*)fiber);
|
199
|
+
io_uring_submit(&data->ring);
|
200
|
+
|
201
|
+
struct io_wait_arguments io_wait_arguments = {
|
202
|
+
.data = data,
|
203
|
+
.fiber = fiber,
|
204
|
+
.flags = flags
|
205
|
+
};
|
206
|
+
|
207
|
+
return rb_rescue(io_wait_transfer, (VALUE)&io_wait_arguments, io_wait_rescue, (VALUE)&io_wait_arguments);
|
172
208
|
}
|
173
209
|
|
174
210
|
inline static
|
175
211
|
void resize_to_capacity(VALUE string, size_t offset, size_t length) {
|
176
212
|
size_t current_length = RSTRING_LEN(string);
|
177
213
|
long difference = (long)(offset + length) - (long)current_length;
|
178
|
-
|
214
|
+
|
179
215
|
difference += 1;
|
180
|
-
|
216
|
+
|
181
217
|
if (difference > 0) {
|
182
218
|
rb_str_modify_expand(string, difference);
|
183
219
|
} else {
|
@@ -188,7 +224,7 @@ void resize_to_capacity(VALUE string, size_t offset, size_t length) {
|
|
188
224
|
inline static
|
189
225
|
void resize_to_fit(VALUE string, size_t offset, size_t length) {
|
190
226
|
size_t current_length = RSTRING_LEN(string);
|
191
|
-
|
227
|
+
|
192
228
|
if (current_length < (offset + length)) {
|
193
229
|
rb_str_set_len(string, offset + length);
|
194
230
|
}
|
@@ -197,95 +233,169 @@ void resize_to_fit(VALUE string, size_t offset, size_t length) {
|
|
197
233
|
VALUE Event_Backend_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
|
198
234
|
struct Event_Backend_URing *data = NULL;
|
199
235
|
TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
|
200
|
-
|
236
|
+
|
201
237
|
resize_to_capacity(buffer, NUM2SIZET(offset), NUM2SIZET(length));
|
202
|
-
|
238
|
+
|
203
239
|
int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
|
204
|
-
struct io_uring_sqe *sqe =
|
205
|
-
|
240
|
+
struct io_uring_sqe *sqe = io_get_sqe(data);
|
241
|
+
|
206
242
|
struct iovec iovecs[1];
|
207
243
|
iovecs[0].iov_base = RSTRING_PTR(buffer) + NUM2SIZET(offset);
|
208
244
|
iovecs[0].iov_len = NUM2SIZET(length);
|
209
|
-
|
245
|
+
|
210
246
|
io_uring_prep_readv(sqe, descriptor, iovecs, 1, 0);
|
211
247
|
io_uring_sqe_set_data(sqe, (void*)fiber);
|
212
|
-
io_uring_submit(data->ring);
|
213
|
-
|
248
|
+
io_uring_submit(&data->ring);
|
249
|
+
|
214
250
|
// fprintf(stderr, "prep_readv(%p, %d, %ld)\n", sqe, descriptor, iovecs[0].iov_len);
|
215
|
-
|
251
|
+
|
216
252
|
int result = NUM2INT(rb_funcall(data->loop, id_transfer, 0));
|
217
|
-
|
253
|
+
|
218
254
|
if (result < 0) {
|
219
255
|
rb_syserr_fail(-result, strerror(-result));
|
220
256
|
}
|
221
|
-
|
257
|
+
|
222
258
|
resize_to_fit(buffer, NUM2SIZET(offset), (size_t)result);
|
223
|
-
|
259
|
+
|
224
260
|
return INT2NUM(result);
|
225
261
|
}
|
226
262
|
|
227
263
|
VALUE Event_Backend_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
|
228
264
|
struct Event_Backend_URing *data = NULL;
|
229
265
|
TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
|
230
|
-
|
266
|
+
|
231
267
|
if ((size_t)RSTRING_LEN(buffer) < NUM2SIZET(offset) + NUM2SIZET(length)) {
|
232
268
|
rb_raise(rb_eRuntimeError, "invalid offset/length exceeds bounds of buffer");
|
233
269
|
}
|
234
|
-
|
270
|
+
|
235
271
|
int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
|
236
|
-
struct io_uring_sqe *sqe =
|
237
|
-
|
272
|
+
struct io_uring_sqe *sqe = io_get_sqe(data);
|
273
|
+
|
238
274
|
struct iovec iovecs[1];
|
239
275
|
iovecs[0].iov_base = RSTRING_PTR(buffer) + NUM2SIZET(offset);
|
240
276
|
iovecs[0].iov_len = NUM2SIZET(length);
|
241
|
-
|
277
|
+
|
242
278
|
io_uring_prep_writev(sqe, descriptor, iovecs, 1, 0);
|
243
279
|
io_uring_sqe_set_data(sqe, (void*)fiber);
|
244
|
-
io_uring_submit(data->ring);
|
280
|
+
io_uring_submit(&data->ring);
|
245
281
|
|
246
282
|
// fprintf(stderr, "prep_writev(%p, %d, %ld)\n", sqe, descriptor, iovecs[0].iov_len);
|
247
283
|
|
248
284
|
int result = NUM2INT(rb_funcall(data->loop, id_transfer, 0));
|
249
|
-
|
285
|
+
|
250
286
|
if (result < 0) {
|
251
287
|
rb_syserr_fail(-result, strerror(-result));
|
252
288
|
}
|
253
|
-
|
289
|
+
|
254
290
|
return INT2NUM(result);
|
255
291
|
}
|
256
292
|
|
293
|
+
static
|
294
|
+
struct __kernel_timespec * make_timeout(VALUE duration, struct __kernel_timespec *storage) {
|
295
|
+
if (duration == Qnil) {
|
296
|
+
return NULL;
|
297
|
+
}
|
298
|
+
|
299
|
+
if (FIXNUM_P(duration)) {
|
300
|
+
storage->tv_sec = NUM2TIMET(duration);
|
301
|
+
storage->tv_nsec = 0;
|
302
|
+
|
303
|
+
return storage;
|
304
|
+
}
|
305
|
+
|
306
|
+
else if (RB_FLOAT_TYPE_P(duration)) {
|
307
|
+
double value = RFLOAT_VALUE(duration);
|
308
|
+
time_t seconds = value;
|
309
|
+
|
310
|
+
storage->tv_sec = seconds;
|
311
|
+
storage->tv_nsec = (value - seconds) * 1000000000L;
|
312
|
+
|
313
|
+
return storage;
|
314
|
+
}
|
315
|
+
|
316
|
+
rb_raise(rb_eRuntimeError, "unable to convert timeout");
|
317
|
+
}
|
318
|
+
|
319
|
+
static
|
320
|
+
int timeout_nonblocking(struct __kernel_timespec *timespec) {
|
321
|
+
return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
|
322
|
+
}
|
323
|
+
|
324
|
+
struct select_arguments {
|
325
|
+
struct Event_Backend_URing *data;
|
326
|
+
|
327
|
+
int count;
|
328
|
+
struct io_uring_cqe **cqes;
|
329
|
+
|
330
|
+
struct __kernel_timespec storage;
|
331
|
+
struct __kernel_timespec *timeout;
|
332
|
+
};
|
333
|
+
|
334
|
+
static
|
335
|
+
void * select_internal(void *_arguments) {
|
336
|
+
struct select_arguments * arguments = (struct select_arguments *)_arguments;
|
337
|
+
|
338
|
+
arguments->count = io_uring_wait_cqes(&arguments->data->ring, arguments->cqes, 1, arguments->timeout, NULL);
|
339
|
+
|
340
|
+
// If waiting resulted in a timeout, there are 0 events.
|
341
|
+
if (arguments->count == -ETIME) {
|
342
|
+
arguments->count = 0;
|
343
|
+
}
|
344
|
+
|
345
|
+
return NULL;
|
346
|
+
}
|
347
|
+
|
348
|
+
static
|
349
|
+
int select_internal_without_gvl(struct select_arguments *arguments) {
|
350
|
+
rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
351
|
+
|
352
|
+
if (arguments->count < 0) {
|
353
|
+
rb_syserr_fail(-arguments->count, "select_internal_without_gvl:io_uring_wait_cqes");
|
354
|
+
}
|
355
|
+
|
356
|
+
return arguments->count;
|
357
|
+
}
|
358
|
+
|
257
359
|
VALUE Event_Backend_URing_select(VALUE self, VALUE duration) {
|
258
360
|
struct Event_Backend_URing *data = NULL;
|
259
361
|
TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
|
260
362
|
|
261
363
|
struct io_uring_cqe *cqes[URING_MAX_EVENTS];
|
262
|
-
struct __kernel_timespec storage;
|
263
364
|
|
264
|
-
|
265
|
-
|
266
|
-
// fprintf(stderr, "result = %d\n", result);
|
365
|
+
// This is a non-blocking operation:
|
366
|
+
int result = io_uring_peek_batch_cqe(&data->ring, cqes, URING_MAX_EVENTS);
|
267
367
|
|
268
368
|
if (result < 0) {
|
269
369
|
rb_syserr_fail(-result, strerror(-result));
|
270
370
|
} else if (result == 0) {
|
271
|
-
|
371
|
+
// We might need to wait for events:
|
372
|
+
struct select_arguments arguments = {
|
373
|
+
.data = data,
|
374
|
+
.cqes = cqes,
|
375
|
+
.timeout = NULL,
|
376
|
+
};
|
272
377
|
|
273
|
-
|
378
|
+
arguments.timeout = make_timeout(duration, &arguments.storage);
|
274
379
|
|
275
|
-
if (
|
276
|
-
result =
|
277
|
-
} else if (result < 0) {
|
278
|
-
rb_syserr_fail(-result, strerror(-result));
|
380
|
+
if (!timeout_nonblocking(arguments.timeout)) {
|
381
|
+
result = select_internal_without_gvl(&arguments);
|
279
382
|
}
|
280
383
|
}
|
281
384
|
|
385
|
+
// fprintf(stderr, "cqes count=%d\n", result);
|
386
|
+
|
282
387
|
for (int i = 0; i < result; i += 1) {
|
388
|
+
// If the operation was cancelled, or the operation has no user data (fiber):
|
389
|
+
if (cqes[i]->res == -ECANCELED || cqes[i]->user_data == 0) {
|
390
|
+
continue;
|
391
|
+
}
|
392
|
+
|
283
393
|
VALUE fiber = (VALUE)io_uring_cqe_get_data(cqes[i]);
|
284
394
|
VALUE result = INT2NUM(cqes[i]->res);
|
285
395
|
|
286
|
-
// fprintf(stderr, "cqes[i]
|
396
|
+
// fprintf(stderr, "cqes[i] res=%d user_data=%p\n", cqes[i]->res, (void*)cqes[i]->user_data);
|
287
397
|
|
288
|
-
io_uring_cqe_seen(data->ring, cqes[i]);
|
398
|
+
io_uring_cqe_seen(&data->ring, cqes[i]);
|
289
399
|
|
290
400
|
rb_funcall(fiber, id_transfer, 1, result);
|
291
401
|
}
|
@@ -301,10 +411,11 @@ void Init_Event_Backend_URing(VALUE Event_Backend) {
|
|
301
411
|
|
302
412
|
rb_define_alloc_func(Event_Backend_URing, Event_Backend_URing_allocate);
|
303
413
|
rb_define_method(Event_Backend_URing, "initialize", Event_Backend_URing_initialize, 1);
|
414
|
+
rb_define_method(Event_Backend_URing, "close", Event_Backend_URing_close, 0);
|
304
415
|
|
305
416
|
rb_define_method(Event_Backend_URing, "io_wait", Event_Backend_URing_io_wait, 3);
|
306
417
|
rb_define_method(Event_Backend_URing, "select", Event_Backend_URing_select, 1);
|
307
|
-
|
418
|
+
|
308
419
|
rb_define_method(Event_Backend_URing, "io_read", Event_Backend_URing_io_read, 5);
|
309
420
|
rb_define_method(Event_Backend_URing, "io_write", Event_Backend_URing_io_write, 5);
|
310
421
|
}
|