uringmachine 0.28.3 → 0.29.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 +10 -1
- data/TODO.md +29 -35
- data/benchmark/common.rb +6 -6
- data/benchmark/gets.rb +49 -0
- data/benchmark/{read_each.rb → output.rb} +27 -24
- data/docs/design/buffer_pool.md +35 -0
- data/docs/um_api.md +2 -0
- data/ext/um/extconf.rb +6 -5
- data/ext/um/um.c +42 -13
- data/ext/um/um.h +103 -31
- data/ext/um/um_buffer_pool.c +246 -0
- data/ext/um/um_class.c +24 -12
- data/ext/um/um_op.c +29 -13
- data/ext/um/um_ssl.c +24 -27
- data/ext/um/um_stream.c +380 -150
- data/ext/um/um_stream_class.c +119 -63
- data/ext/um/um_utils.c +6 -6
- data/grant-2025/tasks.md +12 -7
- data/lib/uringmachine/fiber_scheduler.rb +36 -10
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +60 -19
- data/test/helper.rb +2 -0
- data/test/test_fiber.rb +93 -12
- data/test/test_fiber_scheduler.rb +5 -47
- data/test/test_stream.rb +447 -125
- data/test/test_um.rb +119 -49
- metadata +5 -4
- data/ext/um/um_buffer.c +0 -49
data/ext/um/um_stream_class.c
CHANGED
|
@@ -3,21 +3,68 @@
|
|
|
3
3
|
VALUE cStream;
|
|
4
4
|
VALUE eStreamRESPError;
|
|
5
5
|
|
|
6
|
+
VALUE SYM_bp_read;
|
|
7
|
+
VALUE SYM_bp_recv;
|
|
8
|
+
VALUE SYM_ssl;
|
|
9
|
+
|
|
10
|
+
inline int stream_has_target_obj_p(struct um_stream *stream) {
|
|
11
|
+
switch (stream->mode) {
|
|
12
|
+
case STREAM_SSL:
|
|
13
|
+
case STREAM_STRING:
|
|
14
|
+
case STREAM_IO_BUFFER:
|
|
15
|
+
return true;
|
|
16
|
+
default:
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
inline void stream_mark_segments(struct um_stream *stream) {
|
|
22
|
+
struct um_segment *curr = stream->head;
|
|
23
|
+
while (curr) {
|
|
24
|
+
// rb_gc_mark_movable(curr->obj);
|
|
25
|
+
curr = curr->next;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
inline void stream_compact_segments(struct um_stream *stream) {
|
|
30
|
+
struct um_segment *curr = stream->head;
|
|
31
|
+
while (curr) {
|
|
32
|
+
// curr->obj = rb_gc_location(curr->obj);
|
|
33
|
+
curr = curr->next;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
6
37
|
static void Stream_mark(void *ptr) {
|
|
7
38
|
struct um_stream *stream = ptr;
|
|
8
|
-
rb_gc_mark_movable(stream->
|
|
39
|
+
rb_gc_mark_movable(stream->self);
|
|
40
|
+
rb_gc_mark_movable(stream->machine->self);
|
|
41
|
+
|
|
42
|
+
if (stream_has_target_obj_p(stream)) {
|
|
43
|
+
rb_gc_mark_movable(stream->target);
|
|
44
|
+
stream_mark_segments(stream);
|
|
45
|
+
}
|
|
9
46
|
}
|
|
10
47
|
|
|
11
48
|
static void Stream_compact(void *ptr) {
|
|
12
49
|
struct um_stream *stream = ptr;
|
|
13
|
-
stream->
|
|
50
|
+
stream->self = rb_gc_location(stream->self);
|
|
51
|
+
|
|
52
|
+
if (stream_has_target_obj_p(stream)) {
|
|
53
|
+
stream->target = rb_gc_location(stream->target);
|
|
54
|
+
stream_compact_segments(stream);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static void Stream_free(void *ptr) {
|
|
59
|
+
struct um_stream *stream = ptr;
|
|
60
|
+
stream_clear(stream);
|
|
14
61
|
}
|
|
15
62
|
|
|
16
63
|
static const rb_data_type_t Stream_type = {
|
|
17
64
|
.wrap_struct_name = "UringMachine::Stream",
|
|
18
65
|
.function = {
|
|
19
66
|
.dmark = Stream_mark,
|
|
20
|
-
.dfree =
|
|
67
|
+
.dfree = Stream_free,
|
|
21
68
|
.dsize = NULL,
|
|
22
69
|
.dcompact = Stream_compact
|
|
23
70
|
},
|
|
@@ -26,67 +73,74 @@ static const rb_data_type_t Stream_type = {
|
|
|
26
73
|
|
|
27
74
|
static VALUE Stream_allocate(VALUE klass) {
|
|
28
75
|
struct um_stream *stream;
|
|
29
|
-
|
|
76
|
+
VALUE self = TypedData_Make_Struct(klass, struct um_stream, &Stream_type, stream);
|
|
77
|
+
return self;
|
|
30
78
|
}
|
|
31
79
|
|
|
32
|
-
static inline struct um_stream *
|
|
80
|
+
static inline struct um_stream *um_get_stream(VALUE self) {
|
|
33
81
|
struct um_stream *stream;
|
|
34
82
|
TypedData_Get_Struct(self, struct um_stream, &Stream_type, stream);
|
|
35
83
|
return stream;
|
|
36
84
|
}
|
|
37
85
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
86
|
+
static inline void stream_setup(struct um_stream *stream, VALUE target, VALUE mode) {
|
|
87
|
+
stream->working_buffer = NULL;
|
|
88
|
+
if (mode == SYM_bp_read || mode == Qnil) {
|
|
89
|
+
stream->mode = STREAM_BP_READ;
|
|
90
|
+
stream->fd = NUM2INT(target);
|
|
91
|
+
}
|
|
92
|
+
else if (mode == SYM_bp_recv) {
|
|
93
|
+
stream->mode = STREAM_BP_RECV;
|
|
94
|
+
stream->fd = NUM2INT(target);
|
|
95
|
+
}
|
|
96
|
+
else if (mode == SYM_ssl) {
|
|
97
|
+
stream->mode = STREAM_SSL;
|
|
98
|
+
stream->target = target;
|
|
99
|
+
um_ssl_set_bio(stream->machine, target);
|
|
100
|
+
}
|
|
101
|
+
else
|
|
102
|
+
rb_raise(eUMError, "Invalid stream mode");
|
|
52
103
|
}
|
|
53
104
|
|
|
54
|
-
VALUE
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
105
|
+
VALUE Stream_initialize(int argc, VALUE *argv, VALUE self) {
|
|
106
|
+
VALUE machine;
|
|
107
|
+
VALUE target;
|
|
108
|
+
VALUE mode;
|
|
109
|
+
rb_scan_args(argc, argv, "21", &machine, &target, &mode);
|
|
58
110
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return ULONG2NUM(stream->fd);
|
|
62
|
-
}
|
|
111
|
+
struct um_stream *stream = um_get_stream(self);
|
|
112
|
+
memset(stream, 0, sizeof(struct um_stream));
|
|
63
113
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
114
|
+
RB_OBJ_WRITE(self, &stream->self, self);
|
|
115
|
+
stream->machine = um_get_machine(machine);
|
|
116
|
+
stream_setup(stream, target, mode);
|
|
67
117
|
|
|
68
|
-
return
|
|
118
|
+
return self;
|
|
69
119
|
}
|
|
70
120
|
|
|
71
|
-
VALUE
|
|
72
|
-
struct um_stream *stream =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return
|
|
121
|
+
VALUE Stream_mode(VALUE self) {
|
|
122
|
+
struct um_stream *stream = um_get_stream(self);
|
|
123
|
+
switch (stream->mode) {
|
|
124
|
+
case STREAM_BP_READ: return SYM_bp_read;
|
|
125
|
+
case STREAM_BP_RECV: return SYM_bp_recv;
|
|
126
|
+
case STREAM_SSL: return SYM_ssl;
|
|
127
|
+
default: return Qnil;
|
|
128
|
+
}
|
|
129
|
+
return Qnil;
|
|
76
130
|
}
|
|
77
131
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
132
|
+
VALUE Stream_get_line(VALUE self, VALUE limit) {
|
|
133
|
+
struct um_stream *stream = um_get_stream(self);
|
|
134
|
+
return stream_get_line(stream, Qnil, NUM2ULONG(limit));
|
|
135
|
+
}
|
|
82
136
|
|
|
83
|
-
|
|
137
|
+
VALUE Stream_get_string(VALUE self, VALUE len) {
|
|
138
|
+
struct um_stream *stream = um_get_stream(self);
|
|
139
|
+
return stream_get_string(stream, Qnil, NUM2LONG(len), 0, false);
|
|
84
140
|
}
|
|
85
141
|
|
|
86
142
|
VALUE Stream_resp_decode(VALUE self) {
|
|
87
|
-
struct um_stream *stream =
|
|
88
|
-
if (unlikely(stream->eof)) return Qnil;
|
|
89
|
-
|
|
143
|
+
struct um_stream *stream = um_get_stream(self);
|
|
90
144
|
VALUE out_buffer = rb_utf8_str_new_literal("");
|
|
91
145
|
VALUE obj = resp_decode(stream, out_buffer);
|
|
92
146
|
RB_GC_GUARD(out_buffer);
|
|
@@ -102,34 +156,36 @@ VALUE Stream_resp_encode(VALUE self, VALUE str, VALUE obj) {
|
|
|
102
156
|
return str;
|
|
103
157
|
}
|
|
104
158
|
|
|
105
|
-
VALUE
|
|
106
|
-
struct
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return str;
|
|
159
|
+
VALUE Stream_eof_p(VALUE self) {
|
|
160
|
+
struct um_stream *stream = um_get_stream(self);
|
|
161
|
+
return stream->eof ? Qtrue : Qfalse;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
VALUE Stream_clear(VALUE self) {
|
|
165
|
+
struct um_stream *stream = um_get_stream(self);
|
|
166
|
+
stream_clear(stream);
|
|
167
|
+
return self;
|
|
115
168
|
}
|
|
116
169
|
|
|
117
170
|
void Init_Stream(void) {
|
|
118
|
-
|
|
171
|
+
cStream = rb_define_class_under(cUM, "Stream", rb_cObject);
|
|
119
172
|
rb_define_alloc_func(cStream, Stream_allocate);
|
|
120
173
|
|
|
121
|
-
rb_define_method(cStream, "initialize", Stream_initialize,
|
|
122
|
-
rb_define_method(cStream, "
|
|
123
|
-
rb_define_method(cStream, "fd", Stream_fd, 0);
|
|
174
|
+
rb_define_method(cStream, "initialize", Stream_initialize, -1);
|
|
175
|
+
rb_define_method(cStream, "mode", Stream_mode, 0);
|
|
124
176
|
|
|
125
|
-
rb_define_method(cStream, "get_line", Stream_get_line,
|
|
126
|
-
rb_define_method(cStream, "get_string", Stream_get_string,
|
|
127
|
-
rb_define_method(cStream, "skip", Stream_skip, 1);
|
|
177
|
+
rb_define_method(cStream, "get_line", Stream_get_line, 1);
|
|
178
|
+
rb_define_method(cStream, "get_string", Stream_get_string, 1);
|
|
128
179
|
|
|
129
180
|
rb_define_method(cStream, "resp_decode", Stream_resp_decode, 0);
|
|
130
|
-
|
|
131
181
|
rb_define_singleton_method(cStream, "resp_encode", Stream_resp_encode, 2);
|
|
132
|
-
|
|
182
|
+
|
|
183
|
+
rb_define_method(cStream, "eof?", Stream_eof_p, 0);
|
|
184
|
+
rb_define_method(cStream, "clear", Stream_clear, 0);
|
|
133
185
|
|
|
134
186
|
eStreamRESPError = rb_define_class_under(cStream, "RESPError", rb_eStandardError);
|
|
187
|
+
|
|
188
|
+
SYM_bp_read = ID2SYM(rb_intern("bp_read"));
|
|
189
|
+
SYM_bp_recv = ID2SYM(rb_intern("bp_recv"));
|
|
190
|
+
SYM_ssl = ID2SYM(rb_intern("ssl"));
|
|
135
191
|
}
|
data/ext/um/um_utils.c
CHANGED
|
@@ -62,16 +62,16 @@ inline void * um_prepare_read_buffer(VALUE buffer, ssize_t len, ssize_t ofs) {
|
|
|
62
62
|
return RSTRING_PTR(buffer) + ofs;
|
|
63
63
|
}
|
|
64
64
|
else if (IO_BUFFER_P(buffer)) {
|
|
65
|
-
|
|
65
|
+
char *base;
|
|
66
66
|
size_t size;
|
|
67
|
-
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size); // writing *to* buffer
|
|
67
|
+
rb_io_buffer_get_bytes_for_writing(buffer, (void *)&base, &size); // writing *to* buffer
|
|
68
68
|
if (len == -1) len = size;
|
|
69
69
|
if (ofs < 0) ofs = size + ofs + 1;
|
|
70
70
|
size_t new_size = len + (size_t)ofs;
|
|
71
71
|
|
|
72
72
|
if (size < new_size) {
|
|
73
73
|
rb_io_buffer_resize(buffer, new_size);
|
|
74
|
-
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
74
|
+
rb_io_buffer_get_bytes_for_writing(buffer, (void *)&base, &size);
|
|
75
75
|
}
|
|
76
76
|
return base + ofs;
|
|
77
77
|
}
|
|
@@ -148,7 +148,7 @@ int um_setup_buffer_ring(struct um *machine, unsigned size, unsigned count) {
|
|
|
148
148
|
um_raise_internal_error("Failed to allocate buffers");
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
char *ptr = desc->buf_base;
|
|
152
152
|
for (unsigned i = 0; i < desc->buf_count; i++) {
|
|
153
153
|
io_uring_buf_ring_add(desc->br, ptr, desc->buf_size, i, desc->buf_mask, i);
|
|
154
154
|
ptr += desc->buf_size;
|
|
@@ -164,7 +164,7 @@ inline VALUE um_get_string_from_buffer_ring(struct um *machine, int bgid, __s32
|
|
|
164
164
|
|
|
165
165
|
unsigned buf_idx = flags >> IORING_CQE_BUFFER_SHIFT;
|
|
166
166
|
struct buf_ring_descriptor *desc = machine->buffer_rings + bgid;
|
|
167
|
-
char *src = desc->buf_base + desc->buf_size * buf_idx;
|
|
167
|
+
char *src = (char *)desc->buf_base + desc->buf_size * buf_idx;
|
|
168
168
|
// TODO: add support for UTF8
|
|
169
169
|
// buf = rd->utf8_encoding ? rb_utf8_str_new(src, cqe->res) : rb_str_new(src, cqe->res);
|
|
170
170
|
VALUE buf = rb_str_new(src, result);
|
|
@@ -224,7 +224,7 @@ inline struct iovec *um_alloc_iovecs_for_writing(int argc, VALUE *argv, size_t *
|
|
|
224
224
|
inline void um_advance_iovecs_for_writing(struct iovec **ptr, int *len, size_t adv) {
|
|
225
225
|
while (adv) {
|
|
226
226
|
if (adv < (*ptr)->iov_len) {
|
|
227
|
-
(*ptr)->iov_base
|
|
227
|
+
(*ptr)->iov_base = (char *)(*ptr)->iov_base + adv;
|
|
228
228
|
(*ptr)->iov_len -= adv;
|
|
229
229
|
return;
|
|
230
230
|
}
|
data/grant-2025/tasks.md
CHANGED
|
@@ -24,15 +24,19 @@
|
|
|
24
24
|
|
|
25
25
|
https://www.man7.org/linux/man-pages/man7/inotify.7.html
|
|
26
26
|
|
|
27
|
-
- [
|
|
27
|
+
- [v] Better buffer management
|
|
28
28
|
- [v] Add `UM#sendv` method (see below)
|
|
29
29
|
- [v] Benchmark `#sendv` vs `#send_bundle` (in concurrent situation)
|
|
30
30
|
- [v] Support for `IO::Buffer`?
|
|
31
31
|
- [ ] Benchmark `#read_each` vs `#read` (in concurrent situation)
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
35
|
-
- [
|
|
32
|
+
- [v] Implement automatic buffer pool:
|
|
33
|
+
- [v] Automatic buffer allocation,registration and management.
|
|
34
|
+
- [v] Support for partial buffer consumption.
|
|
35
|
+
- [v] Data processing through a rewritten stream implementation.
|
|
36
|
+
|
|
37
|
+
- [ ] More benchmarks
|
|
38
|
+
- Different reading methods in concurrent setting
|
|
39
|
+
- SSL vs SSL-custom-BIO vs SSL-stream
|
|
36
40
|
|
|
37
41
|
- [v] Sidecar mode
|
|
38
42
|
- [v] Convert `UM#initialize` to take kwargs
|
|
@@ -147,9 +151,10 @@
|
|
|
147
151
|
- [ ] docs (similar to papercraft docs)
|
|
148
152
|
|
|
149
153
|
- [ ] Uma - web server
|
|
150
|
-
- [
|
|
154
|
+
- [v] Rack 3.0-compatible
|
|
151
155
|
see: https://github.com/socketry/protocol-rack
|
|
152
156
|
- [ ] Rails integration (Railtie)
|
|
153
157
|
see: https://github.com/socketry/falcon
|
|
158
|
+
- [ ] Hanami?
|
|
154
159
|
- [ ] Benchmarks
|
|
155
|
-
- [ ] Add to the TechEmpower benchmarks
|
|
160
|
+
- [ ] Add to the TechEmpower benchmarks?
|
|
@@ -77,9 +77,11 @@ class UringMachine
|
|
|
77
77
|
# concurrent applications in Ruby, in tight integration with the standard Ruby
|
|
78
78
|
# I/O and locking APIs.
|
|
79
79
|
class FiberScheduler
|
|
80
|
+
BLOCKING_OP_SUPPORT = ENV['BLOCKING_OP_SUPPORT']
|
|
81
|
+
|
|
80
82
|
|
|
81
83
|
# The blocking operation thread pool is shared by all fiber schedulers.
|
|
82
|
-
DEFAULT_THREAD_POOL = BlockingOperationThreadPool.new
|
|
84
|
+
DEFAULT_THREAD_POOL = BLOCKING_OP_SUPPORT && BlockingOperationThreadPool.new
|
|
83
85
|
|
|
84
86
|
# UringMachine instance associated with scheduler.
|
|
85
87
|
attr_reader :machine
|
|
@@ -114,6 +116,7 @@ class UringMachine
|
|
|
114
116
|
# @param block [Proc] fiber block
|
|
115
117
|
# @return [Fiber]
|
|
116
118
|
def fiber(&block)
|
|
119
|
+
# p fiber: [block]
|
|
117
120
|
fiber = Fiber.new(blocking: false) { @machine.run(fiber, &block) }
|
|
118
121
|
|
|
119
122
|
@fiber_map[fiber] = true
|
|
@@ -141,16 +144,21 @@ class UringMachine
|
|
|
141
144
|
@fiber_map = ObjectSpace::WeakMap.new
|
|
142
145
|
end
|
|
143
146
|
|
|
144
|
-
@machine.
|
|
147
|
+
@machine.await(fibers)
|
|
145
148
|
end
|
|
146
149
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
@
|
|
150
|
+
if BLOCKING_OP_SUPPORT
|
|
151
|
+
|
|
152
|
+
# Runs the given operation in a separate thread, so as not to block other
|
|
153
|
+
# fibers.
|
|
154
|
+
#
|
|
155
|
+
# @param op [callable] blocking operation
|
|
156
|
+
# @return [void]
|
|
157
|
+
def blocking_operation_wait(op)
|
|
158
|
+
# p blocking_operation_wait: [op]
|
|
159
|
+
@thread_pool.process(@machine, op)
|
|
160
|
+
end
|
|
161
|
+
|
|
154
162
|
end
|
|
155
163
|
|
|
156
164
|
# Blocks the current fiber by yielding to the machine. This hook is called
|
|
@@ -160,6 +168,7 @@ class UringMachine
|
|
|
160
168
|
# @param timeout [Number, nil] optional timeout
|
|
161
169
|
# @return [bool] was the operation successful
|
|
162
170
|
def block(blocker, timeout = nil)
|
|
171
|
+
# p block: [blocker, timeout]
|
|
163
172
|
if timeout
|
|
164
173
|
@machine.timeout(timeout, Timeout::Error) { @machine.yield }
|
|
165
174
|
else
|
|
@@ -178,6 +187,7 @@ class UringMachine
|
|
|
178
187
|
# @param fiber [Fiber] fiber to resume
|
|
179
188
|
# @return [void]
|
|
180
189
|
def unblock(blocker, fiber)
|
|
190
|
+
# p unblock: [blocker, fiber]
|
|
181
191
|
@machine.schedule(fiber, nil)
|
|
182
192
|
@machine.wakeup if Thread.current != @thread
|
|
183
193
|
end
|
|
@@ -187,11 +197,13 @@ class UringMachine
|
|
|
187
197
|
# @param duration [Number, nil] sleep duration
|
|
188
198
|
# @return [void]
|
|
189
199
|
def kernel_sleep(duration = nil)
|
|
200
|
+
# p kernel_sleep: duration
|
|
190
201
|
duration ? @machine.sleep(duration) : @machine.yield
|
|
191
202
|
end
|
|
192
203
|
|
|
193
204
|
# Yields to the next runnable fiber.
|
|
194
205
|
def yield
|
|
206
|
+
# p yield: []
|
|
195
207
|
@machine.snooze
|
|
196
208
|
end
|
|
197
209
|
|
|
@@ -202,6 +214,7 @@ class UringMachine
|
|
|
202
214
|
# @param timeout [Number, nil] optional timeout
|
|
203
215
|
# @return [void]
|
|
204
216
|
def io_wait(io, events, timeout = nil)
|
|
217
|
+
# p io_wait: [io, events, timeout]
|
|
205
218
|
timeout ||= io.timeout
|
|
206
219
|
if timeout
|
|
207
220
|
@machine.timeout(timeout, Timeout::Error) {
|
|
@@ -219,6 +232,7 @@ class UringMachine
|
|
|
219
232
|
# @param eios [Array<IO>] exceptable IOs
|
|
220
233
|
# @param timeout [Number, nil] optional timeout
|
|
221
234
|
def io_select(rios, wios, eios, timeout = nil)
|
|
235
|
+
# p io_select: [rios, wios, eios, timeout]
|
|
222
236
|
map_r = map_fds(rios)
|
|
223
237
|
map_w = map_fds(wios)
|
|
224
238
|
map_e = map_fds(eios)
|
|
@@ -243,6 +257,7 @@ class UringMachine
|
|
|
243
257
|
# @param offset [Integer] buffer offset
|
|
244
258
|
# @return [Integer] bytes read
|
|
245
259
|
def io_read(io, buffer, length, offset)
|
|
260
|
+
# p io_read: [io, buffer, length, offset]
|
|
246
261
|
length = buffer.size if length == 0
|
|
247
262
|
|
|
248
263
|
if (timeout = io.timeout)
|
|
@@ -269,6 +284,7 @@ class UringMachine
|
|
|
269
284
|
# @param offset [Integer] buffer offset
|
|
270
285
|
# @return [Integer] bytes read
|
|
271
286
|
def io_pread(io, buffer, from, length, offset)
|
|
287
|
+
# p io_pread: [io, buffer, from, length, offset]
|
|
272
288
|
length = buffer.size if length == 0
|
|
273
289
|
|
|
274
290
|
if (timeout = io.timeout)
|
|
@@ -294,6 +310,7 @@ class UringMachine
|
|
|
294
310
|
# @param offset [Integer] write offset
|
|
295
311
|
# @return [Integer] bytes written
|
|
296
312
|
def io_write(io, buffer, length, offset)
|
|
313
|
+
# p io_write: [io, buffer, length, offset]
|
|
297
314
|
length = buffer.size if length == 0
|
|
298
315
|
buffer = buffer.slice(offset) if offset > 0
|
|
299
316
|
|
|
@@ -321,6 +338,7 @@ class UringMachine
|
|
|
321
338
|
# @param offset [Integer] buffer offset
|
|
322
339
|
# @return [Integer] bytes written
|
|
323
340
|
def io_pwrite(io, buffer, from, length, offset)
|
|
341
|
+
# p io_pwrite: [io, buffer, from, length, offset]
|
|
324
342
|
length = buffer.size if length == 0
|
|
325
343
|
buffer = buffer.slice(offset) if offset > 0
|
|
326
344
|
|
|
@@ -344,6 +362,7 @@ class UringMachine
|
|
|
344
362
|
# @param fd [Integer] file descriptor
|
|
345
363
|
# @return [Integer] file descriptor
|
|
346
364
|
def io_close(fd)
|
|
365
|
+
# p io_close: [fd]
|
|
347
366
|
@machine.close_async(fd)
|
|
348
367
|
rescue Errno => e
|
|
349
368
|
-e.errno
|
|
@@ -368,6 +387,7 @@ class UringMachine
|
|
|
368
387
|
# @param exception [Exception] Exception
|
|
369
388
|
# @return [void]
|
|
370
389
|
def fiber_interrupt(fiber, exception)
|
|
390
|
+
# p fiber_interrupt: [fiber, exception]
|
|
371
391
|
@machine.schedule(fiber, exception)
|
|
372
392
|
@machine.wakeup
|
|
373
393
|
end
|
|
@@ -377,6 +397,7 @@ class UringMachine
|
|
|
377
397
|
# @param hostname [String] hostname to resolve
|
|
378
398
|
# @return [Array<Addrinfo>] array of resolved addresses
|
|
379
399
|
def address_resolve(hostname)
|
|
400
|
+
# p address_resolve: [hostname]
|
|
380
401
|
Resolv.getaddresses(hostname)
|
|
381
402
|
end
|
|
382
403
|
|
|
@@ -388,6 +409,7 @@ class UringMachine
|
|
|
388
409
|
# @param block [Proc] block to run
|
|
389
410
|
# @return [any] block return value
|
|
390
411
|
def timeout_after(duration, exception, message, &block)
|
|
412
|
+
# p timeout_after: [duration, exception, message, block]
|
|
391
413
|
@machine.timeout(duration, exception, &block)
|
|
392
414
|
end
|
|
393
415
|
|
|
@@ -397,7 +419,11 @@ class UringMachine
|
|
|
397
419
|
#
|
|
398
420
|
# @param o [any]
|
|
399
421
|
# @return [void]
|
|
400
|
-
def p(o)
|
|
422
|
+
def p(o)
|
|
423
|
+
UM.debug(o.inspect)
|
|
424
|
+
UM.debug(caller[2..12].inspect)
|
|
425
|
+
UM.debug("")
|
|
426
|
+
end
|
|
401
427
|
|
|
402
428
|
# Maps the given ios to fds.
|
|
403
429
|
#
|
data/lib/uringmachine/version.rb
CHANGED
data/lib/uringmachine.rb
CHANGED
|
@@ -46,7 +46,7 @@ class UringMachine
|
|
|
46
46
|
def terminate(*fibers)
|
|
47
47
|
fibers = fibers.first if fibers.size == 1 && fibers.first.is_a?(Enumerable)
|
|
48
48
|
|
|
49
|
-
fibers.each { schedule(it, TERMINATE_EXCEPTION) }
|
|
49
|
+
fibers.each { schedule(it, TERMINATE_EXCEPTION) unless it.done? }
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Runs the given block in the given fiber. This method is used to run fibers
|
|
@@ -62,24 +62,49 @@ class UringMachine
|
|
|
62
62
|
fiber
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
# Waits for the given fibers to terminate
|
|
65
|
+
# Waits for the given fibers to terminate, returning the return value for each
|
|
66
|
+
# given fiber. This method also accepts procs instead of fibers. When a proc
|
|
67
|
+
# is given, it is ran in a separate fiber which will be joined.
|
|
66
68
|
#
|
|
67
|
-
#
|
|
69
|
+
# machine.join(
|
|
70
|
+
# -> { machine.sleep(0.01); :f1 },
|
|
71
|
+
# -> { machine.sleep(0.02); :f2 },
|
|
72
|
+
# -> { machine.sleep(0.03); :f3 }
|
|
73
|
+
# ) #=> [:f1, :f2, :f3]
|
|
74
|
+
#
|
|
75
|
+
# @param *fibers [Array<Fiber, Proc>] fibers @return [Array<any>] return
|
|
76
|
+
# values of the given fibers
|
|
68
77
|
def join(*fibers)
|
|
69
|
-
|
|
78
|
+
queue = Fiber.current.mailbox
|
|
79
|
+
|
|
80
|
+
if fibers.size == 1
|
|
81
|
+
first = fibers.first
|
|
82
|
+
case first
|
|
83
|
+
when Enumerable
|
|
84
|
+
fibers = first
|
|
85
|
+
when Fiber
|
|
86
|
+
first = proc_spin(first) if first.is_a?(Proc)
|
|
87
|
+
if !first.done?
|
|
88
|
+
first.add_done_listener(queue)
|
|
89
|
+
self.shift(queue)
|
|
90
|
+
end
|
|
91
|
+
return first.result
|
|
92
|
+
end
|
|
93
|
+
end
|
|
70
94
|
|
|
71
|
-
results =
|
|
72
|
-
queue = nil
|
|
95
|
+
results = {}
|
|
73
96
|
pending = nil
|
|
74
97
|
fibers.each do |f|
|
|
98
|
+
f = proc_spin(f) if f.is_a?(Proc)
|
|
75
99
|
if f.done?
|
|
76
100
|
results[f] = f.result
|
|
77
101
|
else
|
|
102
|
+
results[f] = nil
|
|
78
103
|
(pending ||= []) << f
|
|
79
|
-
queue ||= Fiber.current.mailbox
|
|
80
104
|
f.add_done_listener(queue)
|
|
81
105
|
end
|
|
82
106
|
end
|
|
107
|
+
|
|
83
108
|
if pending
|
|
84
109
|
while !pending.empty?
|
|
85
110
|
f = self.shift(queue)
|
|
@@ -96,23 +121,29 @@ class UringMachine
|
|
|
96
121
|
#
|
|
97
122
|
# @param fibers [Fiber, Array<Fiber>] fibers to wait for
|
|
98
123
|
# @return [Integer] number of fibers
|
|
99
|
-
def
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
124
|
+
def await(*fibers)
|
|
125
|
+
queue = Fiber.current.mailbox
|
|
126
|
+
|
|
127
|
+
if fibers.size == 1
|
|
128
|
+
first = fibers.first
|
|
129
|
+
case first
|
|
130
|
+
when Enumerable
|
|
131
|
+
fibers = first
|
|
132
|
+
when Fiber
|
|
133
|
+
first = proc_spin(first) if first.is_a?(Proc)
|
|
134
|
+
if !first.done?
|
|
135
|
+
first.add_done_listener(queue)
|
|
136
|
+
self.shift(queue)
|
|
137
|
+
end
|
|
138
|
+
return 1
|
|
106
139
|
end
|
|
107
|
-
return 1
|
|
108
140
|
end
|
|
109
141
|
|
|
110
|
-
queue = nil
|
|
111
142
|
pending = nil
|
|
112
143
|
fibers.each do |f|
|
|
144
|
+
f = proc_spin(f) if f.is_a?(Proc)
|
|
113
145
|
if !f.done?
|
|
114
146
|
(pending ||= []) << f
|
|
115
|
-
queue ||= Fiber.current.mailbox
|
|
116
147
|
f.add_done_listener(queue)
|
|
117
148
|
end
|
|
118
149
|
end
|
|
@@ -176,11 +207,11 @@ class UringMachine
|
|
|
176
207
|
fiber.set_result(e)
|
|
177
208
|
ensure
|
|
178
209
|
fiber.mark_as_done
|
|
179
|
-
# cleanup
|
|
180
210
|
fiber_set.delete(fiber)
|
|
181
211
|
self.notify_done_listeners(fiber)
|
|
182
212
|
|
|
183
|
-
# switch away to a different fiber
|
|
213
|
+
# finally switch away to a different fiber, the current fiber should not be
|
|
214
|
+
# resumed after this.
|
|
184
215
|
self.switch
|
|
185
216
|
end
|
|
186
217
|
|
|
@@ -215,6 +246,16 @@ class UringMachine
|
|
|
215
246
|
}
|
|
216
247
|
end
|
|
217
248
|
|
|
249
|
+
# @param proc [Proc]
|
|
250
|
+
# @return [Fiber]
|
|
251
|
+
def proc_spin(proc)
|
|
252
|
+
if proc.arity == 0
|
|
253
|
+
spin { proc.call }
|
|
254
|
+
else
|
|
255
|
+
spin(&proc)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
218
259
|
# Fiber extensions
|
|
219
260
|
module FiberExtensions
|
|
220
261
|
attr_reader :result, :done, :done_listeners
|