uringmachine 0.19.1 → 0.21.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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +3 -4
  3. data/CHANGELOG.md +32 -1
  4. data/TODO.md +0 -39
  5. data/examples/bm_fileno.rb +33 -0
  6. data/examples/bm_mutex.rb +85 -0
  7. data/examples/bm_mutex_single.rb +33 -0
  8. data/examples/bm_queue.rb +29 -29
  9. data/examples/bm_send.rb +2 -5
  10. data/examples/bm_snooze.rb +20 -42
  11. data/examples/bm_write.rb +4 -1
  12. data/examples/fiber_scheduler_demo.rb +15 -51
  13. data/examples/fiber_scheduler_fork.rb +24 -0
  14. data/examples/nc_ssl.rb +71 -0
  15. data/ext/um/extconf.rb +5 -15
  16. data/ext/um/um.c +310 -74
  17. data/ext/um/um.h +66 -29
  18. data/ext/um/um_async_op.c +1 -1
  19. data/ext/um/um_async_op_class.c +2 -2
  20. data/ext/um/um_buffer.c +1 -1
  21. data/ext/um/um_class.c +178 -31
  22. data/ext/um/um_const.c +51 -3
  23. data/ext/um/um_mutex_class.c +1 -1
  24. data/ext/um/um_op.c +37 -0
  25. data/ext/um/um_queue_class.c +1 -1
  26. data/ext/um/um_stream.c +5 -5
  27. data/ext/um/um_stream_class.c +3 -0
  28. data/ext/um/um_sync.c +28 -39
  29. data/ext/um/um_utils.c +59 -19
  30. data/grant-2025/journal.md +353 -0
  31. data/grant-2025/tasks.md +135 -0
  32. data/lib/uringmachine/fiber_scheduler.rb +316 -57
  33. data/lib/uringmachine/version.rb +1 -1
  34. data/lib/uringmachine.rb +6 -0
  35. data/test/test_fiber_scheduler.rb +640 -0
  36. data/test/test_stream.rb +2 -2
  37. data/test/test_um.rb +722 -54
  38. data/uringmachine.gemspec +5 -5
  39. data/vendor/liburing/.github/workflows/ci.yml +94 -1
  40. data/vendor/liburing/.github/workflows/test_build.c +9 -0
  41. data/vendor/liburing/configure +27 -0
  42. data/vendor/liburing/examples/Makefile +6 -0
  43. data/vendor/liburing/examples/helpers.c +8 -0
  44. data/vendor/liburing/examples/helpers.h +5 -0
  45. data/vendor/liburing/liburing.spec +1 -1
  46. data/vendor/liburing/src/Makefile +9 -3
  47. data/vendor/liburing/src/include/liburing/barrier.h +11 -5
  48. data/vendor/liburing/src/include/liburing/io_uring/query.h +41 -0
  49. data/vendor/liburing/src/include/liburing/io_uring.h +51 -0
  50. data/vendor/liburing/src/include/liburing/sanitize.h +16 -4
  51. data/vendor/liburing/src/include/liburing.h +458 -121
  52. data/vendor/liburing/src/liburing-ffi.map +16 -0
  53. data/vendor/liburing/src/liburing.map +8 -0
  54. data/vendor/liburing/src/sanitize.c +4 -1
  55. data/vendor/liburing/src/setup.c +7 -4
  56. data/vendor/liburing/test/232c93d07b74.c +4 -16
  57. data/vendor/liburing/test/Makefile +15 -1
  58. data/vendor/liburing/test/accept.c +2 -13
  59. data/vendor/liburing/test/bind-listen.c +175 -13
  60. data/vendor/liburing/test/conn-unreach.c +132 -0
  61. data/vendor/liburing/test/fd-pass.c +32 -7
  62. data/vendor/liburing/test/fdinfo.c +39 -12
  63. data/vendor/liburing/test/fifo-futex-poll.c +114 -0
  64. data/vendor/liburing/test/fifo-nonblock-read.c +1 -12
  65. data/vendor/liburing/test/futex.c +1 -1
  66. data/vendor/liburing/test/helpers.c +99 -2
  67. data/vendor/liburing/test/helpers.h +9 -0
  68. data/vendor/liburing/test/io_uring_passthrough.c +6 -12
  69. data/vendor/liburing/test/mock_file.c +379 -0
  70. data/vendor/liburing/test/mock_file.h +47 -0
  71. data/vendor/liburing/test/nop.c +2 -2
  72. data/vendor/liburing/test/nop32-overflow.c +150 -0
  73. data/vendor/liburing/test/nop32.c +126 -0
  74. data/vendor/liburing/test/pipe.c +166 -0
  75. data/vendor/liburing/test/poll-race-mshot.c +13 -1
  76. data/vendor/liburing/test/read-write.c +4 -4
  77. data/vendor/liburing/test/recv-mshot-fair.c +81 -34
  78. data/vendor/liburing/test/recvsend_bundle.c +1 -1
  79. data/vendor/liburing/test/resize-rings.c +2 -0
  80. data/vendor/liburing/test/ring-query.c +322 -0
  81. data/vendor/liburing/test/ringbuf-loop.c +87 -0
  82. data/vendor/liburing/test/ringbuf-read.c +4 -4
  83. data/vendor/liburing/test/runtests.sh +2 -2
  84. data/vendor/liburing/test/send-zerocopy.c +43 -5
  85. data/vendor/liburing/test/send_recv.c +103 -32
  86. data/vendor/liburing/test/shutdown.c +2 -12
  87. data/vendor/liburing/test/socket-nb.c +3 -14
  88. data/vendor/liburing/test/socket-rw-eagain.c +2 -12
  89. data/vendor/liburing/test/socket-rw-offset.c +2 -12
  90. data/vendor/liburing/test/socket-rw.c +2 -12
  91. data/vendor/liburing/test/sqe-mixed-bad-wrap.c +87 -0
  92. data/vendor/liburing/test/sqe-mixed-nop.c +82 -0
  93. data/vendor/liburing/test/sqe-mixed-uring_cmd.c +153 -0
  94. data/vendor/liburing/test/timestamp.c +56 -19
  95. data/vendor/liburing/test/vec-regbuf.c +2 -4
  96. data/vendor/liburing/test/wq-aff.c +7 -0
  97. metadata +37 -15
data/ext/um/um.h CHANGED
@@ -4,12 +4,20 @@
4
4
  #include <ruby.h>
5
5
  #include <liburing.h>
6
6
 
7
+
7
8
  // debugging
9
+ enum {
10
+ // set to 1 to enable debug logging
11
+ DEBUG = 0
12
+ };
13
+
8
14
  #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
9
15
  #define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf(": %s\n", StringValueCStr(s)); }
10
16
  #define CALLER() rb_funcall(rb_mKernel, rb_intern("caller"), 0)
11
17
  #define TRACE_CALLER() INSPECT("caller: ", CALLER())
12
18
  #define TRACE_FREE(ptr) //printf("Free %p %s:%d\n", ptr, __FILE__, __LINE__)
19
+ #define DEBUG_MARK(machine, markv, msg) \
20
+ if (machine->mark == markv) printf("%s\n", msg);
13
21
 
14
22
  // branching
15
23
  #ifndef unlikely
@@ -20,7 +28,10 @@
20
28
  #define likely(cond) __builtin_expect(!!(cond), 1)
21
29
  #endif
22
30
 
23
- enum op_kind {
31
+ #define IO_BUFFER_P(buffer) \
32
+ (TYPE(buffer) == RUBY_T_DATA) && rb_obj_is_instance_of(buffer, rb_cIOBuffer)
33
+
34
+ enum um_op_kind {
24
35
  OP_TIMEOUT,
25
36
  OP_SCHEDULE,
26
37
 
@@ -47,7 +58,7 @@ enum op_kind {
47
58
  OP_SHUTDOWN_ASYNC,
48
59
 
49
60
  OP_POLL,
50
- OP_WAITPID,
61
+ OP_WAITID,
51
62
 
52
63
  OP_FUTEX_WAIT,
53
64
  OP_FUTEX_WAKE,
@@ -59,12 +70,17 @@ enum op_kind {
59
70
  OP_SLEEP_MULTISHOT
60
71
  };
61
72
 
62
- #define OP_F_COMPLETED (1U << 0) // op is completed (set on each CQE for multishot ops)
63
- #define OP_F_TRANSIENT (1U << 1) // op is heap allocated
64
- #define OP_F_ASYNC (1U << 2) // op belongs to an AsyncOp
65
- #define OP_F_IGNORE_CANCELED (1U << 3) // CQE with -ECANCEL should be ignored
66
- #define OP_F_MULTISHOT (1U << 4) // op is multishot
67
- #define OP_F_FREE_ON_COMPLETE (1U << 5) // op should be freed on receiving CQE
73
+ #define OP_F_COMPLETED (1U << 0) // op is completed (set on each CQE for multishot ops)
74
+ #define OP_F_TRANSIENT (1U << 1) // op is heap allocated
75
+ #define OP_F_ASYNC (1U << 2) // op belongs to an AsyncOp
76
+ #define OP_F_CANCELED (1U << 3) // op is cancelled
77
+ #define OP_F_IGNORE_CANCELED (1U << 4) // CQE with -ECANCEL should be ignored
78
+ #define OP_F_MULTISHOT (1U << 5) // op is multishot
79
+ #define OP_F_FREE_ON_COMPLETE (1U << 6) // op should be freed on receiving CQE
80
+ #define OP_F_RUNQUEUE_SKIP (1U << 7) // runqueue entry should be skipped
81
+ #define OP_F_SELECT_POLLIN (1U << 8) // select POLLIN
82
+ #define OP_F_SELECT_POLLOUT (1U << 9) // select POLLOUT
83
+ #define OP_F_SELECT_POLLPRI (1U << 10) // select POLLPRI
68
84
 
69
85
  struct um_op_result {
70
86
  __s32 res;
@@ -76,8 +92,8 @@ struct um_op {
76
92
  struct um_op *prev;
77
93
  struct um_op *next;
78
94
 
79
- enum op_kind kind;
80
- unsigned flags;
95
+ enum um_op_kind kind;
96
+ uint flags;
81
97
 
82
98
  VALUE fiber;
83
99
  VALUE value;
@@ -85,7 +101,7 @@ struct um_op {
85
101
 
86
102
  struct um_op_result result;
87
103
  struct um_op_result *multishot_result_tail;
88
- unsigned multishot_result_count;
104
+ uint multishot_result_count;
89
105
 
90
106
  struct __kernel_timespec ts; // used for timeout operation
91
107
  };
@@ -114,12 +130,18 @@ struct um {
114
130
 
115
131
  struct io_uring ring;
116
132
 
117
- unsigned int ring_initialized;
118
- unsigned int unsubmitted_count;
119
- unsigned int pending_count;
133
+ uint ring_initialized; // is the ring initialized successfully
134
+ uint mark; // used to mark instances for debugging
135
+
136
+ uint unsubmitted_count; // number of unsubmitted SQEs pending
137
+ uint pending_count; // number of pending operations (i.e. not yet completed)
138
+ uint buffer_ring_count; // number of registered buffer rings
139
+ ulong total_op_count; // total number of operations submitted since ring was initialized
140
+
141
+ uint entries; // number of entries in SQ
142
+ uint sqpoll_mode; // SQPOLL mode enabled
120
143
 
121
144
  struct buf_ring_descriptor buffer_rings[BUFFER_RING_MAX_COUNT];
122
- unsigned int buffer_ring_count;
123
145
 
124
146
  struct um_op *transient_head;
125
147
  struct um_op *runqueue_head;
@@ -131,6 +153,7 @@ struct um {
131
153
 
132
154
  struct um_mutex {
133
155
  uint32_t state;
156
+ uint32_t num_waiters;
134
157
  };
135
158
 
136
159
  struct um_queue_entry {
@@ -173,14 +196,17 @@ struct um_write_buffer {
173
196
  };
174
197
 
175
198
  extern VALUE cUM;
199
+ extern VALUE eUMError;
176
200
  extern VALUE cMutex;
177
201
  extern VALUE cQueue;
178
202
  extern VALUE cAsyncOp;
203
+ extern VALUE eStreamRESPError;
179
204
 
180
205
  struct um *um_get_machine(VALUE self);
181
- void um_setup(VALUE self, struct um *machine);
206
+ void um_setup(VALUE self, struct um *machine, uint entries, uint sqpoll_timeout_msec);
182
207
  void um_teardown(struct um *machine);
183
208
 
209
+ const char * um_op_kind_name(enum um_op_kind kind);
184
210
  struct um_op *um_op_alloc(struct um *machine);
185
211
  void um_op_free(struct um *machine, struct um_op *op);
186
212
  void um_op_clear(struct um *machine, struct um_op *op);
@@ -204,21 +230,24 @@ double um_timestamp_to_double(__s64 tv_sec, __u32 tv_nsec);
204
230
  int um_value_is_exception_p(VALUE v);
205
231
  VALUE um_raise_exception(VALUE v);
206
232
 
207
- #define RAISE_IF_EXCEPTION(v) if (um_value_is_exception_p(v)) { um_raise_exception(v); }
233
+ #define RAISE_IF_EXCEPTION(v) if (unlikely(um_value_is_exception_p(v))) { um_raise_exception(v); }
208
234
 
209
- void um_prep_op(struct um *machine, struct um_op *op, enum op_kind kind, unsigned flags);
235
+ void um_prep_op(struct um *machine, struct um_op *op, enum um_op_kind kind, unsigned flags);
210
236
  void um_raise_on_error_result(int result);
211
- void * um_prepare_read_buffer(VALUE buffer, unsigned len, int ofs);
212
- void um_update_read_buffer(struct um *machine, VALUE buffer, int buffer_offset, __s32 result, __u32 flags);
237
+ void um_get_buffer_bytes_for_writing(VALUE buffer, const void **base, size_t *size);
238
+ void * um_prepare_read_buffer(VALUE buffer, ssize_t len, ssize_t ofs);
239
+ void um_update_read_buffer(struct um *machine, VALUE buffer, ssize_t buffer_offset, __s32 result, __u32 flags);
213
240
  int um_setup_buffer_ring(struct um *machine, unsigned size, unsigned count);
214
241
  VALUE um_get_string_from_buffer_ring(struct um *machine, int bgid, __s32 result, __u32 flags);
215
242
  void um_add_strings_to_buffer_ring(struct um *machine, int bgid, VALUE strings);
216
243
 
217
244
  struct io_uring_sqe *um_get_sqe(struct um *machine, struct um_op *op);
218
245
 
246
+ uint um_submit(struct um *machine);
219
247
  VALUE um_fiber_switch(struct um *machine);
220
248
  VALUE um_await(struct um *machine);
221
- void um_submit_cancel_op(struct um *machine, struct um_op *op);
249
+ VALUE um_wakeup(struct um *machine);
250
+ void um_cancel_op(struct um *machine, struct um_op *op);
222
251
  void um_cancel_and_wait(struct um *machine, struct um_op *op);
223
252
  int um_check_completion(struct um *machine, struct um_op *op);
224
253
 
@@ -229,25 +258,31 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class);
229
258
 
230
259
  VALUE um_sleep(struct um *machine, double duration);
231
260
  VALUE um_periodically(struct um *machine, double interval);
232
- VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset);
233
- size_t um_read_raw(struct um *machine, int fd, char *buffer, int maxlen);
261
+ VALUE um_read(struct um *machine, int fd, VALUE buffer, size_t maxlen, ssize_t buffer_offset, __u64 file_offset);
262
+ size_t um_read_raw(struct um *machine, int fd, char *buffer, size_t maxlen);
234
263
  VALUE um_read_each(struct um *machine, int fd, int bgid);
235
- VALUE um_write(struct um *machine, int fd, VALUE str, int len);
264
+ VALUE um_write(struct um *machine, int fd, VALUE buffer, size_t len, __u64 file_offset);
265
+ VALUE um_write_async(struct um *machine, int fd, VALUE buffer, size_t len, __u64 file_offset);
236
266
  VALUE um_close(struct um *machine, int fd);
237
267
  VALUE um_close_async(struct um *machine, int fd);
238
268
  VALUE um_open(struct um *machine, VALUE pathname, int flags, int mode);
239
269
  VALUE um_poll(struct um *machine, int fd, unsigned mask);
240
- VALUE um_waitpid(struct um *machine, int pid, int options);
270
+ VALUE um_select(struct um *machine, VALUE rfds, VALUE wfds, VALUE efds);
271
+ VALUE um_waitid(struct um *machine, int idtype, int id, int options);
272
+
273
+ #ifdef HAVE_RB_PROCESS_STATUS_NEW
274
+ VALUE um_waitid_status(struct um *machine, int idtype, int id, int options);
275
+ #endif
276
+
241
277
  VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask);
242
- VALUE um_write_async(struct um *machine, int fd, VALUE str);
243
278
 
244
279
  VALUE um_accept(struct um *machine, int fd);
245
280
  VALUE um_accept_each(struct um *machine, int fd);
246
281
  VALUE um_socket(struct um *machine, int domain, int type, int protocol, uint flags);
247
282
  VALUE um_connect(struct um *machine, int fd, const struct sockaddr *addr, socklen_t addrlen);
248
- VALUE um_send(struct um *machine, int fd, VALUE buffer, int len, int flags);
283
+ VALUE um_send(struct um *machine, int fd, VALUE buffer, size_t len, int flags);
249
284
  VALUE um_send_bundle(struct um *machine, int fd, int bgid, VALUE strings);
250
- VALUE um_recv(struct um *machine, int fd, VALUE buffer, int maxlen, int flags);
285
+ VALUE um_recv(struct um *machine, int fd, VALUE buffer, size_t maxlen, int flags);
251
286
  VALUE um_recv_each(struct um *machine, int fd, int bgid, int flags);
252
287
  VALUE um_bind(struct um *machine, int fd, struct sockaddr *addr, socklen_t addrlen);
253
288
  VALUE um_listen(struct um *machine, int fd, int backlog);
@@ -266,7 +301,7 @@ struct um_mutex *Mutex_data(VALUE self);
266
301
  struct um_queue *Queue_data(VALUE self);
267
302
 
268
303
  void um_mutex_init(struct um_mutex *mutex);
269
- VALUE um_mutex_synchronize(struct um *machine, VALUE mutex, uint32_t *state);
304
+ VALUE um_mutex_synchronize(struct um *machine, struct um_mutex *mutex);
270
305
 
271
306
  void um_queue_init(struct um_queue *queue);
272
307
  void um_queue_free(struct um_queue *queue);
@@ -282,6 +317,8 @@ VALUE stream_get_string(struct um_stream *stream, VALUE buf, ssize_t len);
282
317
  VALUE resp_decode(struct um_stream *stream, VALUE out_buffer);
283
318
  void resp_encode(struct um_write_buffer *buf, VALUE obj);
284
319
 
320
+ __attribute__((noreturn)) void um_raise_internal_error(const char *msg);
321
+
285
322
  void write_buffer_init(struct um_write_buffer *buf, VALUE str);
286
323
  void write_buffer_update_len(struct um_write_buffer *buf);
287
324
 
data/ext/um/um_async_op.c CHANGED
@@ -36,5 +36,5 @@ VALUE um_async_op_await(struct um_async_op *async_op) {
36
36
  }
37
37
 
38
38
  void um_async_op_cancel(struct um_async_op *async_op) {
39
- um_submit_cancel_op(async_op->machine, async_op->op);
39
+ um_cancel_op(async_op->machine, async_op->op);
40
40
  }
@@ -52,7 +52,7 @@ void um_async_op_set(VALUE self, struct um *machine, struct um_op *op) {
52
52
 
53
53
  inline void raise_on_missing_op(struct um_async_op *async_op) {
54
54
  if (!async_op->op)
55
- rb_raise(rb_eRuntimeError, "Missing op");
55
+ um_raise_internal_error("Missing op");
56
56
  }
57
57
 
58
58
  inline int async_op_is_done(struct um_async_op *async_op) {
@@ -67,7 +67,7 @@ VALUE AsyncOp_kind(VALUE self) {
67
67
  case OP_TIMEOUT:
68
68
  return SYM_timeout;
69
69
  default:
70
- rb_raise(rb_eRuntimeError, "Invalid op kind");
70
+ um_raise_internal_error("Invalid op kind");
71
71
  }
72
72
  }
73
73
 
data/ext/um/um_buffer.c CHANGED
@@ -28,7 +28,7 @@ inline struct um_buffer *um_buffer_checkout(struct um *machine, int len) {
28
28
 
29
29
  buffer->len = buffer_size(len);
30
30
  if (posix_memalign(&buffer->ptr, 4096, buffer->len))
31
- rb_raise(rb_eRuntimeError, "Failed to allocate buffer");
31
+ um_raise_internal_error("Failed to allocate buffer");
32
32
  }
33
33
  return buffer;
34
34
  }
data/ext/um/um_class.c CHANGED
@@ -1,7 +1,13 @@
1
1
  #include "um.h"
2
2
  #include <arpa/inet.h>
3
+ #include <ruby/io.h>
4
+ #include <sys/syscall.h>
5
+ #include <unistd.h>
3
6
 
4
7
  VALUE cUM;
8
+ VALUE eUMError;
9
+
10
+ static ID id_fileno;
5
11
 
6
12
  static void UM_mark(void *ptr) {
7
13
  struct um *machine = ptr;
@@ -43,14 +49,38 @@ static VALUE UM_allocate(VALUE klass) {
43
49
  inline struct um *um_get_machine(VALUE self) {
44
50
  struct um *um;
45
51
  TypedData_Get_Struct(self, struct um, &UringMachine_type, um);
46
- if (!um->ring_initialized) rb_raise(rb_eRuntimeError, "Machine not initialized");
52
+ if (!um->ring_initialized) um_raise_internal_error("Machine not initialized");
47
53
 
48
54
  return um;
49
55
  }
50
56
 
51
- VALUE UM_initialize(VALUE self) {
57
+ static inline uint get_sqpoll_timeout_msec(VALUE sqpoll_timeout) {
58
+ switch (TYPE(sqpoll_timeout)) {
59
+ case T_NIL:
60
+ case T_FALSE:
61
+ return 0;
62
+ case T_FLOAT:
63
+ return (uint)(NUM2DBL(sqpoll_timeout) * 1000);
64
+ case T_FIXNUM:
65
+ return NUM2UINT(sqpoll_timeout) * 1000;
66
+ case T_TRUE:
67
+ return 1000;
68
+ default:
69
+ rb_raise(eUMError, "Invalid sqpoll_timeout value");
70
+ }
71
+ }
72
+
73
+ VALUE UM_initialize(int argc, VALUE *argv, VALUE self) {
52
74
  struct um *machine = RTYPEDDATA_DATA(self);
53
- um_setup(self, machine);
75
+ VALUE entries;
76
+ VALUE sqpoll_timeout;
77
+ rb_scan_args(argc, argv, "02", &entries, &sqpoll_timeout);
78
+
79
+ uint entries_i = NIL_P(entries) ? 0 : NUM2UINT(entries);
80
+ uint sqpoll_timeout_msec = get_sqpoll_timeout_msec(sqpoll_timeout);
81
+
82
+
83
+ um_setup(self, machine, entries_i, sqpoll_timeout_msec);
54
84
  return self;
55
85
  }
56
86
 
@@ -60,9 +90,25 @@ VALUE UM_setup_buffer_ring(VALUE self, VALUE size, VALUE count) {
60
90
  return INT2NUM(bgid);
61
91
  }
62
92
 
93
+ VALUE UM_entries(VALUE self) {
94
+ struct um *machine = um_get_machine(self);
95
+ return UINT2NUM(machine->entries);
96
+ }
97
+
98
+ VALUE UM_mark_m(VALUE self, VALUE mark) {
99
+ struct um *machine = um_get_machine(self);
100
+ machine->mark = NUM2UINT(mark);
101
+ return self;
102
+ }
103
+
63
104
  VALUE UM_pending_count(VALUE self) {
64
105
  struct um *machine = um_get_machine(self);
65
- return INT2NUM(machine->pending_count);
106
+ return UINT2NUM(machine->pending_count);
107
+ }
108
+
109
+ VALUE UM_total_op_count(VALUE self) {
110
+ struct um *machine = um_get_machine(self);
111
+ return UINT2NUM(machine->total_op_count);
66
112
  }
67
113
 
68
114
  VALUE UM_snooze(VALUE self) {
@@ -76,6 +122,17 @@ VALUE UM_yield(VALUE self) {
76
122
  return um_await(machine);
77
123
  }
78
124
 
125
+ VALUE UM_wakeup(VALUE self) {
126
+ struct um *machine = um_get_machine(self);
127
+ return um_wakeup(machine);
128
+ }
129
+
130
+ VALUE UM_submit(VALUE self) {
131
+ struct um *machine = um_get_machine(self);
132
+ uint ret = um_submit(machine);
133
+ return UINT2NUM(ret);
134
+ }
135
+
79
136
  VALUE UM_schedule(VALUE self, VALUE fiber, VALUE value) {
80
137
  struct um *machine = um_get_machine(self);
81
138
  um_schedule(machine, fiber, value);
@@ -103,21 +160,19 @@ VALUE UM_read(int argc, VALUE *argv, VALUE self) {
103
160
  VALUE buffer;
104
161
  VALUE maxlen;
105
162
  VALUE buffer_offset;
106
- rb_scan_args(argc, argv, "31", &fd, &buffer, &maxlen, &buffer_offset);
163
+ VALUE file_offset;
164
+ rb_scan_args(argc, argv, "23", &fd, &buffer, &maxlen, &buffer_offset, &file_offset);
107
165
 
108
- return um_read(
109
- machine, NUM2INT(fd), buffer, NUM2INT(maxlen),
110
- NIL_P(buffer_offset) ? 0 : NUM2INT(buffer_offset)
111
- );
166
+ ssize_t maxlen_i = NIL_P(maxlen) ? -1 : NUM2INT(maxlen);
167
+ ssize_t buffer_offset_i = NIL_P(buffer_offset) ? 0 : NUM2INT(buffer_offset);
168
+ __u64 file_offset_i = NIL_P(file_offset) ? (__u64)-1 : NUM2UINT(file_offset);
169
+
170
+ return um_read(machine, NUM2INT(fd), buffer, maxlen_i, buffer_offset_i, file_offset_i);
112
171
  }
113
172
 
114
173
  VALUE UM_read_each(VALUE self, VALUE fd, VALUE bgid) {
115
- #ifdef HAVE_IO_URING_PREP_READ_MULTISHOT
116
174
  struct um *machine = um_get_machine(self);
117
175
  return um_read_each(machine, NUM2INT(fd), NUM2INT(bgid));
118
- #else
119
- rb_raise(rb_eRuntimeError, "Not supported by kernel");
120
- #endif
121
176
  }
122
177
 
123
178
  VALUE UM_write(int argc, VALUE *argv, VALUE self) {
@@ -125,15 +180,27 @@ VALUE UM_write(int argc, VALUE *argv, VALUE self) {
125
180
  VALUE fd;
126
181
  VALUE buffer;
127
182
  VALUE len;
128
- rb_scan_args(argc, argv, "21", &fd, &buffer, &len);
183
+ VALUE file_offset;
184
+ rb_scan_args(argc, argv, "22", &fd, &buffer, &len, &file_offset);
185
+
186
+ size_t len_i = NIL_P(len) ? (size_t)-1 : NUM2UINT(len);
187
+ __u64 file_offset_i = NIL_P(file_offset) ? (__u64)-1 : NUM2UINT(file_offset);
129
188
 
130
- int bytes = NIL_P(len) ? RSTRING_LEN(buffer) : NUM2INT(len);
131
- return um_write(machine, NUM2INT(fd), buffer, bytes);
189
+ return um_write(machine, NUM2INT(fd), buffer, len_i, file_offset_i);
132
190
  }
133
191
 
134
- VALUE UM_write_async(VALUE self, VALUE fd, VALUE str) {
192
+ VALUE UM_write_async(int argc, VALUE *argv, VALUE self) {
135
193
  struct um *machine = um_get_machine(self);
136
- return um_write_async(machine, NUM2INT(fd), str);
194
+ VALUE fd;
195
+ VALUE buffer;
196
+ VALUE len;
197
+ VALUE file_offset;
198
+ rb_scan_args(argc, argv, "22", &fd, &buffer, &len, &file_offset);
199
+
200
+ size_t len_i = NIL_P(len) ? (size_t)-1 : NUM2UINT(len);
201
+ __u64 file_offset_i = NIL_P(file_offset) ? (__u64)-1 : NUM2UINT(file_offset);
202
+
203
+ return um_write_async(machine, NUM2INT(fd), buffer, len_i, file_offset_i);
137
204
  }
138
205
 
139
206
  VALUE UM_statx(VALUE self, VALUE dirfd, VALUE path, VALUE flags, VALUE mask) {
@@ -270,12 +337,10 @@ VALUE UM_setsockopt(VALUE self, VALUE fd, VALUE level, VALUE opt, VALUE value) {
270
337
  return um_setsockopt(machine, NUM2INT(fd), NUM2INT(level), NUM2INT(opt), numeric_value(value));
271
338
  }
272
339
 
273
- #ifdef HAVE_IO_URING_PREP_FUTEX
274
-
275
340
  VALUE UM_mutex_synchronize(VALUE self, VALUE mutex) {
276
341
  struct um *machine = um_get_machine(self);
277
342
  struct um_mutex *mutex_data = Mutex_data(mutex);
278
- return um_mutex_synchronize(machine, mutex, &mutex_data->state);
343
+ return um_mutex_synchronize(machine, mutex_data);
279
344
  }
280
345
 
281
346
  VALUE UM_queue_push(VALUE self, VALUE queue, VALUE value) {
@@ -302,8 +367,6 @@ VALUE UM_queue_shift(VALUE self, VALUE queue) {
302
367
  return um_queue_shift(machine, que);
303
368
  }
304
369
 
305
- #endif
306
-
307
370
  struct um_open_ctx {
308
371
  VALUE self;
309
372
  VALUE fd;
@@ -332,10 +395,22 @@ VALUE UM_poll(VALUE self, VALUE fd, VALUE mask) {
332
395
  return um_poll(machine, NUM2INT(fd), NUM2UINT(mask));
333
396
  }
334
397
 
335
- VALUE UM_waitpid(VALUE self, VALUE pid, VALUE options) {
398
+ VALUE UM_select(VALUE self, VALUE rfds, VALUE wfds, VALUE efds) {
399
+ struct um *machine = um_get_machine(self);
400
+ return um_select(machine, rfds, wfds, efds);
401
+ }
402
+
403
+ VALUE UM_waitid(VALUE self, VALUE idtype, VALUE id, VALUE options) {
404
+ struct um *machine = um_get_machine(self);
405
+ return um_waitid(machine, NUM2INT(idtype), NUM2INT(id), NUM2INT(options));
406
+ }
407
+
408
+ #ifdef HAVE_RB_PROCESS_STATUS_NEW
409
+ VALUE UM_waitid_status(VALUE self, VALUE idtype, VALUE id, VALUE options) {
336
410
  struct um *machine = um_get_machine(self);
337
- return um_waitpid(machine, NUM2INT(pid), NUM2INT(options));
411
+ return um_waitid_status(machine, NUM2INT(idtype), NUM2INT(id), NUM2INT(options));
338
412
  }
413
+ #endif
339
414
 
340
415
  VALUE UM_prep_timeout(VALUE self, VALUE interval) {
341
416
  struct um *machine = um_get_machine(self);
@@ -353,28 +428,93 @@ VALUE UM_pipe(VALUE self) {
353
428
  return rb_ary_new_from_args(2, INT2NUM(fds[0]), INT2NUM(fds[1]));
354
429
  }
355
430
 
431
+ VALUE UM_pidfd_open(VALUE self, VALUE pid) {
432
+ int fd = syscall(SYS_pidfd_open, NUM2INT(pid), 0);
433
+ if (fd == -1) {
434
+ int e = errno;
435
+ rb_syserr_fail(e, strerror(e));
436
+ }
437
+
438
+ return INT2NUM(fd);
439
+ }
440
+
441
+ VALUE UM_pidfd_send_signal(VALUE self, VALUE fd, VALUE sig) {
442
+ int ret = syscall(
443
+ SYS_pidfd_send_signal, NUM2INT(fd), NUM2INT(sig), NULL, 0
444
+ );
445
+ if (ret) {
446
+ int e = errno;
447
+ rb_syserr_fail(e, strerror(e));
448
+ }
449
+
450
+ return fd;
451
+ }
452
+
453
+ VALUE UM_io_nonblock_p(VALUE self, VALUE io) {
454
+ int fd = rb_io_descriptor(io);
455
+ int oflags = fcntl(fd, F_GETFL);
456
+ if (oflags == -1) return Qnil;
457
+
458
+ return (oflags & O_NONBLOCK) ? Qtrue : Qfalse;
459
+ }
460
+
461
+ VALUE UM_io_set_nonblock(VALUE self, VALUE io, VALUE nonblock) {
462
+ int fd = rb_io_descriptor(io);
463
+ int oflags = fcntl(fd, F_GETFL);
464
+ if (oflags == -1) return Qnil;
465
+
466
+ if (RTEST(nonblock)) {
467
+ if (!(oflags & O_NONBLOCK)) {
468
+ oflags |= O_NONBLOCK;
469
+ fcntl(fd, F_SETFL, oflags);
470
+ }
471
+ }
472
+ else {
473
+ if (oflags & O_NONBLOCK) {
474
+ oflags &= ~O_NONBLOCK;
475
+ fcntl(fd, F_SETFL, oflags);
476
+ }
477
+ }
478
+ return nonblock;
479
+ }
480
+
356
481
  VALUE UM_kernel_version(VALUE self) {
357
482
  return INT2NUM(UM_KERNEL_VERSION);
358
483
  }
359
484
 
485
+ VALUE UM_debug(VALUE self, VALUE str) {
486
+ printf("%s\n", StringValueCStr(str));
487
+ return Qnil;
488
+ }
489
+
360
490
  void Init_UM(void) {
361
491
  rb_ext_ractor_safe(true);
362
492
 
363
493
  cUM = rb_define_class("UringMachine", rb_cObject);
364
494
  rb_define_alloc_func(cUM, UM_allocate);
365
495
 
366
- rb_define_method(cUM, "initialize", UM_initialize, 0);
496
+ rb_define_method(cUM, "initialize", UM_initialize, -1);
497
+ rb_define_method(cUM, "entries", UM_entries, 0);
498
+ rb_define_method(cUM, "mark", UM_mark_m, 1);
367
499
  rb_define_method(cUM, "pending_count", UM_pending_count, 0);
500
+ rb_define_method(cUM, "total_op_count", UM_total_op_count, 0);
368
501
  rb_define_method(cUM, "setup_buffer_ring", UM_setup_buffer_ring, 2);
369
502
 
370
503
  rb_define_singleton_method(cUM, "pipe", UM_pipe, 0);
371
- rb_define_singleton_method(cUM, "kernel_version", UM_kernel_version, 0);
504
+ rb_define_singleton_method(cUM, "pidfd_open", UM_pidfd_open, 1);
505
+ rb_define_singleton_method(cUM, "pidfd_send_signal", UM_pidfd_send_signal, 2);
372
506
 
507
+ rb_define_singleton_method(cUM, "io_nonblock?", UM_io_nonblock_p, 1);
508
+ rb_define_singleton_method(cUM, "io_set_nonblock", UM_io_set_nonblock, 2);
509
+ rb_define_singleton_method(cUM, "kernel_version", UM_kernel_version, 0);
510
+ rb_define_singleton_method(cUM, "debug", UM_debug, 1);
373
511
 
374
512
  rb_define_method(cUM, "schedule", UM_schedule, 2);
375
513
  rb_define_method(cUM, "snooze", UM_snooze, 0);
376
514
  rb_define_method(cUM, "timeout", UM_timeout, 2);
377
515
  rb_define_method(cUM, "yield", UM_yield, 0);
516
+ rb_define_method(cUM, "wakeup", UM_wakeup, 0);
517
+ rb_define_method(cUM, "submit", UM_submit, 0);
378
518
 
379
519
  rb_define_method(cUM, "close", UM_close, 1);
380
520
  rb_define_method(cUM, "close_async", UM_close_async, 1);
@@ -384,11 +524,16 @@ void Init_UM(void) {
384
524
  rb_define_method(cUM, "sleep", UM_sleep, 1);
385
525
  rb_define_method(cUM, "periodically", UM_periodically, 1);
386
526
  rb_define_method(cUM, "write", UM_write, -1);
387
- rb_define_method(cUM, "write_async", UM_write_async, 2);
527
+ rb_define_method(cUM, "write_async", UM_write_async, -1);
388
528
  rb_define_method(cUM, "statx", UM_statx, 4);
389
529
 
390
530
  rb_define_method(cUM, "poll", UM_poll, 2);
391
- rb_define_method(cUM, "waitpid", UM_waitpid, 2);
531
+ rb_define_method(cUM, "select", UM_select, 3);
532
+ rb_define_method(cUM, "waitid", UM_waitid, 3);
533
+
534
+ #ifdef HAVE_RB_PROCESS_STATUS_NEW
535
+ rb_define_method(cUM, "waitid_status", UM_waitid_status, 3);
536
+ #endif
392
537
 
393
538
  rb_define_method(cUM, "accept", UM_accept, 1);
394
539
  rb_define_method(cUM, "accept_each", UM_accept_each, 1);
@@ -407,13 +552,15 @@ void Init_UM(void) {
407
552
 
408
553
  rb_define_method(cUM, "prep_timeout", UM_prep_timeout, 1);
409
554
 
410
- #ifdef HAVE_IO_URING_PREP_FUTEX
411
555
  rb_define_method(cUM, "pop", UM_queue_pop, 1);
412
556
  rb_define_method(cUM, "push", UM_queue_push, 2);
413
557
  rb_define_method(cUM, "shift", UM_queue_shift, 1);
414
558
  rb_define_method(cUM, "synchronize", UM_mutex_synchronize, 1);
415
559
  rb_define_method(cUM, "unshift", UM_queue_unshift, 2);
416
- #endif
560
+
561
+ eUMError = rb_define_class_under(cUM, "Error", rb_eStandardError);
417
562
 
418
563
  um_define_net_constants(cUM);
564
+
565
+ id_fileno = rb_intern_const("fileno");
419
566
  }
data/ext/um/um_const.c CHANGED
@@ -13,6 +13,7 @@
13
13
  #include <netdb.h>
14
14
  #include <net/if.h>
15
15
  #include <poll.h>
16
+ #include <signal.h>
16
17
 
17
18
  #define DEF_CONST_INT(mod, v) rb_define_const(mod, #v, INT2NUM(v))
18
19
 
@@ -88,12 +89,12 @@ void um_define_net_constants(VALUE mod) {
88
89
  DEF_CONST_INT(mod, O_TRUNC);
89
90
  DEF_CONST_INT(mod, O_WRONLY);
90
91
 
91
- DEF_CONST_INT(mod, WNOHANG);
92
- DEF_CONST_INT(mod, WUNTRACED);
93
92
  DEF_CONST_INT(mod, WCONTINUED);
94
93
  DEF_CONST_INT(mod, WEXITED);
95
- DEF_CONST_INT(mod, WSTOPPED);
94
+ DEF_CONST_INT(mod, WNOHANG);
96
95
  DEF_CONST_INT(mod, WNOWAIT);
96
+ DEF_CONST_INT(mod, WSTOPPED);
97
+ DEF_CONST_INT(mod, WUNTRACED);
97
98
 
98
99
  DEF_CONST_INT(mod, SOCK_STREAM);
99
100
  DEF_CONST_INT(mod, SOCK_DGRAM);
@@ -391,4 +392,51 @@ void um_define_net_constants(VALUE mod) {
391
392
  DEF_CONST_INT(mod, EKEYREJECTED);
392
393
  DEF_CONST_INT(mod, EOWNERDEAD);
393
394
  DEF_CONST_INT(mod, ENOTRECOVERABLE);
395
+
396
+ DEF_CONST_INT(mod, P_PID);
397
+ DEF_CONST_INT(mod, P_PIDFD);
398
+ DEF_CONST_INT(mod, P_PGID);
399
+ DEF_CONST_INT(mod, P_ALL);
400
+
401
+ DEF_CONST_INT(mod, CLD_EXITED);
402
+ DEF_CONST_INT(mod, CLD_KILLED);
403
+ DEF_CONST_INT(mod, CLD_DUMPED);
404
+ DEF_CONST_INT(mod, CLD_STOPPED);
405
+ DEF_CONST_INT(mod, CLD_TRAPPED);
406
+ DEF_CONST_INT(mod, CLD_CONTINUED);
407
+
408
+ DEF_CONST_INT(mod, SIGHUP);
409
+ DEF_CONST_INT(mod, SIGINT);
410
+ DEF_CONST_INT(mod, SIGQUIT);
411
+ DEF_CONST_INT(mod, SIGILL);
412
+ DEF_CONST_INT(mod, SIGTRAP);
413
+ DEF_CONST_INT(mod, SIGABRT);
414
+ DEF_CONST_INT(mod, SIGIOT);
415
+ DEF_CONST_INT(mod, SIGFPE);
416
+ DEF_CONST_INT(mod, SIGKILL);
417
+ DEF_CONST_INT(mod, SIGBUS);
418
+ DEF_CONST_INT(mod, SIGSEGV);
419
+ DEF_CONST_INT(mod, SIGSYS);
420
+ DEF_CONST_INT(mod, SIGPIPE);
421
+ DEF_CONST_INT(mod, SIGALRM);
422
+ DEF_CONST_INT(mod, SIGTERM);
423
+ DEF_CONST_INT(mod, SIGURG);
424
+ DEF_CONST_INT(mod, SIGSTOP);
425
+ DEF_CONST_INT(mod, SIGTSTP);
426
+ DEF_CONST_INT(mod, SIGCONT);
427
+ DEF_CONST_INT(mod, SIGCHLD);
428
+ DEF_CONST_INT(mod, SIGCLD);
429
+ DEF_CONST_INT(mod, SIGTTIN);
430
+ DEF_CONST_INT(mod, SIGTTOU);
431
+ DEF_CONST_INT(mod, SIGIO);
432
+ DEF_CONST_INT(mod, SIGXCPU);
433
+ DEF_CONST_INT(mod, SIGXFSZ);
434
+ DEF_CONST_INT(mod, SIGVTALRM);
435
+ DEF_CONST_INT(mod, SIGPROF);
436
+ DEF_CONST_INT(mod, SIGWINCH);
437
+ DEF_CONST_INT(mod, SIGUSR1);
438
+ DEF_CONST_INT(mod, SIGUSR2);
439
+ DEF_CONST_INT(mod, SIGPWR);
440
+ DEF_CONST_INT(mod, SIGPOLL);
441
+
394
442
  }
@@ -11,7 +11,7 @@ static const rb_data_type_t Mutex_type = {
11
11
  .dsize = NULL,
12
12
  .dcompact = NULL
13
13
  },
14
- .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
14
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
15
15
  };
16
16
 
17
17
  static VALUE Mutex_allocate(VALUE klass) {