polyphony 0.54.0 → 0.59
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/.gitignore +3 -1
- data/CHANGELOG.md +54 -25
- data/Gemfile.lock +1 -1
- data/TODO.md +0 -3
- data/examples/core/idle_gc.rb +21 -0
- data/examples/io/pipe.rb +11 -0
- data/examples/io/splice_chunks.rb +29 -0
- data/examples/io/stdio.rb +8 -0
- data/ext/polyphony/backend_common.c +288 -0
- data/ext/polyphony/backend_common.h +49 -130
- data/ext/polyphony/backend_io_uring.c +439 -122
- data/ext/polyphony/backend_io_uring_context.c +14 -3
- data/ext/polyphony/backend_io_uring_context.h +11 -11
- data/ext/polyphony/backend_libev.c +463 -94
- data/ext/polyphony/fiber.c +0 -2
- data/ext/polyphony/polyphony.c +17 -22
- data/ext/polyphony/polyphony.h +9 -16
- data/ext/polyphony/polyphony_ext.c +0 -4
- data/ext/polyphony/runqueue.c +35 -72
- data/ext/polyphony/runqueue.h +27 -0
- data/ext/polyphony/thread.c +10 -84
- data/lib/polyphony/extensions/fiber.rb +2 -2
- data/lib/polyphony/extensions/socket.rb +6 -20
- data/lib/polyphony/extensions/thread.rb +8 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +3 -3
- data/test/test_backend.rb +137 -2
- data/test/test_fiber.rb +0 -1
- data/test/test_io.rb +6 -3
- data/test/test_signal.rb +1 -1
- data/test/test_thread.rb +57 -11
- data/test/test_thread_pool.rb +1 -1
- data/test/test_timer.rb +16 -10
- data/test/test_trace.rb +27 -49
- metadata +8 -4
- data/ext/polyphony/tracing.c +0 -11
- data/lib/polyphony/adapters/trace.rb +0 -138
|
@@ -1,17 +1,42 @@
|
|
|
1
|
-
#
|
|
1
|
+
#ifndef BACKEND_COMMON_H
|
|
2
|
+
#define BACKEND_COMMON_H
|
|
2
3
|
|
|
3
4
|
#include "ruby.h"
|
|
4
5
|
#include "ruby/io.h"
|
|
6
|
+
#include "runqueue.h"
|
|
5
7
|
|
|
8
|
+
struct backend_stats {
|
|
9
|
+
int scheduled_fibers;
|
|
10
|
+
int waiting_fibers;
|
|
11
|
+
int pending_ops;
|
|
12
|
+
};
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
struct Backend_base {
|
|
15
|
+
runqueue_t runqueue;
|
|
16
|
+
unsigned int currently_polling;
|
|
17
|
+
unsigned int pending_count;
|
|
18
|
+
double idle_gc_period;
|
|
19
|
+
double idle_gc_last_time;
|
|
20
|
+
VALUE idle_proc;
|
|
21
|
+
VALUE trace_proc;
|
|
22
|
+
};
|
|
11
23
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
void backend_base_initialize(struct Backend_base *base);
|
|
25
|
+
void backend_base_finalize(struct Backend_base *base);
|
|
26
|
+
void backend_base_mark(struct Backend_base *base);
|
|
27
|
+
VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base);
|
|
28
|
+
void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize);
|
|
29
|
+
void backend_trace(struct Backend_base *base, int argc, VALUE *argv);
|
|
30
|
+
|
|
31
|
+
// tracing
|
|
32
|
+
#define SHOULD_TRACE(base) ((base)->trace_proc != Qnil)
|
|
33
|
+
#define TRACE(base, ...) rb_funcall((base)->trace_proc, ID_call, __VA_ARGS__)
|
|
34
|
+
#define COND_TRACE(base, ...) if (SHOULD_TRACE(base)) { TRACE(base, __VA_ARGS__); }
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
|
39
|
+
int pidfd_open(pid_t pid, unsigned int flags);
|
|
15
40
|
#endif
|
|
16
41
|
|
|
17
42
|
//////////////////////////////////////////////////////////////////////
|
|
@@ -26,75 +51,19 @@ struct io_internal_read_struct {
|
|
|
26
51
|
|
|
27
52
|
#define StringValue(v) rb_string_value(&(v))
|
|
28
53
|
|
|
29
|
-
int io_setstrbuf(VALUE *str, long len)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
*str = rb_str_new(0, len);
|
|
35
|
-
return 1;
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
VALUE s = StringValue(*str);
|
|
39
|
-
long clen = RSTRING_LEN(s);
|
|
40
|
-
if (clen >= len) {
|
|
41
|
-
rb_str_modify(s);
|
|
42
|
-
return 0;
|
|
43
|
-
}
|
|
44
|
-
len -= clen;
|
|
45
|
-
}
|
|
46
|
-
rb_str_modify_expand(*str, len);
|
|
47
|
-
return 0;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
#define MAX_REALLOC_GAP 4096
|
|
51
|
-
|
|
52
|
-
inline void io_shrink_read_string(VALUE str, long n) {
|
|
53
|
-
if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
|
|
54
|
-
rb_str_resize(str, n);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
void io_set_read_length(VALUE str, long n, int shrinkable) {
|
|
59
|
-
if (RSTRING_LEN(str) != n) {
|
|
60
|
-
rb_str_modify(str);
|
|
61
|
-
rb_str_set_len(str, n);
|
|
62
|
-
if (shrinkable) io_shrink_read_string(str, n);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
|
67
|
-
if (fptr->encs.enc) {
|
|
68
|
-
return fptr->encs.enc;
|
|
69
|
-
}
|
|
70
|
-
return rb_default_external_encoding();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
|
74
|
-
OBJ_TAINT(str);
|
|
75
|
-
rb_enc_associate(str, io_read_encoding(fptr));
|
|
76
|
-
return str;
|
|
77
|
-
}
|
|
54
|
+
int io_setstrbuf(VALUE *str, long len);
|
|
55
|
+
void io_shrink_read_string(VALUE str, long n);
|
|
56
|
+
void io_set_read_length(VALUE str, long n, int shrinkable);
|
|
57
|
+
rb_encoding* io_read_encoding(rb_io_t *fptr);
|
|
58
|
+
VALUE io_enc_str(VALUE str, rb_io_t *fptr);
|
|
78
59
|
|
|
79
60
|
//////////////////////////////////////////////////////////////////////
|
|
80
61
|
//////////////////////////////////////////////////////////////////////
|
|
81
62
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
backend->pending_count++;
|
|
85
|
-
ret = Thread_switch_fiber(rb_thread_current());
|
|
86
|
-
backend->pending_count--;
|
|
87
|
-
RB_GC_GUARD(ret);
|
|
88
|
-
return ret;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
inline VALUE backend_snooze() {
|
|
92
|
-
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
|
93
|
-
return Thread_switch_fiber(rb_thread_current());
|
|
94
|
-
}
|
|
63
|
+
VALUE backend_await(struct Backend_base *backend);
|
|
64
|
+
VALUE backend_snooze();
|
|
95
65
|
|
|
96
66
|
// macros for doing read loops
|
|
97
|
-
|
|
98
67
|
#define READ_LOOP_PREPARE_STR() { \
|
|
99
68
|
str = Qnil; \
|
|
100
69
|
shrinkable = io_setstrbuf(&str, len); \
|
|
@@ -117,63 +86,13 @@ inline VALUE backend_snooze() {
|
|
|
117
86
|
READ_LOOP_PREPARE_STR(); \
|
|
118
87
|
}
|
|
119
88
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
inline double current_time() {
|
|
132
|
-
struct timespec ts;
|
|
133
|
-
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
134
|
-
long long ns = ts.tv_sec;
|
|
135
|
-
ns = ns * 1e9 + ts.tv_nsec;
|
|
136
|
-
double t = ns;
|
|
137
|
-
return t / 1e9;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
inline VALUE backend_timeout_exception(VALUE exception) {
|
|
141
|
-
if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
|
|
142
|
-
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
|
143
|
-
else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
|
|
144
|
-
return rb_funcall(exception, ID_new, 0);
|
|
145
|
-
else
|
|
146
|
-
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
|
147
|
-
}
|
|
89
|
+
void rectify_io_file_pos(rb_io_t *fptr);
|
|
90
|
+
double current_time();
|
|
91
|
+
VALUE backend_timeout_exception(VALUE exception);
|
|
92
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg);
|
|
93
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg);
|
|
94
|
+
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
|
95
|
+
void backend_run_idle_tasks(struct Backend_base *base);
|
|
96
|
+
void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
|
|
148
97
|
|
|
149
|
-
|
|
150
|
-
return rb_yield(arg);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
|
154
|
-
return exception;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
|
158
|
-
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
static VALUE empty_string = Qnil;
|
|
162
|
-
|
|
163
|
-
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
|
164
|
-
switch (RARRAY_LEN(ary)) {
|
|
165
|
-
case 0:
|
|
166
|
-
return Qnil;
|
|
167
|
-
case 1:
|
|
168
|
-
return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
|
|
169
|
-
default:
|
|
170
|
-
if (empty_string == Qnil) {
|
|
171
|
-
empty_string = rb_str_new_literal("");
|
|
172
|
-
rb_global_variable(&empty_string);
|
|
173
|
-
}
|
|
174
|
-
VALUE joined = rb_ary_join(ary, empty_string);
|
|
175
|
-
VALUE result = Backend_send(self, io, joined, flags);
|
|
176
|
-
RB_GC_GUARD(joined);
|
|
177
|
-
return result;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
98
|
+
#endif /* BACKEND_COMMON_H */
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
#include <sys/socket.h>
|
|
5
5
|
#include <sys/uio.h>
|
|
6
6
|
#include <unistd.h>
|
|
7
|
-
#include <fcntl.h>
|
|
8
7
|
#include <netinet/in.h>
|
|
9
8
|
#include <arpa/inet.h>
|
|
10
9
|
#include <stdnoreturn.h>
|
|
@@ -19,39 +18,21 @@
|
|
|
19
18
|
#include "backend_io_uring_context.h"
|
|
20
19
|
#include "ruby/thread.h"
|
|
21
20
|
#include "ruby/io.h"
|
|
21
|
+
#include "backend_common.h"
|
|
22
22
|
|
|
23
23
|
VALUE SYM_io_uring;
|
|
24
|
+
VALUE SYM_send;
|
|
25
|
+
VALUE SYM_splice;
|
|
26
|
+
VALUE SYM_write;
|
|
24
27
|
|
|
25
28
|
#ifdef POLYPHONY_UNSET_NONBLOCK
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// One of the changes introduced in Ruby 3.0 as part of the work on the
|
|
29
|
-
// FiberScheduler interface is that all created sockets are marked as
|
|
30
|
-
// non-blocking. This prevents the io_uring backend from working correctly,
|
|
31
|
-
// since it will return an EAGAIN error just like a normal syscall. So here
|
|
32
|
-
// instead of setting O_NONBLOCK (which is required for the libev backend), we
|
|
33
|
-
// unset it.
|
|
34
|
-
inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
|
|
35
|
-
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
|
36
|
-
if (is_nonblocking == Qfalse) return;
|
|
37
|
-
|
|
38
|
-
rb_ivar_set(io, ID_ivar_is_nonblocking, Qfalse);
|
|
39
|
-
|
|
40
|
-
int oflags = fcntl(fptr->fd, F_GETFL);
|
|
41
|
-
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
|
42
|
-
oflags &= !O_NONBLOCK;
|
|
43
|
-
fcntl(fptr->fd, F_SETFL, oflags);
|
|
44
|
-
}
|
|
29
|
+
#define io_unset_nonblock(fptr, io) io_verify_blocking_mode(fptr, io, Qtrue)
|
|
45
30
|
#else
|
|
46
|
-
// NOP
|
|
47
31
|
#define io_unset_nonblock(fptr, io)
|
|
48
32
|
#endif
|
|
49
33
|
|
|
50
34
|
typedef struct Backend_t {
|
|
51
|
-
|
|
52
|
-
unsigned int currently_polling;
|
|
53
|
-
unsigned int pending_count;
|
|
54
|
-
unsigned int poll_no_wait_count;
|
|
35
|
+
struct Backend_base base;
|
|
55
36
|
|
|
56
37
|
// implementation-specific fields
|
|
57
38
|
struct io_uring ring;
|
|
@@ -61,7 +42,15 @@ typedef struct Backend_t {
|
|
|
61
42
|
int event_fd;
|
|
62
43
|
} Backend_t;
|
|
63
44
|
|
|
64
|
-
|
|
45
|
+
static void Backend_mark(void *ptr) {
|
|
46
|
+
Backend_t *backend = ptr;
|
|
47
|
+
backend_base_mark(&backend->base);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static void Backend_free(void *ptr) {
|
|
51
|
+
Backend_t *backend = ptr;
|
|
52
|
+
backend_base_finalize(&backend->base);
|
|
53
|
+
}
|
|
65
54
|
|
|
66
55
|
static size_t Backend_size(const void *ptr) {
|
|
67
56
|
return sizeof(Backend_t);
|
|
@@ -69,7 +58,7 @@ static size_t Backend_size(const void *ptr) {
|
|
|
69
58
|
|
|
70
59
|
static const rb_data_type_t Backend_type = {
|
|
71
60
|
"IOUringBackend",
|
|
72
|
-
{
|
|
61
|
+
{Backend_mark, Backend_free, Backend_size,},
|
|
73
62
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
74
63
|
};
|
|
75
64
|
|
|
@@ -86,9 +75,7 @@ static VALUE Backend_initialize(VALUE self) {
|
|
|
86
75
|
Backend_t *backend;
|
|
87
76
|
GetBackend(self, backend);
|
|
88
77
|
|
|
89
|
-
backend->
|
|
90
|
-
backend->pending_count = 0;
|
|
91
|
-
backend->poll_no_wait_count = 0;
|
|
78
|
+
backend_base_initialize(&backend->base);
|
|
92
79
|
backend->pending_sqes = 0;
|
|
93
80
|
backend->prepared_limit = 2048;
|
|
94
81
|
|
|
@@ -116,21 +103,13 @@ VALUE Backend_post_fork(VALUE self) {
|
|
|
116
103
|
io_uring_queue_exit(&backend->ring);
|
|
117
104
|
io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
|
|
118
105
|
context_store_free(&backend->store);
|
|
119
|
-
backend->currently_polling = 0;
|
|
120
|
-
backend->pending_count = 0;
|
|
121
|
-
backend->poll_no_wait_count = 0;
|
|
106
|
+
backend->base.currently_polling = 0;
|
|
107
|
+
backend->base.pending_count = 0;
|
|
122
108
|
backend->pending_sqes = 0;
|
|
123
109
|
|
|
124
110
|
return self;
|
|
125
111
|
}
|
|
126
112
|
|
|
127
|
-
unsigned int Backend_pending_count(VALUE self) {
|
|
128
|
-
Backend_t *backend;
|
|
129
|
-
GetBackend(self, backend);
|
|
130
|
-
|
|
131
|
-
return backend->pending_count;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
113
|
typedef struct poll_context {
|
|
135
114
|
struct io_uring *ring;
|
|
136
115
|
struct io_uring_cqe *cqe;
|
|
@@ -154,18 +133,11 @@ static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe,
|
|
|
154
133
|
op_context_t *ctx = io_uring_cqe_get_data(cqe);
|
|
155
134
|
if (!ctx) return;
|
|
156
135
|
|
|
136
|
+
// printf("cqe ctx %p id: %d result: %d (%s, ref_count: %d)\n", ctx, ctx->id, cqe->res, op_type_to_str(ctx->type), ctx->ref_count);
|
|
157
137
|
ctx->result = cqe->res;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// completion, so we can release the context
|
|
162
|
-
context_store_release(&backend->store, ctx);
|
|
163
|
-
else {
|
|
164
|
-
// otherwise, we mark it as completed, schedule the fiber and let it deal
|
|
165
|
-
// with releasing the context
|
|
166
|
-
ctx->completed = 1;
|
|
167
|
-
if (ctx->result != -ECANCELED) Fiber_make_runnable(ctx->fiber, ctx->resume_value);
|
|
168
|
-
}
|
|
138
|
+
if (ctx->ref_count == 2 && ctx->result != -ECANCELED && ctx->fiber)
|
|
139
|
+
Fiber_make_runnable(ctx->fiber, ctx->resume_value);
|
|
140
|
+
context_store_release(&backend->store, ctx);
|
|
169
141
|
}
|
|
170
142
|
|
|
171
143
|
// adapted from io_uring_peek_batch_cqe in queue.c
|
|
@@ -205,48 +177,70 @@ void io_uring_backend_poll(Backend_t *backend) {
|
|
|
205
177
|
io_uring_submit(&backend->ring);
|
|
206
178
|
}
|
|
207
179
|
|
|
208
|
-
backend->currently_polling = 1;
|
|
180
|
+
backend->base.currently_polling = 1;
|
|
209
181
|
rb_thread_call_without_gvl(io_uring_backend_poll_without_gvl, (void *)&poll_ctx, RUBY_UBF_IO, 0);
|
|
210
|
-
backend->currently_polling = 0;
|
|
182
|
+
backend->base.currently_polling = 0;
|
|
211
183
|
if (poll_ctx.result < 0) return;
|
|
212
184
|
|
|
213
185
|
io_uring_backend_handle_completion(poll_ctx.cqe, backend);
|
|
214
186
|
io_uring_cqe_seen(&backend->ring, poll_ctx.cqe);
|
|
215
187
|
}
|
|
216
188
|
|
|
217
|
-
VALUE Backend_poll(VALUE self, VALUE
|
|
218
|
-
int
|
|
189
|
+
inline VALUE Backend_poll(VALUE self, VALUE blocking) {
|
|
190
|
+
int is_blocking = blocking == Qtrue;
|
|
219
191
|
Backend_t *backend;
|
|
220
192
|
GetBackend(self, backend);
|
|
221
193
|
|
|
222
|
-
if (
|
|
223
|
-
backend->poll_no_wait_count++;
|
|
224
|
-
if (backend->poll_no_wait_count < 10) return self;
|
|
225
|
-
|
|
226
|
-
long runnable_count = Runqueue_len(runqueue);
|
|
227
|
-
if (backend->poll_no_wait_count < runnable_count) return self;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
backend->poll_no_wait_count = 0;
|
|
231
|
-
|
|
232
|
-
if (is_nowait && backend->pending_sqes) {
|
|
194
|
+
if (!is_blocking && backend->pending_sqes) {
|
|
233
195
|
backend->pending_sqes = 0;
|
|
234
196
|
io_uring_submit(&backend->ring);
|
|
235
197
|
}
|
|
236
198
|
|
|
237
|
-
COND_TRACE(2, SYM_fiber_event_poll_enter,
|
|
238
|
-
if (
|
|
199
|
+
COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
|
|
200
|
+
if (is_blocking) io_uring_backend_poll(backend);
|
|
239
201
|
io_uring_backend_handle_ready_cqes(backend);
|
|
240
|
-
COND_TRACE(2, SYM_fiber_event_poll_leave,
|
|
202
|
+
COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
|
|
241
203
|
|
|
242
204
|
return self;
|
|
243
205
|
}
|
|
244
206
|
|
|
207
|
+
inline void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize) {
|
|
208
|
+
Backend_t *backend;
|
|
209
|
+
GetBackend(self, backend);
|
|
210
|
+
|
|
211
|
+
backend_base_schedule_fiber(thread, self, &backend->base, fiber, value, prioritize);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
|
|
215
|
+
Backend_t *backend;
|
|
216
|
+
GetBackend(self, backend);
|
|
217
|
+
|
|
218
|
+
runqueue_delete(&backend->base.runqueue, fiber);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
inline VALUE Backend_switch_fiber(VALUE self) {
|
|
222
|
+
Backend_t *backend;
|
|
223
|
+
GetBackend(self, backend);
|
|
224
|
+
|
|
225
|
+
return backend_base_switch_fiber(self, &backend->base);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
inline struct backend_stats Backend_stats(VALUE self) {
|
|
229
|
+
Backend_t *backend;
|
|
230
|
+
GetBackend(self, backend);
|
|
231
|
+
|
|
232
|
+
return (struct backend_stats){
|
|
233
|
+
.scheduled_fibers = runqueue_len(&backend->base.runqueue),
|
|
234
|
+
.waiting_fibers = 0,
|
|
235
|
+
.pending_ops = backend->base.pending_count
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
245
239
|
VALUE Backend_wakeup(VALUE self) {
|
|
246
240
|
Backend_t *backend;
|
|
247
241
|
GetBackend(self, backend);
|
|
248
242
|
|
|
249
|
-
if (backend->currently_polling) {
|
|
243
|
+
if (backend->base.currently_polling) {
|
|
250
244
|
// Since we're currently blocking while waiting for a completion, we add a
|
|
251
245
|
// NOP which would cause the io_uring_enter syscall to return
|
|
252
246
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
@@ -277,16 +271,17 @@ int io_uring_backend_defer_submit_and_await(
|
|
|
277
271
|
{
|
|
278
272
|
VALUE switchpoint_result = Qnil;
|
|
279
273
|
|
|
280
|
-
|
|
281
|
-
|
|
274
|
+
if (sqe) {
|
|
275
|
+
io_uring_sqe_set_data(sqe, ctx);
|
|
276
|
+
io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
|
|
277
|
+
}
|
|
282
278
|
io_uring_backend_defer_submit(backend);
|
|
283
279
|
|
|
284
|
-
switchpoint_result = backend_await(backend);
|
|
280
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
|
285
281
|
|
|
286
|
-
if (
|
|
282
|
+
if (ctx->ref_count > 1) {
|
|
283
|
+
// op was not completed (an exception was raised), so we need to cancel it
|
|
287
284
|
ctx->result = -ECANCELED;
|
|
288
|
-
|
|
289
|
-
// op was not completed, so we need to cancel it
|
|
290
285
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
291
286
|
io_uring_prep_cancel(sqe, ctx, 0);
|
|
292
287
|
backend->pending_sqes = 0;
|
|
@@ -300,7 +295,7 @@ int io_uring_backend_defer_submit_and_await(
|
|
|
300
295
|
}
|
|
301
296
|
|
|
302
297
|
VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
|
|
303
|
-
op_context_t *ctx =
|
|
298
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_POLL);
|
|
304
299
|
VALUE resumed_value = Qnil;
|
|
305
300
|
|
|
306
301
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
@@ -332,14 +327,14 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
|
332
327
|
|
|
333
328
|
while (1) {
|
|
334
329
|
VALUE resume_value = Qnil;
|
|
335
|
-
op_context_t *ctx =
|
|
330
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
|
336
331
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
337
332
|
io_uring_prep_read(sqe, fptr->fd, buf, buffer_size - total, -1);
|
|
338
333
|
|
|
339
334
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
340
|
-
|
|
335
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
341
336
|
RAISE_IF_EXCEPTION(resume_value);
|
|
342
|
-
if (!
|
|
337
|
+
if (!completed) return resume_value;
|
|
343
338
|
RB_GC_GUARD(resume_value);
|
|
344
339
|
|
|
345
340
|
if (result < 0)
|
|
@@ -393,14 +388,14 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
|
393
388
|
|
|
394
389
|
while (1) {
|
|
395
390
|
VALUE resume_value = Qnil;
|
|
396
|
-
op_context_t *ctx =
|
|
391
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
|
397
392
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
398
393
|
io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
|
|
399
394
|
|
|
400
395
|
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
401
|
-
|
|
396
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
402
397
|
RAISE_IF_EXCEPTION(resume_value);
|
|
403
|
-
if (!
|
|
398
|
+
if (!completed) return resume_value;
|
|
404
399
|
RB_GC_GUARD(resume_value);
|
|
405
400
|
|
|
406
401
|
if (result < 0)
|
|
@@ -440,14 +435,14 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
|
440
435
|
|
|
441
436
|
while (1) {
|
|
442
437
|
VALUE resume_value = Qnil;
|
|
443
|
-
op_context_t *ctx =
|
|
438
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
|
444
439
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
445
440
|
io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
|
|
446
441
|
|
|
447
442
|
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
448
|
-
|
|
443
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
449
444
|
RAISE_IF_EXCEPTION(resume_value);
|
|
450
|
-
if (!
|
|
445
|
+
if (!completed) return resume_value;
|
|
451
446
|
RB_GC_GUARD(resume_value);
|
|
452
447
|
|
|
453
448
|
if (result < 0)
|
|
@@ -483,14 +478,14 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
|
483
478
|
|
|
484
479
|
while (left > 0) {
|
|
485
480
|
VALUE resume_value = Qnil;
|
|
486
|
-
op_context_t *ctx =
|
|
481
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
|
|
487
482
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
488
|
-
io_uring_prep_write(sqe, fptr->fd, buf, left,
|
|
483
|
+
io_uring_prep_write(sqe, fptr->fd, buf, left, 0);
|
|
489
484
|
|
|
490
485
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
491
|
-
|
|
486
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
492
487
|
RAISE_IF_EXCEPTION(resume_value);
|
|
493
|
-
if (!
|
|
488
|
+
if (!completed) return resume_value;
|
|
494
489
|
RB_GC_GUARD(resume_value);
|
|
495
490
|
|
|
496
491
|
if (result < 0)
|
|
@@ -532,17 +527,17 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
532
527
|
|
|
533
528
|
while (1) {
|
|
534
529
|
VALUE resume_value = Qnil;
|
|
535
|
-
op_context_t *ctx =
|
|
530
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
|
|
536
531
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
537
532
|
io_uring_prep_writev(sqe, fptr->fd, iov_ptr, iov_count, -1);
|
|
538
533
|
|
|
539
534
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
540
|
-
|
|
535
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
541
536
|
if (TEST_EXCEPTION(resume_value)) {
|
|
542
537
|
free(iov);
|
|
543
538
|
RAISE_EXCEPTION(resume_value);
|
|
544
539
|
}
|
|
545
|
-
if (!
|
|
540
|
+
if (!completed) {
|
|
546
541
|
free(iov);
|
|
547
542
|
return resume_value;
|
|
548
543
|
}
|
|
@@ -605,14 +600,14 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
|
|
|
605
600
|
|
|
606
601
|
while (1) {
|
|
607
602
|
VALUE resume_value = Qnil;
|
|
608
|
-
op_context_t *ctx =
|
|
603
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
|
609
604
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
610
605
|
io_uring_prep_recv(sqe, fptr->fd, buf, len - total, 0);
|
|
611
606
|
|
|
612
607
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
613
|
-
|
|
608
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
614
609
|
RAISE_IF_EXCEPTION(resume_value);
|
|
615
|
-
if (!
|
|
610
|
+
if (!completed) return resume_value;
|
|
616
611
|
RB_GC_GUARD(resume_value);
|
|
617
612
|
|
|
618
613
|
if (result < 0)
|
|
@@ -652,14 +647,14 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
|
|
|
652
647
|
|
|
653
648
|
while (1) {
|
|
654
649
|
VALUE resume_value = Qnil;
|
|
655
|
-
op_context_t *ctx =
|
|
650
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
|
656
651
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
657
652
|
io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
|
|
658
653
|
|
|
659
654
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
660
|
-
|
|
655
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
661
656
|
RAISE_IF_EXCEPTION(resume_value);
|
|
662
|
-
if (!
|
|
657
|
+
if (!completed) return resume_value;
|
|
663
658
|
RB_GC_GUARD(resume_value);
|
|
664
659
|
|
|
665
660
|
if (result < 0)
|
|
@@ -698,14 +693,14 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
|
|
|
698
693
|
|
|
699
694
|
while (1) {
|
|
700
695
|
VALUE resume_value = Qnil;
|
|
701
|
-
op_context_t *ctx =
|
|
696
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
|
702
697
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
703
698
|
io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
|
|
704
699
|
|
|
705
700
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
706
|
-
|
|
701
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
707
702
|
RAISE_IF_EXCEPTION(resume_value);
|
|
708
|
-
if (!
|
|
703
|
+
if (!completed) return resume_value;
|
|
709
704
|
RB_GC_GUARD(resume_value);
|
|
710
705
|
|
|
711
706
|
if (result < 0)
|
|
@@ -741,14 +736,14 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
|
741
736
|
|
|
742
737
|
while (left > 0) {
|
|
743
738
|
VALUE resume_value = Qnil;
|
|
744
|
-
op_context_t *ctx =
|
|
739
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
|
|
745
740
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
746
741
|
io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
|
|
747
742
|
|
|
748
743
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
749
|
-
|
|
744
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
750
745
|
RAISE_IF_EXCEPTION(resume_value);
|
|
751
|
-
if (!
|
|
746
|
+
if (!completed) return resume_value;
|
|
752
747
|
RB_GC_GUARD(resume_value);
|
|
753
748
|
|
|
754
749
|
if (result < 0)
|
|
@@ -775,14 +770,14 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
|
|
|
775
770
|
|
|
776
771
|
while (1) {
|
|
777
772
|
VALUE resume_value = Qnil;
|
|
778
|
-
op_context_t *ctx =
|
|
773
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
|
|
779
774
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
780
775
|
io_uring_prep_accept(sqe, fptr->fd, &addr, &len, 0);
|
|
781
776
|
|
|
782
777
|
int fd = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
783
|
-
|
|
778
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
784
779
|
RAISE_IF_EXCEPTION(resume_value);
|
|
785
|
-
if (!
|
|
780
|
+
if (!completed) return resume_value;
|
|
786
781
|
RB_GC_GUARD(resume_value);
|
|
787
782
|
|
|
788
783
|
if (fd < 0)
|
|
@@ -846,14 +841,14 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
|
|
|
846
841
|
VALUE resume_value = Qnil;
|
|
847
842
|
|
|
848
843
|
while (1) {
|
|
849
|
-
op_context_t *ctx =
|
|
844
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
|
|
850
845
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
851
846
|
io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
|
|
852
847
|
|
|
853
848
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
854
|
-
|
|
849
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
855
850
|
RAISE_IF_EXCEPTION(resume_value);
|
|
856
|
-
if (!
|
|
851
|
+
if (!completed) return resume_value;
|
|
857
852
|
|
|
858
853
|
if (result < 0)
|
|
859
854
|
rb_syserr_fail(-result, strerror(-result));
|
|
@@ -897,13 +892,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
|
897
892
|
addr.sin_port = htons(NUM2INT(port));
|
|
898
893
|
|
|
899
894
|
VALUE resume_value = Qnil;
|
|
900
|
-
op_context_t *ctx =
|
|
895
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_CONNECT);
|
|
901
896
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
902
897
|
io_uring_prep_connect(sqe, fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
|
|
903
898
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
904
|
-
|
|
899
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
905
900
|
RAISE_IF_EXCEPTION(resume_value);
|
|
906
|
-
if (!
|
|
901
|
+
if (!completed) return resume_value;
|
|
907
902
|
RB_GC_GUARD(resume_value);
|
|
908
903
|
|
|
909
904
|
if (result < 0) rb_syserr_fail(-result, strerror(-result));
|
|
@@ -943,12 +938,10 @@ int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duratio
|
|
|
943
938
|
struct __kernel_timespec ts = double_to_timespec(duration);
|
|
944
939
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
945
940
|
|
|
946
|
-
op_context_t *ctx =
|
|
941
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
|
|
947
942
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
|
948
|
-
|
|
949
943
|
io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
|
|
950
|
-
|
|
951
|
-
return ctx->completed;
|
|
944
|
+
return context_store_release(&backend->store, ctx);
|
|
952
945
|
}
|
|
953
946
|
|
|
954
947
|
VALUE Backend_sleep(VALUE self, VALUE duration) {
|
|
@@ -996,7 +989,7 @@ struct Backend_timeout_ctx {
|
|
|
996
989
|
|
|
997
990
|
VALUE Backend_timeout_ensure(VALUE arg) {
|
|
998
991
|
struct Backend_timeout_ctx *timeout_ctx = (struct Backend_timeout_ctx *)arg;
|
|
999
|
-
if (
|
|
992
|
+
if (timeout_ctx->ctx->ref_count) {
|
|
1000
993
|
timeout_ctx->ctx->result = -ECANCELED;
|
|
1001
994
|
|
|
1002
995
|
// op was not completed, so we need to cancel it
|
|
@@ -1005,7 +998,7 @@ VALUE Backend_timeout_ensure(VALUE arg) {
|
|
|
1005
998
|
timeout_ctx->backend->pending_sqes = 0;
|
|
1006
999
|
io_uring_submit(&timeout_ctx->backend->ring);
|
|
1007
1000
|
}
|
|
1008
|
-
|
|
1001
|
+
context_store_release(&timeout_ctx->backend->store, timeout_ctx->ctx);
|
|
1009
1002
|
return Qnil;
|
|
1010
1003
|
}
|
|
1011
1004
|
|
|
@@ -1023,7 +1016,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
|
|
1023
1016
|
|
|
1024
1017
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
1025
1018
|
|
|
1026
|
-
op_context_t *ctx =
|
|
1019
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
|
|
1027
1020
|
ctx->resume_value = timeout;
|
|
1028
1021
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
|
1029
1022
|
io_uring_sqe_set_data(sqe, ctx);
|
|
@@ -1091,6 +1084,321 @@ VALUE Backend_kind(VALUE self) {
|
|
|
1091
1084
|
return SYM_io_uring;
|
|
1092
1085
|
}
|
|
1093
1086
|
|
|
1087
|
+
struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, VALUE str) {
|
|
1088
|
+
rb_io_t *fptr;
|
|
1089
|
+
VALUE underlying_io;
|
|
1090
|
+
|
|
1091
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
1092
|
+
if (underlying_io != Qnil) io = underlying_io;
|
|
1093
|
+
io = rb_io_get_write_io(io);
|
|
1094
|
+
GetOpenFile(io, fptr);
|
|
1095
|
+
io_unset_nonblock(fptr, io);
|
|
1096
|
+
|
|
1097
|
+
char *buf = StringValuePtr(str);
|
|
1098
|
+
long len = RSTRING_LEN(str);
|
|
1099
|
+
|
|
1100
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
1101
|
+
io_uring_prep_write(sqe, fptr->fd, buf, len, 0);
|
|
1102
|
+
return sqe;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VALUE str, VALUE flags) {
|
|
1106
|
+
rb_io_t *fptr;
|
|
1107
|
+
VALUE underlying_io;
|
|
1108
|
+
|
|
1109
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
1110
|
+
if (underlying_io != Qnil) io = underlying_io;
|
|
1111
|
+
io = rb_io_get_write_io(io);
|
|
1112
|
+
GetOpenFile(io, fptr);
|
|
1113
|
+
io_unset_nonblock(fptr, io);
|
|
1114
|
+
|
|
1115
|
+
char *buf = StringValuePtr(str);
|
|
1116
|
+
long len = RSTRING_LEN(str);
|
|
1117
|
+
int flags_int = NUM2INT(flags);
|
|
1118
|
+
|
|
1119
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
1120
|
+
io_uring_prep_send(sqe, fptr->fd, buf, len, flags_int);
|
|
1121
|
+
return sqe;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen) {
|
|
1125
|
+
rb_io_t *src_fptr;
|
|
1126
|
+
rb_io_t *dest_fptr;
|
|
1127
|
+
VALUE underlying_io;
|
|
1128
|
+
|
|
1129
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
1130
|
+
if (underlying_io != Qnil) src = underlying_io;
|
|
1131
|
+
GetOpenFile(src, src_fptr);
|
|
1132
|
+
io_unset_nonblock(src_fptr, src);
|
|
1133
|
+
|
|
1134
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
1135
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
|
1136
|
+
dest = rb_io_get_write_io(dest);
|
|
1137
|
+
GetOpenFile(dest, dest_fptr);
|
|
1138
|
+
io_unset_nonblock(dest_fptr, dest);
|
|
1139
|
+
|
|
1140
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
1141
|
+
io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
|
|
1142
|
+
return sqe;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1146
|
+
VALUE resume_value = Qnil;
|
|
1147
|
+
unsigned int sqe_count = 0;
|
|
1148
|
+
struct io_uring_sqe *last_sqe = 0;
|
|
1149
|
+
Backend_t *backend;
|
|
1150
|
+
GetBackend(self, backend);
|
|
1151
|
+
if (argc == 0) return resume_value;
|
|
1152
|
+
|
|
1153
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_CHAIN);
|
|
1154
|
+
for (int i = 0; i < argc; i++) {
|
|
1155
|
+
VALUE op = argv[i];
|
|
1156
|
+
VALUE op_type = RARRAY_AREF(op, 0);
|
|
1157
|
+
VALUE op_len = RARRAY_LEN(op);
|
|
1158
|
+
|
|
1159
|
+
if (op_type == SYM_write && op_len == 3) {
|
|
1160
|
+
last_sqe = Backend_chain_prepare_write(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
|
|
1161
|
+
}
|
|
1162
|
+
else if (op_type == SYM_send && op_len == 4)
|
|
1163
|
+
last_sqe = Backend_chain_prepare_send(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
|
1164
|
+
else if (op_type == SYM_splice && op_len == 4)
|
|
1165
|
+
last_sqe = Backend_chain_prepare_splice(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
|
1166
|
+
else {
|
|
1167
|
+
if (sqe_count) {
|
|
1168
|
+
io_uring_sqe_set_data(last_sqe, ctx);
|
|
1169
|
+
io_uring_sqe_set_flags(last_sqe, IOSQE_ASYNC);
|
|
1170
|
+
|
|
1171
|
+
ctx->ref_count = sqe_count;
|
|
1172
|
+
ctx->result = -ECANCELED;
|
|
1173
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
1174
|
+
io_uring_prep_cancel(sqe, ctx, 0);
|
|
1175
|
+
backend->pending_sqes = 0;
|
|
1176
|
+
io_uring_submit(&backend->ring);
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
ctx->ref_count = 1;
|
|
1180
|
+
context_store_release(&backend->store, ctx);
|
|
1181
|
+
}
|
|
1182
|
+
rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
io_uring_sqe_set_data(last_sqe, ctx);
|
|
1186
|
+
unsigned int flags = (i == argc - 1) ? IOSQE_ASYNC : IOSQE_ASYNC & IOSQE_IO_LINK;
|
|
1187
|
+
io_uring_sqe_set_flags(last_sqe, flags);
|
|
1188
|
+
sqe_count++;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
ctx->ref_count = sqe_count + 1;
|
|
1192
|
+
io_uring_backend_defer_submit(backend);
|
|
1193
|
+
resume_value = backend_await((struct Backend_base *)backend);
|
|
1194
|
+
int result = ctx->result;
|
|
1195
|
+
int completed = context_store_release(&backend->store, ctx);
|
|
1196
|
+
if (!completed) {
|
|
1197
|
+
// op was not completed (an exception was raised), so we need to cancel it
|
|
1198
|
+
ctx->result = -ECANCELED;
|
|
1199
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
1200
|
+
io_uring_prep_cancel(sqe, ctx, 0);
|
|
1201
|
+
backend->pending_sqes = 0;
|
|
1202
|
+
io_uring_submit(&backend->ring);
|
|
1203
|
+
RAISE_IF_EXCEPTION(resume_value);
|
|
1204
|
+
return resume_value;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
RB_GC_GUARD(resume_value);
|
|
1208
|
+
return INT2NUM(result);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
|
|
1212
|
+
Backend_t *backend;
|
|
1213
|
+
GetBackend(self, backend);
|
|
1214
|
+
backend->base.idle_gc_period = NUM2DBL(period);
|
|
1215
|
+
backend->base.idle_gc_last_time = current_time();
|
|
1216
|
+
return self;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
VALUE Backend_idle_proc_set(VALUE self, VALUE block) {
|
|
1220
|
+
Backend_t *backend;
|
|
1221
|
+
GetBackend(self, backend);
|
|
1222
|
+
backend->base.idle_proc = block;
|
|
1223
|
+
return self;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
inline VALUE Backend_run_idle_tasks(VALUE self) {
|
|
1227
|
+
Backend_t *backend;
|
|
1228
|
+
GetBackend(self, backend);
|
|
1229
|
+
backend_run_idle_tasks(&backend->base);
|
|
1230
|
+
return self;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
static inline void splice_chunks_prep_write(op_context_t *ctx, struct io_uring_sqe *sqe, int fd, VALUE str) {
|
|
1234
|
+
char *buf = RSTRING_PTR(str);
|
|
1235
|
+
int len = RSTRING_LEN(str);
|
|
1236
|
+
io_uring_prep_write(sqe, fd, buf, len, 0);
|
|
1237
|
+
// io_uring_prep_send(sqe, fd, buf, len, 0);
|
|
1238
|
+
io_uring_sqe_set_data(sqe, ctx);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
static inline void splice_chunks_prep_splice(op_context_t *ctx, struct io_uring_sqe *sqe, int src, int dest, int maxlen) {
|
|
1242
|
+
io_uring_prep_splice(sqe, src, -1, dest, -1, maxlen, 0);
|
|
1243
|
+
io_uring_sqe_set_data(sqe, ctx);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
static inline void splice_chunks_get_sqe(
|
|
1247
|
+
Backend_t *backend,
|
|
1248
|
+
op_context_t **ctx,
|
|
1249
|
+
struct io_uring_sqe **sqe,
|
|
1250
|
+
enum op_type type
|
|
1251
|
+
)
|
|
1252
|
+
{
|
|
1253
|
+
if (*ctx) {
|
|
1254
|
+
if (*sqe) (*sqe)->flags |= IOSQE_IO_LINK;
|
|
1255
|
+
(*ctx)->ref_count++;
|
|
1256
|
+
}
|
|
1257
|
+
else
|
|
1258
|
+
*ctx = context_store_acquire(&backend->store, type);
|
|
1259
|
+
(*sqe) = io_uring_get_sqe(&backend->ring);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
|
|
1263
|
+
ctx->result = -ECANCELED;
|
|
1264
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
1265
|
+
io_uring_prep_cancel(sqe, ctx, 0);
|
|
1266
|
+
backend->pending_sqes = 0;
|
|
1267
|
+
io_uring_submit(&backend->ring);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
static inline int splice_chunks_await_ops(
|
|
1271
|
+
Backend_t *backend,
|
|
1272
|
+
op_context_t **ctx,
|
|
1273
|
+
int *result,
|
|
1274
|
+
VALUE *switchpoint_result
|
|
1275
|
+
)
|
|
1276
|
+
{
|
|
1277
|
+
int res = io_uring_backend_defer_submit_and_await(backend, 0, *ctx, switchpoint_result);
|
|
1278
|
+
if (result) (*result) = res;
|
|
1279
|
+
int completed = context_store_release(&backend->store, *ctx);
|
|
1280
|
+
if (!completed) {
|
|
1281
|
+
splice_chunks_cancel(backend, *ctx);
|
|
1282
|
+
if (TEST_EXCEPTION(*switchpoint_result)) return 1;
|
|
1283
|
+
}
|
|
1284
|
+
*ctx = 0;
|
|
1285
|
+
return 0;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
#define SPLICE_CHUNKS_AWAIT_OPS(backend, ctx, result, switchpoint_result) \
|
|
1289
|
+
if (splice_chunks_await_ops(backend, ctx, result, switchpoint_result)) goto error;
|
|
1290
|
+
|
|
1291
|
+
VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
|
|
1292
|
+
Backend_t *backend;
|
|
1293
|
+
GetBackend(self, backend);
|
|
1294
|
+
int total = 0;
|
|
1295
|
+
int err = 0;
|
|
1296
|
+
VALUE switchpoint_result = Qnil;
|
|
1297
|
+
op_context_t *ctx = 0;
|
|
1298
|
+
struct io_uring_sqe *sqe = 0;
|
|
1299
|
+
|
|
1300
|
+
rb_io_t *src_fptr;
|
|
1301
|
+
rb_io_t *dest_fptr;
|
|
1302
|
+
|
|
1303
|
+
VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
1304
|
+
if (underlying_io != Qnil) src = underlying_io;
|
|
1305
|
+
GetOpenFile(src, src_fptr);
|
|
1306
|
+
io_verify_blocking_mode(src_fptr, src, Qtrue);
|
|
1307
|
+
|
|
1308
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
1309
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
|
1310
|
+
dest = rb_io_get_write_io(dest);
|
|
1311
|
+
GetOpenFile(dest, dest_fptr);
|
|
1312
|
+
io_verify_blocking_mode(dest_fptr, dest, Qtrue);
|
|
1313
|
+
|
|
1314
|
+
int maxlen = NUM2INT(chunk_size);
|
|
1315
|
+
VALUE str = Qnil;
|
|
1316
|
+
VALUE chunk_len_value = Qnil;
|
|
1317
|
+
|
|
1318
|
+
int pipefd[2] = { -1, -1 };
|
|
1319
|
+
if (pipe(pipefd) == -1) {
|
|
1320
|
+
err = errno;
|
|
1321
|
+
goto syscallerror;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
if (prefix != Qnil) {
|
|
1325
|
+
splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
|
|
1326
|
+
splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, prefix);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
while (1) {
|
|
1330
|
+
int chunk_len;
|
|
1331
|
+
VALUE chunk_prefix_str = Qnil;
|
|
1332
|
+
VALUE chunk_postfix_str = Qnil;
|
|
1333
|
+
|
|
1334
|
+
splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
|
|
1335
|
+
splice_chunks_prep_splice(ctx, sqe, src_fptr->fd, pipefd[1], maxlen);
|
|
1336
|
+
|
|
1337
|
+
SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, &chunk_len, &switchpoint_result);
|
|
1338
|
+
if (chunk_len == 0) break;
|
|
1339
|
+
|
|
1340
|
+
total += chunk_len;
|
|
1341
|
+
chunk_len_value = INT2NUM(chunk_len);
|
|
1342
|
+
|
|
1343
|
+
|
|
1344
|
+
if (chunk_prefix != Qnil) {
|
|
1345
|
+
chunk_prefix_str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
|
|
1346
|
+
splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
|
|
1347
|
+
splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_prefix_str);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
|
|
1351
|
+
splice_chunks_prep_splice(ctx, sqe, pipefd[0], dest_fptr->fd, chunk_len);
|
|
1352
|
+
|
|
1353
|
+
if (chunk_postfix != Qnil) {
|
|
1354
|
+
chunk_postfix_str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
|
|
1355
|
+
splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
|
|
1356
|
+
splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_postfix_str);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
RB_GC_GUARD(chunk_prefix_str);
|
|
1360
|
+
RB_GC_GUARD(chunk_postfix_str);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
if (postfix != Qnil) {
|
|
1364
|
+
splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
|
|
1365
|
+
splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, postfix);
|
|
1366
|
+
}
|
|
1367
|
+
if (ctx) {
|
|
1368
|
+
SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, 0, &switchpoint_result);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
RB_GC_GUARD(str);
|
|
1372
|
+
RB_GC_GUARD(chunk_len_value);
|
|
1373
|
+
RB_GC_GUARD(switchpoint_result);
|
|
1374
|
+
if (pipefd[0] != -1) close(pipefd[0]);
|
|
1375
|
+
if (pipefd[1] != -1) close(pipefd[1]);
|
|
1376
|
+
return INT2NUM(total);
|
|
1377
|
+
syscallerror:
|
|
1378
|
+
if (pipefd[0] != -1) close(pipefd[0]);
|
|
1379
|
+
if (pipefd[1] != -1) close(pipefd[1]);
|
|
1380
|
+
rb_syserr_fail(err, strerror(err));
|
|
1381
|
+
error:
|
|
1382
|
+
if (pipefd[0] != -1) close(pipefd[0]);
|
|
1383
|
+
if (pipefd[1] != -1) close(pipefd[1]);
|
|
1384
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
VALUE Backend_trace(int argc, VALUE *argv, VALUE self) {
|
|
1388
|
+
Backend_t *backend;
|
|
1389
|
+
GetBackend(self, backend);
|
|
1390
|
+
backend_trace(&backend->base, argc, argv);
|
|
1391
|
+
return self;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
|
|
1395
|
+
Backend_t *backend;
|
|
1396
|
+
GetBackend(self, backend);
|
|
1397
|
+
|
|
1398
|
+
backend->base.trace_proc = block;
|
|
1399
|
+
return self;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1094
1402
|
void Init_Backend() {
|
|
1095
1403
|
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
|
1096
1404
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
|
@@ -1098,10 +1406,16 @@ void Init_Backend() {
|
|
|
1098
1406
|
rb_define_method(cBackend, "initialize", Backend_initialize, 0);
|
|
1099
1407
|
rb_define_method(cBackend, "finalize", Backend_finalize, 0);
|
|
1100
1408
|
rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
|
|
1409
|
+
rb_define_method(cBackend, "trace", Backend_trace, -1);
|
|
1410
|
+
rb_define_method(cBackend, "trace_proc=", Backend_trace_proc_set, 1);
|
|
1101
1411
|
|
|
1102
|
-
rb_define_method(cBackend, "poll", Backend_poll,
|
|
1412
|
+
rb_define_method(cBackend, "poll", Backend_poll, 1);
|
|
1103
1413
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
|
1104
1414
|
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
|
1415
|
+
rb_define_method(cBackend, "chain", Backend_chain, -1);
|
|
1416
|
+
rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
|
|
1417
|
+
rb_define_method(cBackend, "idle_proc=", Backend_idle_proc_set, 1);
|
|
1418
|
+
rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
|
|
1105
1419
|
|
|
1106
1420
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
|
1107
1421
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
|
@@ -1130,6 +1444,9 @@ void Init_Backend() {
|
|
|
1130
1444
|
#endif
|
|
1131
1445
|
|
|
1132
1446
|
SYM_io_uring = ID2SYM(rb_intern("io_uring"));
|
|
1447
|
+
SYM_send = ID2SYM(rb_intern("send"));
|
|
1448
|
+
SYM_splice = ID2SYM(rb_intern("splice"));
|
|
1449
|
+
SYM_write = ID2SYM(rb_intern("write"));
|
|
1133
1450
|
}
|
|
1134
1451
|
|
|
1135
1452
|
#endif // POLYPHONY_BACKEND_LIBURING
|