uringmachine 0.1 → 0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2ac86e83825689e22080a04bf37ebfd553134d3209920d16f1ed413418847aa
4
- data.tar.gz: a599878fb9a7745069b39efcb60c3610eea525693adadc2236d4ad06cbc9ad83
3
+ metadata.gz: 7129b3c8605d5734f7152bddb4fa1b6f034fd1de26262eabfbfc2190846967bd
4
+ data.tar.gz: 902e6663bac65d56e45d67383e2cd3eb0601534ed2b451f4f76b2812ce6125b1
5
5
  SHA512:
6
- metadata.gz: 21f2236193e8df1799718ec96ffef7f72132fa57b3e61371544d35f1748459f1e2b9bb10a577ca3e9cee8e6666f2675aca4a9bd9f21c9f4923f8be633859cbe3
7
- data.tar.gz: 2bfa3c754a5911edb2b9f2437c265fe978db9b39fcc05bce8c3047e81ce051e26a934014313edeb428ee6b40997ce490c50f9459a0e414b9aad2dc75530a31cf
6
+ metadata.gz: 683db63642ddb5d98c9eb137f8121a8cb8cb0fe44456928fca78cf8e322cebc01078efa71379abcceae1a1889140bf5b39f6ac16348db0b77d9c8a5bb9cf5cd0
7
+ data.tar.gz: 850dce780030b6102371861805f831299d3ea113f8c02141515afe167b0e49f43a51812a6159a91b6335085b41fcb226713eeb963ae69d208818cb1f3cb303b3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # 2024-10-04 Version 0.3
2
+
3
+ - Fix race condition affecting `#timeout` and `#sleep`.
4
+ - Add `#accept_each`
5
+ - Add `#accept`
6
+
7
+ # 2024-10-03 Version 0.2
8
+
9
+ - Remove old IOU code.
10
+ - Add `#read_each`
11
+ - Add `#read`
12
+
1
13
  # 2024-10-03 Version 0.1
2
14
 
3
15
  The basic fiber scheduling stuff
data/Rakefile CHANGED
@@ -26,7 +26,7 @@ CLEAN.include "**/*.o", "**/*.so", "**/*.so.*", "**/*.a", "**/*.bundle", "**/*.j
26
26
 
27
27
  task :release do
28
28
  require_relative './lib/uringmachine/version'
29
- version = UM::VERSION
29
+ version = UringMachine::VERSION
30
30
 
31
31
  puts 'Building uringmachine...'
32
32
  `gem build uringmachine.gemspec`
data/ext/um/um.c CHANGED
@@ -2,6 +2,54 @@
2
2
  #include "ruby/thread.h"
3
3
  #include <sys/mman.h>
4
4
 
5
+ void um_setup(struct um *machine) {
6
+ machine->ring_initialized = 0;
7
+ machine->unsubmitted_count = 0;
8
+ machine->buffer_ring_count = 0;
9
+ machine->pending_count = 0;
10
+ machine->runqueue_head = NULL;
11
+ machine->runqueue_tail = NULL;
12
+ machine->op_freelist = NULL;
13
+ machine->result_freelist = NULL;
14
+
15
+ unsigned prepared_limit = 4096;
16
+ int flags = 0;
17
+ #ifdef HAVE_IORING_SETUP_SUBMIT_ALL
18
+ flags |= IORING_SETUP_SUBMIT_ALL;
19
+ #endif
20
+ #ifdef HAVE_IORING_SETUP_COOP_TASKRUN
21
+ flags |= IORING_SETUP_COOP_TASKRUN;
22
+ #endif
23
+
24
+ while (1) {
25
+ int ret = io_uring_queue_init(prepared_limit, &machine->ring, flags);
26
+ if (likely(!ret)) break;
27
+
28
+ // if ENOMEM is returned, try with half as much entries
29
+ if (unlikely(ret == -ENOMEM && prepared_limit > 64))
30
+ prepared_limit = prepared_limit / 2;
31
+ else
32
+ rb_syserr_fail(-ret, strerror(-ret));
33
+ }
34
+ machine->ring_initialized = 1;
35
+ }
36
+
37
+ inline void um_teardown(struct um *machine) {
38
+ if (!machine->ring_initialized) return;
39
+
40
+ for (unsigned i = 0; i < machine->buffer_ring_count; i++) {
41
+ struct buf_ring_descriptor *desc = machine->buffer_rings + i;
42
+ io_uring_free_buf_ring(&machine->ring, desc->br, desc->buf_count, i);
43
+ free(desc->buf_base);
44
+ }
45
+ machine->buffer_ring_count = 0;
46
+ io_uring_queue_exit(&machine->ring);
47
+ machine->ring_initialized = 0;
48
+
49
+ um_free_op_linked_list(machine, machine->op_freelist);
50
+ um_free_op_linked_list(machine, machine->runqueue_head);
51
+ }
52
+
5
53
  static inline struct io_uring_sqe *um_get_sqe(struct um *machine, struct um_op *op) {
6
54
  struct io_uring_sqe *sqe;
7
55
  sqe = io_uring_get_sqe(&machine->ring);
@@ -24,22 +72,6 @@ done:
24
72
  return sqe;
25
73
  }
26
74
 
27
- inline void um_cleanup(struct um *machine) {
28
- if (!machine->ring_initialized) return;
29
-
30
- for (unsigned i = 0; i < machine->buffer_ring_count; i++) {
31
- struct buf_ring_descriptor *desc = machine->buffer_rings + i;
32
- io_uring_free_buf_ring(&machine->ring, desc->br, desc->buf_count, i);
33
- free(desc->buf_base);
34
- }
35
- machine->buffer_ring_count = 0;
36
- io_uring_queue_exit(&machine->ring);
37
- machine->ring_initialized = 0;
38
-
39
- um_free_linked_list(machine->runqueue_head);
40
- um_free_linked_list(machine->freelist_head);
41
- }
42
-
43
75
  struct wait_for_cqe_ctx {
44
76
  struct um *machine;
45
77
  struct io_uring_cqe *cqe;
@@ -57,26 +89,47 @@ void *um_wait_for_cqe_without_gvl(void *ptr) {
57
89
  return NULL;
58
90
  }
59
91
 
92
+ inline void um_handle_submitted_op_cqe_single(struct um *machine, struct um_op *op, struct io_uring_cqe *cqe) {
93
+ op->cqe_result = cqe->res;
94
+ op->cqe_flags = cqe->flags;
95
+ op->state = OP_completed;
96
+ um_runqueue_push(machine, op);
97
+ }
98
+
99
+ inline void um_handle_submitted_op_cqe_multi(struct um *machine, struct um_op *op, struct io_uring_cqe *cqe) {
100
+ if (!op->results_head) {
101
+ struct um_op *op2 = um_op_checkout(machine);
102
+ op2->state = OP_schedule;
103
+ op2->fiber = op->fiber;
104
+ op2->resume_value = Qnil;
105
+ um_runqueue_push(machine, op2);
106
+ }
107
+ um_op_result_push(machine, op, cqe->res, cqe->flags);
108
+
109
+ if (!(cqe->flags & IORING_CQE_F_MORE))
110
+ op->state = OP_completed;
111
+ }
112
+
60
113
  inline void um_process_cqe(struct um *machine, struct io_uring_cqe *cqe) {
61
114
  struct um_op *op = (struct um_op *)cqe->user_data;
62
- if (!op) return;
115
+ if (unlikely(!op)) return;
63
116
 
64
117
  switch (op->state) {
65
118
  case OP_submitted:
66
- if (cqe->res == -ECANCELED) {
119
+ if (unlikely(cqe->res == -ECANCELED)) {
67
120
  um_op_checkin(machine, op);
121
+ break;
68
122
  }
69
- else {
70
- op->state = OP_completed;
71
- op->cqe_result = cqe->res;
72
- op->cqe_flags = cqe->flags;
73
- um_runqueue_push(machine, op);
74
- }
123
+ if (!op->is_multishot)
124
+ um_handle_submitted_op_cqe_single(machine, op, cqe);
125
+ else
126
+ um_handle_submitted_op_cqe_multi(machine, op, cqe);
75
127
  break;
76
128
  case OP_abandonned:
77
129
  // op has been abandonned by the I/O method, so we need to cleanup (check
78
130
  // the op in to the free list).
79
131
  um_op_checkin(machine, op);
132
+ break;
80
133
  default:
81
134
  // TODO: invalid state, should raise!
82
135
  }
@@ -173,19 +226,26 @@ static inline void um_cancel_op(struct um *machine, struct um_op *op) {
173
226
  io_uring_prep_cancel64(sqe, (long long)op, 0);
174
227
  }
175
228
 
176
- static inline VALUE um_await_op(struct um *machine, struct um_op *op) {
229
+ static inline VALUE um_await_op(struct um *machine, struct um_op *op, int *result, int *flags) {
177
230
  op->fiber = rb_fiber_current();
178
231
  VALUE v = um_fiber_switch(machine);
179
-
180
232
  int is_exception = um_value_is_exception_p(v);
181
233
 
182
- if (is_exception && op->state == OP_submitted) {
234
+ if (unlikely(is_exception && op->state == OP_submitted)) {
183
235
  um_cancel_op(machine, op);
184
236
  op->state = OP_abandonned;
185
237
  }
186
- else
187
- um_op_checkin(machine, op);
188
- return is_exception ? um_raise_exception(v) : v;
238
+ else {
239
+ // We copy over the CQE result and flags, since the op is immediately
240
+ // checked in.
241
+ if (result) *result = op->cqe_result;
242
+ if (flags) *flags = op->cqe_flags;
243
+ if (!op->is_multishot)
244
+ um_op_checkin(machine, op);
245
+ }
246
+
247
+ if (unlikely(is_exception)) um_raise_exception(v);
248
+ return v;
189
249
  }
190
250
 
191
251
  inline VALUE um_await(struct um *machine) {
@@ -216,13 +276,14 @@ inline void um_interrupt(struct um *machine, VALUE fiber, VALUE value) {
216
276
  }
217
277
  }
218
278
 
219
- struct timeout_ctx {
279
+ struct op_ensure_ctx {
220
280
  struct um *machine;
221
281
  struct um_op *op;
282
+ int bgid;
222
283
  };
223
284
 
224
285
  VALUE um_timeout_ensure(VALUE arg) {
225
- struct timeout_ctx *ctx = (struct timeout_ctx *)arg;
286
+ struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
226
287
 
227
288
  if (ctx->op->state == OP_submitted) {
228
289
  // A CQE has not yet been received, we cancel the timeout and abandon the op
@@ -242,26 +303,155 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class) {
242
303
  if (!ID_new) ID_new = rb_intern("new");
243
304
 
244
305
  struct um_op *op = um_op_checkout(machine);
245
- struct __kernel_timespec ts = um_double_to_timespec(NUM2DBL(interval));
306
+ op->ts = um_double_to_timespec(NUM2DBL(interval));
246
307
 
247
308
  struct io_uring_sqe *sqe = um_get_sqe(machine, op);
248
- io_uring_prep_timeout(sqe, &ts, 0, 0);
309
+ io_uring_prep_timeout(sqe, &op->ts, 0, 0);
249
310
  op->state = OP_submitted;
250
311
  op->fiber = rb_fiber_current();
251
312
  op->resume_value = rb_funcall(class, ID_new, 0);
252
313
 
253
- struct timeout_ctx ctx = { .machine = machine, .op = op };
314
+ struct op_ensure_ctx ctx = { .machine = machine, .op = op };
254
315
  return rb_ensure(rb_yield, Qnil, um_timeout_ensure, (VALUE)&ctx);
255
316
  }
256
317
 
257
318
  inline VALUE um_sleep(struct um *machine, double duration) {
258
319
  struct um_op *op = um_op_checkout(machine);
259
- struct __kernel_timespec ts = um_double_to_timespec(duration);
320
+ op->ts = um_double_to_timespec(duration);
321
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
322
+ int result = 0;
323
+
324
+ io_uring_prep_timeout(sqe, &op->ts, 0, 0);
325
+ op->state = OP_submitted;
326
+
327
+ return um_await_op(machine, op, &result, NULL);
328
+ }
329
+
330
+ inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset) {
331
+ struct um_op *op = um_op_checkout(machine);
332
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
333
+ int result = 0;
334
+ int flags = 0;
335
+
336
+ void *ptr = um_prepare_read_buffer(buffer, maxlen, buffer_offset);
337
+ io_uring_prep_read(sqe, fd, ptr, maxlen, -1);
338
+ op->state = OP_submitted;
260
339
 
340
+ um_await_op(machine, op, &result, &flags);
341
+
342
+ um_raise_on_system_error(result);
343
+ um_update_read_buffer(machine, buffer, buffer_offset, result, flags);
344
+ return INT2FIX(result);
345
+ }
346
+
347
+ VALUE um_multishot_ensure(VALUE arg) {
348
+ struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
349
+ switch (ctx->op->state) {
350
+ case OP_submitted:
351
+ um_cancel_op(ctx->machine, ctx->op);
352
+ break;
353
+ case OP_completed:
354
+ um_op_checkin(ctx->machine, ctx->op);
355
+ break;
356
+ default:
357
+ }
358
+ return Qnil;
359
+ }
360
+
361
+ VALUE um_read_each_safe_loop(VALUE arg) {
362
+ struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
363
+ int result = 0;
364
+ int flags = 0;
365
+ int total = 0;
366
+
367
+ while (1) {
368
+ um_await_op(ctx->machine, ctx->op, NULL, NULL);
369
+ if (!ctx->op->results_head) {
370
+ // TODO: raise, this shouldn't happen
371
+ printf("no result found!\n");
372
+ }
373
+ while (um_op_result_shift(ctx->machine, ctx->op, &result, &flags)) {
374
+ if (likely(result > 0)) {
375
+ total += result;
376
+ VALUE buf = get_string_from_buffer_ring(ctx->machine, ctx->bgid, result, flags);
377
+ rb_yield(buf);
378
+ }
379
+ else
380
+ return INT2FIX(total);
381
+ }
382
+ }
383
+ }
384
+
385
+ VALUE um_read_each(struct um *machine, int fd, int bgid) {
386
+ struct um_op *op = um_op_checkout(machine);
261
387
  struct io_uring_sqe *sqe = um_get_sqe(machine, op);
262
- io_uring_prep_timeout(sqe, &ts, 0, 0);
388
+
389
+ op->is_multishot = 1;
390
+ io_uring_prep_read_multishot(sqe, fd, 0, -1, bgid);
263
391
  op->state = OP_submitted;
264
392
 
265
- return um_await_op(machine, op);
393
+ struct op_ensure_ctx ctx = { .machine = machine, .op = op, .bgid = bgid };
394
+ return rb_ensure(um_read_each_safe_loop, (VALUE)&ctx, um_multishot_ensure, (VALUE)&ctx);
266
395
  }
267
396
 
397
+ VALUE um_write(struct um *machine, int fd, VALUE buffer, int len) {
398
+ struct um_op *op = um_op_checkout(machine);
399
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
400
+ int result = 0;
401
+ int flags = 0;
402
+
403
+ io_uring_prep_write(sqe, fd, RSTRING_PTR(buffer), len, -1);
404
+ op->state = OP_submitted;
405
+
406
+ um_await_op(machine, op, &result, &flags);
407
+ um_raise_on_system_error(result);
408
+ return INT2FIX(result);
409
+ }
410
+
411
+ VALUE um_accept(struct um *machine, int fd) {
412
+ struct um_op *op = um_op_checkout(machine);
413
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
414
+ struct sockaddr addr;
415
+ socklen_t len;
416
+ int result = 0;
417
+ int flags = 0;
418
+ io_uring_prep_accept(sqe, fd, &addr, &len, 0);
419
+ op->state = OP_submitted;
420
+
421
+ um_await_op(machine, op, &result, &flags);
422
+ um_raise_on_system_error(result);
423
+ return INT2FIX(result);
424
+ }
425
+
426
+ VALUE um_accept_each_safe_loop(VALUE arg) {
427
+ struct op_ensure_ctx *ctx = (struct op_ensure_ctx *)arg;
428
+ int result = 0;
429
+ int flags = 0;
430
+
431
+ while (1) {
432
+ um_await_op(ctx->machine, ctx->op, NULL, NULL);
433
+ if (!ctx->op->results_head) {
434
+ // TODO: raise, this shouldn't happen
435
+ printf("no result found!\n");
436
+ }
437
+ while (um_op_result_shift(ctx->machine, ctx->op, &result, &flags)) {
438
+ if (likely(result > 0))
439
+ rb_yield(INT2FIX(result));
440
+ else
441
+ return Qnil;
442
+ }
443
+ }
444
+ }
445
+
446
+ VALUE um_accept_each(struct um *machine, int fd) {
447
+ struct um_op *op = um_op_checkout(machine);
448
+ struct io_uring_sqe *sqe = um_get_sqe(machine, op);
449
+ struct sockaddr addr;
450
+ socklen_t len;
451
+ io_uring_prep_multishot_accept(sqe, fd, &addr, &len, 0);
452
+ op->state = OP_submitted;
453
+ op->is_multishot = 1;
454
+
455
+ struct op_ensure_ctx ctx = { .machine = machine, .op = op };
456
+ return rb_ensure(um_accept_each_safe_loop, (VALUE)&ctx, um_multishot_ensure, (VALUE)&ctx);
457
+ }
data/ext/um/um.h CHANGED
@@ -29,13 +29,27 @@ enum op_state {
29
29
  OP_schedule, // the corresponding fiber is scheduled
30
30
  };
31
31
 
32
+ struct um_result_entry {
33
+ struct um_result_entry *next;
34
+
35
+ int result;
36
+ int flags;
37
+ };
38
+
32
39
  struct um_op {
33
40
  enum op_state state;
34
41
  struct um_op *prev;
35
42
  struct um_op *next;
43
+
44
+ // linked list for multishot results
45
+ struct um_result_entry *results_head;
46
+ struct um_result_entry *results_tail;
36
47
 
37
48
  VALUE fiber;
38
49
  VALUE resume_value;
50
+ int is_multishot;
51
+ struct __kernel_timespec ts;
52
+
39
53
  int cqe_result;
40
54
  int cqe_flags;
41
55
  };
@@ -51,7 +65,9 @@ struct buf_ring_descriptor {
51
65
  #define BUFFER_RING_MAX_COUNT 10
52
66
 
53
67
  struct um {
54
- struct um_op *freelist_head;
68
+ struct um_op *op_freelist;
69
+ struct um_result_entry *result_freelist;
70
+
55
71
  struct um_op *runqueue_head;
56
72
  struct um_op *runqueue_tail;
57
73
 
@@ -67,31 +83,43 @@ struct um {
67
83
 
68
84
  extern VALUE cUM;
69
85
 
86
+ void um_setup(struct um *machine);
87
+ void um_teardown(struct um *machine);
88
+ void um_free_op_linked_list(struct um *machine, struct um_op *op);
89
+ void um_free_result_linked_list(struct um *machine, struct um_result_entry *entry);
90
+
70
91
  struct __kernel_timespec um_double_to_timespec(double value);
92
+ int um_value_is_exception_p(VALUE v);
93
+ VALUE um_raise_exception(VALUE v);
94
+ void um_raise_on_system_error(int result);
71
95
 
72
- void um_cleanup(struct um *machine);
96
+ void * um_prepare_read_buffer(VALUE buffer, unsigned len, int ofs);
97
+ void um_update_read_buffer(struct um *machine, VALUE buffer, int buffer_offset, int result, int flags);
98
+ VALUE get_string_from_buffer_ring(struct um *machine, int bgid, int result, int flags);
73
99
 
74
- void um_free_linked_list(struct um_op *op);
75
100
  VALUE um_fiber_switch(struct um *machine);
76
101
  VALUE um_await(struct um *machine);
77
102
 
78
103
  void um_op_checkin(struct um *machine, struct um_op *op);
79
104
  struct um_op* um_op_checkout(struct um *machine);
80
-
81
- VALUE um_raise_exception(VALUE v);
105
+ void um_op_result_push(struct um *machine, struct um_op *op, int result, int flags);
106
+ int um_op_result_shift(struct um *machine, struct um_op *op, int *result, int *flags);
82
107
 
83
108
  struct um_op *um_runqueue_find_by_fiber(struct um *machine, VALUE fiber);
84
109
  void um_runqueue_push(struct um *machine, struct um_op *op);
85
110
  struct um_op *um_runqueue_shift(struct um *machine);
86
111
  void um_runqueue_unshift(struct um *machine, struct um_op *op);
87
112
 
88
- int um_value_is_exception_p(VALUE v);
89
-
90
-
91
113
  void um_schedule(struct um *machine, VALUE fiber, VALUE value);
92
114
  void um_interrupt(struct um *machine, VALUE fiber, VALUE value);
93
115
  VALUE um_timeout(struct um *machine, VALUE interval, VALUE class);
94
116
 
95
117
  VALUE um_sleep(struct um *machine, double duration);
118
+ VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset);
119
+ VALUE um_read_each(struct um *machine, int fd, int bgid);
120
+ VALUE um_write(struct um *machine, int fd, VALUE buffer, int len);
121
+
122
+ VALUE um_accept(struct um *machine, int fd);
123
+ VALUE um_accept_each(struct um *machine, int fd);
96
124
 
97
125
  #endif // UM_H
data/ext/um/um_class.c CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "um.h"
2
+ #include <sys/mman.h>
2
3
 
3
4
  VALUE cUM;
4
5
 
@@ -13,7 +14,7 @@ static void UM_compact(void *ptr) {
13
14
  }
14
15
 
15
16
  static void UM_free(void *ptr) {
16
- um_cleanup((struct um *)ptr);
17
+ um_teardown((struct um *)ptr);
17
18
  free(ptr);
18
19
  }
19
20
 
@@ -42,37 +43,58 @@ inline struct um *get_machine(VALUE self) {
42
43
 
43
44
  VALUE UM_initialize(VALUE self) {
44
45
  struct um *machine = RTYPEDDATA_DATA(self);
46
+ um_setup(machine);
47
+ return self;
48
+ }
49
+
50
+ VALUE UM_setup_buffer_ring(VALUE self, VALUE size, VALUE count) {
51
+ struct um *machine = get_machine(self);
45
52
 
46
- machine->ring_initialized = 0;
47
- machine->unsubmitted_count = 0;
48
- machine->buffer_ring_count = 0;
49
- machine->pending_count = 0;
50
- machine->runqueue_head = NULL;
51
- machine->runqueue_tail = NULL;
52
- machine->freelist_head = NULL;
53
-
54
- unsigned prepared_limit = 4096;
55
- int flags = 0;
56
- #ifdef HAVE_IORING_SETUP_SUBMIT_ALL
57
- flags |= IORING_SETUP_SUBMIT_ALL;
58
- #endif
59
- #ifdef HAVE_IORING_SETUP_COOP_TASKRUN
60
- flags |= IORING_SETUP_COOP_TASKRUN;
61
- #endif
62
-
63
- while (1) {
64
- int ret = io_uring_queue_init(prepared_limit, &machine->ring, flags);
65
- if (likely(!ret)) break;
66
-
67
- // if ENOMEM is returned, try with half as much entries
68
- if (unlikely(ret == -ENOMEM && prepared_limit > 64))
69
- prepared_limit = prepared_limit / 2;
70
- else
71
- rb_syserr_fail(-ret, strerror(-ret));
53
+ if (machine->buffer_ring_count == BUFFER_RING_MAX_COUNT)
54
+ rb_raise(rb_eRuntimeError, "Cannot setup more than BUFFER_RING_MAX_COUNT buffer rings");
55
+
56
+ struct buf_ring_descriptor *desc = machine->buffer_rings + machine->buffer_ring_count;
57
+ desc->buf_count = NUM2UINT(count);
58
+ desc->buf_size = NUM2UINT(size);
59
+
60
+ desc->br_size = sizeof(struct io_uring_buf) * desc->buf_count;
61
+ void *mapped = mmap(
62
+ NULL, desc->br_size, PROT_READ | PROT_WRITE,
63
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0
64
+ );
65
+ if (mapped == MAP_FAILED)
66
+ rb_raise(rb_eRuntimeError, "Failed to allocate buffer ring");
67
+
68
+ desc->br = (struct io_uring_buf_ring *)mapped;
69
+ io_uring_buf_ring_init(desc->br);
70
+
71
+ unsigned bg_id = machine->buffer_ring_count;
72
+ struct io_uring_buf_reg reg = {
73
+ .ring_addr = (unsigned long)desc->br,
74
+ .ring_entries = desc->buf_count,
75
+ .bgid = bg_id
76
+ };
77
+ int ret = io_uring_register_buf_ring(&machine->ring, &reg, 0);
78
+ if (ret) {
79
+ munmap(desc->br, desc->br_size);
80
+ rb_syserr_fail(-ret, strerror(-ret));
81
+ }
82
+
83
+ desc->buf_base = malloc(desc->buf_count * desc->buf_size);
84
+ if (!desc->buf_base) {
85
+ io_uring_free_buf_ring(&machine->ring, desc->br, desc->buf_count, bg_id);
86
+ rb_raise(rb_eRuntimeError, "Failed to allocate buffers");
72
87
  }
73
- machine->ring_initialized = 1;
74
88
 
75
- return self;
89
+ int mask = io_uring_buf_ring_mask(desc->buf_count);
90
+ for (unsigned i = 0; i < desc->buf_count; i++) {
91
+ io_uring_buf_ring_add(
92
+ desc->br, desc->buf_base + i * desc->buf_size, desc->buf_size,
93
+ i, mask, i);
94
+ }
95
+ io_uring_buf_ring_advance(desc->br, desc->buf_count);
96
+ machine->buffer_ring_count++;
97
+ return UINT2NUM(bg_id);
76
98
  }
77
99
 
78
100
  VALUE UM_pending_count(VALUE self) {
@@ -114,6 +136,46 @@ VALUE UM_sleep(VALUE self, VALUE duration) {
114
136
  return duration;
115
137
  }
116
138
 
139
+ VALUE UM_read(int argc, VALUE *argv, VALUE self) {
140
+ struct um *machine = get_machine(self);
141
+ VALUE fd;
142
+ VALUE buffer;
143
+ VALUE maxlen;
144
+ VALUE buffer_offset;
145
+ rb_scan_args(argc, argv, "31", &fd, &buffer, &maxlen, &buffer_offset);
146
+
147
+ return um_read(
148
+ machine, NUM2INT(fd), buffer, NUM2INT(maxlen),
149
+ NIL_P(buffer_offset) ? 0 : NUM2INT(buffer_offset)
150
+ );
151
+ }
152
+
153
+ VALUE UM_read_each(VALUE self, VALUE fd, VALUE bgid) {
154
+ struct um *machine = get_machine(self);
155
+ return um_read_each(machine, NUM2INT(fd), NUM2INT(bgid));
156
+ }
157
+
158
+ VALUE UM_write(int argc, VALUE *argv, VALUE self) {
159
+ struct um *machine = get_machine(self);
160
+ VALUE fd;
161
+ VALUE buffer;
162
+ VALUE len;
163
+ rb_scan_args(argc, argv, "21", &fd, &buffer, &len);
164
+
165
+ int bytes = NIL_P(len) ? RSTRING_LEN(buffer) : NUM2INT(len);
166
+ return um_write(machine, NUM2INT(fd), buffer, bytes);
167
+ }
168
+
169
+ VALUE UM_accept(VALUE self, VALUE fd) {
170
+ struct um *machine = get_machine(self);
171
+ return um_accept(machine, NUM2INT(fd));
172
+ }
173
+
174
+ VALUE UM_accept_each(VALUE self, VALUE fd) {
175
+ struct um *machine = get_machine(self);
176
+ return um_accept_each(machine, NUM2INT(fd));
177
+ }
178
+
117
179
  void Init_UM(void) {
118
180
  rb_ext_ractor_safe(true);
119
181
 
@@ -121,8 +183,7 @@ void Init_UM(void) {
121
183
  rb_define_alloc_func(cUM, UM_allocate);
122
184
 
123
185
  rb_define_method(cUM, "initialize", UM_initialize, 0);
124
- // rb_define_method(cUM, "setup_buffer_ring", UM_setup_buffer_ring, 1);
125
-
186
+ rb_define_method(cUM, "setup_buffer_ring", UM_setup_buffer_ring, 2);
126
187
  rb_define_method(cUM, "pending_count", UM_pending_count, 0);
127
188
 
128
189
  rb_define_method(cUM, "snooze", UM_snooze, 0);
@@ -132,6 +193,12 @@ void Init_UM(void) {
132
193
  rb_define_method(cUM, "timeout", UM_timeout, 2);
133
194
 
134
195
  rb_define_method(cUM, "sleep", UM_sleep, 1);
196
+ rb_define_method(cUM, "read", UM_read, -1);
197
+ rb_define_method(cUM, "read_each", UM_read_each, 2);
198
+ rb_define_method(cUM, "write", UM_write, -1);
199
+
200
+ rb_define_method(cUM, "accept", UM_accept, 1);
201
+ rb_define_method(cUM, "accept_each", UM_accept_each, 1);
135
202
 
136
203
  // rb_define_method(cUM, "emit", UM_emit, 1);
137
204
 
data/ext/um/um_ext.c CHANGED
@@ -1,11 +1,5 @@
1
- #include "iou.h"
2
-
3
- void Init_IOURing();
4
- void Init_OpCtx();
5
1
  void Init_UM();
6
2
 
7
3
  void Init_um_ext(void) {
8
- Init_IOURing();
9
- Init_OpCtx();
10
4
  Init_UM();
11
5
  }