uringmachine 0.25.0 → 0.26.0
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/CHANGELOG.md +4 -0
- data/TODO.md +15 -5
- data/docs/wroclove.rb.md +52 -0
- data/ext/um/um.c +382 -364
- data/ext/um/um.h +41 -21
- data/ext/um/um_async_op.c +9 -8
- data/ext/um/um_async_op_class.c +3 -3
- data/ext/um/um_op.c +15 -1
- data/ext/um/um_ssl.c +1 -1
- data/ext/um/um_sync.c +18 -11
- data/ext/um/um_utils.c +14 -4
- data/lib/uringmachine/version.rb +1 -1
- data/test/test_async_op.rb +3 -2
- data/test/test_fiber_scheduler.rb +4 -1
- data/test/test_um.rb +289 -9
- metadata +2 -1
data/ext/um/um.h
CHANGED
|
@@ -73,20 +73,31 @@ enum um_op_kind {
|
|
|
73
73
|
OP_READ_MULTISHOT,
|
|
74
74
|
OP_RECV_MULTISHOT,
|
|
75
75
|
OP_TIMEOUT_MULTISHOT,
|
|
76
|
-
OP_SLEEP_MULTISHOT
|
|
77
76
|
};
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
#define
|
|
81
|
-
#define
|
|
82
|
-
#define
|
|
83
|
-
#define
|
|
84
|
-
#define OP_F_MULTISHOT
|
|
85
|
-
#define
|
|
86
|
-
#define
|
|
87
|
-
#define
|
|
88
|
-
#define
|
|
89
|
-
|
|
78
|
+
|
|
79
|
+
#define OP_F_CQE_SEEN (1U << 0) // CQE has been seen
|
|
80
|
+
#define OP_F_CQE_DONE (1U << 1) // CQE has been seen and operation is done
|
|
81
|
+
#define OP_F_SCHEDULED (1U << 2) // op is on runqueue
|
|
82
|
+
#define OP_F_CANCELED (1U << 3) // op is cancelled (disregard CQE results)
|
|
83
|
+
#define OP_F_MULTISHOT (1U << 4) // op is multishot
|
|
84
|
+
#define OP_F_ASYNC (1U << 5) // op is async (no fiber is scheduled to be resumed on completion)
|
|
85
|
+
#define OP_F_TRANSIENT (1U << 6) // op is on transient list (for GC purposes)
|
|
86
|
+
#define OP_F_FREE_IOVECS (1U << 7) // op->iovecs should be freed on release
|
|
87
|
+
#define OP_F_SKIP (1U << 8) // op should be skipped when pulled from runqueue
|
|
88
|
+
|
|
89
|
+
#define OP_F_SELECT_POLLIN (1U << 13) // select POLLIN
|
|
90
|
+
#define OP_F_SELECT_POLLOUT (1U << 14) // select POLLOUT
|
|
91
|
+
#define OP_F_SELECT_POLLPRI (1U << 15) // select POLLPRI
|
|
92
|
+
|
|
93
|
+
#define OP_CQE_SEEN_P(op) ((op)->flags & OP_F_CQE_SEEN)
|
|
94
|
+
#define OP_CQE_DONE_P(op) ((op)->flags & OP_F_CQE_DONE)
|
|
95
|
+
#define OP_SCHEDULED_P(op) ((op)->flags & OP_F_SCHEDULED)
|
|
96
|
+
#define OP_CANCELED_P(op) ((op)->flags & OP_F_CANCELED)
|
|
97
|
+
#define OP_MULTISHOT_P(op) ((op)->flags & OP_F_MULTISHOT)
|
|
98
|
+
#define OP_ASYNC_P(op) ((op)->flags & OP_F_ASYNC)
|
|
99
|
+
#define OP_TRANSIENT_P(op) ((op)->flags & OP_F_TRANSIENT)
|
|
100
|
+
#define OP_SKIP_P(op) ((op)->flags & OP_F_SKIP)
|
|
90
101
|
|
|
91
102
|
struct um_op_result {
|
|
92
103
|
__s32 res;
|
|
@@ -100,6 +111,7 @@ struct um_op {
|
|
|
100
111
|
|
|
101
112
|
enum um_op_kind kind;
|
|
102
113
|
uint flags;
|
|
114
|
+
uint ref_count;
|
|
103
115
|
|
|
104
116
|
VALUE fiber;
|
|
105
117
|
VALUE value;
|
|
@@ -109,7 +121,12 @@ struct um_op {
|
|
|
109
121
|
struct um_op_result *multishot_result_tail;
|
|
110
122
|
uint multishot_result_count;
|
|
111
123
|
|
|
112
|
-
|
|
124
|
+
union {
|
|
125
|
+
struct __kernel_timespec ts; // used for timeout operation
|
|
126
|
+
struct iovec *iovecs; // used for vectorized write/send
|
|
127
|
+
siginfo_t siginfo; // used for waitid
|
|
128
|
+
int int_value; // used for getsockopt
|
|
129
|
+
};
|
|
113
130
|
};
|
|
114
131
|
|
|
115
132
|
struct um_buffer {
|
|
@@ -237,8 +254,12 @@ void um_teardown(struct um *machine);
|
|
|
237
254
|
VALUE um_metrics(struct um *machine, struct um_metrics *metrics);
|
|
238
255
|
|
|
239
256
|
const char * um_op_kind_name(enum um_op_kind kind);
|
|
240
|
-
|
|
241
|
-
|
|
257
|
+
|
|
258
|
+
struct um_op *um_op_acquire(struct um *machine);
|
|
259
|
+
void um_op_release(struct um *machine, struct um_op *op);
|
|
260
|
+
|
|
261
|
+
// struct um_op *um_op_alloc(struct um *machine);
|
|
262
|
+
// void um_op_free(struct um *machine, struct um_op *op);
|
|
242
263
|
void um_op_clear(struct um *machine, struct um_op *op);
|
|
243
264
|
void um_op_transient_add(struct um *machine, struct um_op *op);
|
|
244
265
|
void um_op_transient_remove(struct um *machine, struct um_op *op);
|
|
@@ -264,9 +285,9 @@ VALUE um_raise_exception(VALUE v);
|
|
|
264
285
|
|
|
265
286
|
#define RAISE_IF_EXCEPTION(v) if (unlikely(um_value_is_exception_p(v))) { um_raise_exception(v); }
|
|
266
287
|
|
|
267
|
-
void um_prep_op(struct um *machine, struct um_op *op, enum um_op_kind kind, unsigned flags);
|
|
288
|
+
void um_prep_op(struct um *machine, struct um_op *op, enum um_op_kind kind, uint ref_count, unsigned flags);
|
|
268
289
|
void um_raise_on_error_result(int result);
|
|
269
|
-
|
|
290
|
+
int um_get_buffer_bytes_for_writing(VALUE buffer, const void **base, size_t *size, int raise_on_bad_buffer);
|
|
270
291
|
void * um_prepare_read_buffer(VALUE buffer, ssize_t len, ssize_t ofs);
|
|
271
292
|
void um_update_read_buffer(VALUE buffer, ssize_t buffer_offset, __s32 result);
|
|
272
293
|
int um_setup_buffer_ring(struct um *machine, unsigned size, unsigned count);
|
|
@@ -283,10 +304,9 @@ VALUE um_yield(struct um *machine);
|
|
|
283
304
|
VALUE um_switch(struct um *machine);
|
|
284
305
|
VALUE um_wakeup(struct um *machine);
|
|
285
306
|
void um_cancel_op(struct um *machine, struct um_op *op);
|
|
286
|
-
void
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
#define um_op_completed_p(op) ((op)->flags & OP_F_COMPLETED)
|
|
307
|
+
void um_cancel_op_and_discard_cqe(struct um *machine, struct um_op *op);
|
|
308
|
+
void um_cancel_op_and_await_cqe(struct um *machine, struct um_op *op);
|
|
309
|
+
int um_verify_op_completion(struct um *machine, struct um_op *op, int await_cancelled);
|
|
290
310
|
|
|
291
311
|
void um_schedule(struct um *machine, VALUE fiber, VALUE value);
|
|
292
312
|
VALUE um_timeout(struct um *machine, VALUE interval, VALUE class);
|
data/ext/um/um_async_op.c
CHANGED
|
@@ -5,11 +5,11 @@ VALUE um_prep_timeout(struct um *machine, double interval) {
|
|
|
5
5
|
static ID ID_new = 0;
|
|
6
6
|
if (!ID_new) ID_new = rb_intern("new");
|
|
7
7
|
|
|
8
|
-
struct um_op *op =
|
|
9
|
-
um_prep_op(machine, op, OP_TIMEOUT,
|
|
8
|
+
struct um_op *op = um_op_acquire(machine);
|
|
9
|
+
um_prep_op(machine, op, OP_TIMEOUT, 2, OP_F_ASYNC | OP_F_TRANSIENT);
|
|
10
10
|
op->ts = um_double_to_timespec(interval);
|
|
11
11
|
|
|
12
|
-
VALUE obj = rb_funcall(cAsyncOp,
|
|
12
|
+
VALUE obj = rb_funcall(cAsyncOp, ID_new, 0);
|
|
13
13
|
um_async_op_set(obj, machine, op);
|
|
14
14
|
|
|
15
15
|
RB_OBJ_WRITE(machine->self, &op->async_op, obj);
|
|
@@ -17,18 +17,19 @@ VALUE um_prep_timeout(struct um *machine, double interval) {
|
|
|
17
17
|
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
18
18
|
io_uring_prep_timeout(sqe, &op->ts, 0, 0);
|
|
19
19
|
|
|
20
|
-
um_op_transient_add(machine, op);
|
|
21
|
-
|
|
22
20
|
return obj;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
VALUE um_async_op_await(struct um_async_op *async_op) {
|
|
24
|
+
if (OP_CQE_DONE_P(async_op->op)) return async_op->op->value;
|
|
25
|
+
|
|
26
26
|
RB_OBJ_WRITE(async_op->machine->self, &async_op->op->fiber, rb_fiber_current());
|
|
27
|
-
async_op->op
|
|
27
|
+
um_op_transient_remove(async_op->machine, async_op->op);
|
|
28
|
+
async_op->op->flags &= ~(OP_F_ASYNC | OP_F_TRANSIENT);
|
|
28
29
|
|
|
29
30
|
VALUE ret = um_switch(async_op->machine);
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
um_verify_op_completion(async_op->machine, async_op->op, true);
|
|
32
33
|
|
|
33
34
|
RAISE_IF_EXCEPTION(ret);
|
|
34
35
|
RB_GC_GUARD(ret);
|
data/ext/um/um_async_op_class.c
CHANGED
|
@@ -19,8 +19,8 @@ static void AsyncOp_mark(void *ptr) {
|
|
|
19
19
|
|
|
20
20
|
static void AsyncOp_free(void *ptr) {
|
|
21
21
|
struct um_async_op *async_op = ptr;
|
|
22
|
-
if (async_op->op)
|
|
23
|
-
|
|
22
|
+
if (likely(async_op->op))
|
|
23
|
+
um_op_release(async_op->machine, async_op->op);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
static const rb_data_type_t AsyncOp_type = {
|
|
@@ -63,7 +63,7 @@ inline void raise_on_missing_op(struct um_async_op *async_op) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
inline int async_op_is_done(struct um_async_op *async_op) {
|
|
66
|
-
return (async_op->op
|
|
66
|
+
return OP_CQE_DONE_P(async_op->op);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/* Returns the kind of asynchronous operation.
|
data/ext/um/um_op.c
CHANGED
|
@@ -37,7 +37,6 @@ const char * um_op_kind_name(enum um_op_kind kind) {
|
|
|
37
37
|
case OP_READ_MULTISHOT: return "OP_READ_MULTISHOT";
|
|
38
38
|
case OP_RECV_MULTISHOT: return "OP_RECV_MULTISHOT";
|
|
39
39
|
case OP_TIMEOUT_MULTISHOT: return "OP_TIMEOUT_MULTISHOT";
|
|
40
|
-
case OP_SLEEP_MULTISHOT: return "OP_SLEEP_MULTISHOT";
|
|
41
40
|
default: return "UNKNOWN_OP_KIND";
|
|
42
41
|
}
|
|
43
42
|
}
|
|
@@ -59,6 +58,7 @@ inline void um_op_transient_add(struct um *machine, struct um_op *op) {
|
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
inline void um_op_transient_remove(struct um *machine, struct um_op *op) {
|
|
61
|
+
op->flags &= ~OP_F_TRANSIENT;
|
|
62
62
|
if (op->prev)
|
|
63
63
|
op->prev->next = op->next;
|
|
64
64
|
if (op->next)
|
|
@@ -185,3 +185,17 @@ inline void um_op_free(struct um *machine, struct um_op *op) {
|
|
|
185
185
|
machine->op_freelist = op;
|
|
186
186
|
machine->metrics.ops_free++;
|
|
187
187
|
}
|
|
188
|
+
|
|
189
|
+
inline struct um_op *um_op_acquire(struct um *machine) {
|
|
190
|
+
return um_op_alloc(machine);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
inline void um_op_release(struct um *machine, struct um_op *op) {
|
|
194
|
+
op->ref_count--;
|
|
195
|
+
if (op->ref_count) return;
|
|
196
|
+
|
|
197
|
+
if (op->flags & OP_F_FREE_IOVECS) free(op->iovecs);
|
|
198
|
+
if (op->flags & OP_F_MULTISHOT)
|
|
199
|
+
um_op_multishot_results_clear(machine, op);
|
|
200
|
+
um_op_free(machine, op);
|
|
201
|
+
}
|
data/ext/um/um_ssl.c
CHANGED
|
@@ -97,7 +97,7 @@ int um_ssl_write(struct um *machine, VALUE ssl_obj, VALUE buf, int len) {
|
|
|
97
97
|
SSL *ssl = RTYPEDDATA_GET_DATA(ssl_obj);
|
|
98
98
|
const void *base;
|
|
99
99
|
size_t size;
|
|
100
|
-
um_get_buffer_bytes_for_writing(buf, &base, &size);
|
|
100
|
+
um_get_buffer_bytes_for_writing(buf, &base, &size, true);
|
|
101
101
|
if ((len == (int)-1) || (len > (int)size)) len = (int)size;
|
|
102
102
|
if (unlikely(!len)) return INT2NUM(0);
|
|
103
103
|
|
data/ext/um/um_sync.c
CHANGED
|
@@ -6,35 +6,42 @@
|
|
|
6
6
|
|
|
7
7
|
// The value argument is the current (known) futex value.
|
|
8
8
|
void um_futex_wait(struct um *machine, uint32_t *futex, uint32_t value) {
|
|
9
|
-
struct um_op op;
|
|
10
|
-
um_prep_op(machine,
|
|
11
|
-
struct io_uring_sqe *sqe = um_get_sqe(machine,
|
|
9
|
+
struct um_op *op = um_op_acquire(machine);
|
|
10
|
+
um_prep_op(machine, op, OP_FUTEX_WAIT, 2, 0);
|
|
11
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
12
12
|
io_uring_prep_futex_wait(
|
|
13
13
|
sqe, (uint32_t *)futex, value, FUTEX_BITSET_MATCH_ANY, FUTEX2_SIZE_U32, 0
|
|
14
14
|
);
|
|
15
15
|
|
|
16
16
|
VALUE ret = um_yield(machine);
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
if (unlikely(!OP_CQE_DONE_P(op)))
|
|
19
|
+
um_cancel_op_and_await_cqe(machine, op);
|
|
19
20
|
else {
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
int res = op->result.res;
|
|
22
|
+
if (res != -EAGAIN) {
|
|
23
|
+
um_op_release(machine, op);
|
|
24
|
+
um_raise_on_error_result(res);
|
|
25
|
+
}
|
|
22
26
|
}
|
|
27
|
+
um_op_release(machine, op);
|
|
23
28
|
|
|
24
29
|
RAISE_IF_EXCEPTION(ret);
|
|
25
30
|
RB_GC_GUARD(ret);
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
void um_futex_wake(struct um *machine, uint32_t *futex, uint32_t num_waiters) {
|
|
29
|
-
struct um_op op;
|
|
30
|
-
um_prep_op(machine,
|
|
31
|
-
struct io_uring_sqe *sqe = um_get_sqe(machine,
|
|
34
|
+
struct um_op *op = um_op_acquire(machine);
|
|
35
|
+
um_prep_op(machine, op, OP_FUTEX_WAKE, 2, 0);
|
|
36
|
+
struct io_uring_sqe *sqe = um_get_sqe(machine, op);
|
|
32
37
|
io_uring_prep_futex_wake(
|
|
33
38
|
sqe, (uint32_t *)futex, num_waiters, FUTEX_BITSET_MATCH_ANY, FUTEX2_SIZE_U32, 0
|
|
34
39
|
);
|
|
35
40
|
|
|
36
41
|
VALUE ret = um_yield(machine);
|
|
37
|
-
|
|
42
|
+
|
|
43
|
+
um_verify_op_completion(machine, op, true);
|
|
44
|
+
um_op_release(machine, op);
|
|
38
45
|
|
|
39
46
|
RAISE_IF_EXCEPTION(ret);
|
|
40
47
|
RB_GC_GUARD(ret);
|
data/ext/um/um_utils.c
CHANGED
|
@@ -98,15 +98,21 @@ inline void um_update_read_buffer(VALUE buffer, ssize_t buffer_offset, __s32 res
|
|
|
98
98
|
adjust_read_buffer_len(buffer, result, buffer_offset);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
// returns false if buffer is invalid and raise_on_bad_buffer is false
|
|
102
|
+
inline int um_get_buffer_bytes_for_writing(VALUE buffer, const void **base, size_t *size, int raise_on_bad_buffer) {
|
|
102
103
|
if (TYPE(buffer) == T_STRING) {
|
|
103
104
|
*base = RSTRING_PTR(buffer);
|
|
104
105
|
*size = RSTRING_LEN(buffer);
|
|
105
106
|
}
|
|
106
107
|
else if (IO_BUFFER_P(buffer))
|
|
107
108
|
rb_io_buffer_get_bytes_for_reading(buffer, base, size); // reading *from* buffer
|
|
108
|
-
else
|
|
109
|
-
|
|
109
|
+
else {
|
|
110
|
+
if (raise_on_bad_buffer)
|
|
111
|
+
um_raise_internal_error("Invalid buffer provided");
|
|
112
|
+
else
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
int um_setup_buffer_ring(struct um *machine, unsigned size, unsigned count) {
|
|
@@ -204,7 +210,11 @@ inline struct iovec *um_alloc_iovecs_for_writing(int argc, VALUE *argv, size_t *
|
|
|
204
210
|
size_t len = 0;
|
|
205
211
|
|
|
206
212
|
for (int i = 0; i < argc; i++) {
|
|
207
|
-
um_get_buffer_bytes_for_writing(argv[i], (const void **)&iovecs[i].iov_base, &iovecs[i].iov_len);
|
|
213
|
+
int ok = um_get_buffer_bytes_for_writing(argv[i], (const void **)&iovecs[i].iov_base, &iovecs[i].iov_len, false);
|
|
214
|
+
if (unlikely(!ok)) {
|
|
215
|
+
free(iovecs);
|
|
216
|
+
um_raise_internal_error("Invalid buffer provided");
|
|
217
|
+
}
|
|
208
218
|
len += iovecs[i].iov_len;
|
|
209
219
|
}
|
|
210
220
|
if (total_len) *total_len = len;
|
data/lib/uringmachine/version.rb
CHANGED
data/test/test_async_op.rb
CHANGED
|
@@ -51,11 +51,12 @@ class AsyncOpTest < UMBaseTest
|
|
|
51
51
|
@op.cancel
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
assert_raises(Errno::ECANCELED) {
|
|
55
|
+
@op.await
|
|
56
|
+
}
|
|
55
57
|
|
|
56
58
|
assert_equal 0, machine.metrics[:ops_pending]
|
|
57
59
|
assert_equal true, @op.done?
|
|
58
|
-
assert_equal (-ECANCELED), res
|
|
59
60
|
assert_equal true, @op.cancelled?
|
|
60
61
|
end
|
|
61
62
|
|
|
@@ -393,9 +393,12 @@ class FiberSchedulerTest < UMBaseTest
|
|
|
393
393
|
sleep 0.001
|
|
394
394
|
buf = IO.read(fn)
|
|
395
395
|
end
|
|
396
|
-
assert_equal 2, machine.metrics[:total_ops]
|
|
397
396
|
|
|
398
397
|
@scheduler.join
|
|
398
|
+
metrics = machine.metrics
|
|
399
|
+
assert_in_range 5..12, metrics[:total_ops]
|
|
400
|
+
assert_equal 0, metrics[:ops_pending]
|
|
401
|
+
assert_equal 256, metrics[:ops_free]
|
|
399
402
|
assert_equal 'foobar', buf
|
|
400
403
|
assert_equal({
|
|
401
404
|
fiber: 2,
|