iou 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81760683a4ded7963311f6d8961dff7d0f13edd164afb4cf3056f151889f612d
4
- data.tar.gz: c700916bbe4b519bc95522da3855fa084f40ab56d8c8cebd2f1d04521681add2
3
+ metadata.gz: f240c18e2449f95a1a41d0af6ddea012c5ade81125c50149930f084499d8721f
4
+ data.tar.gz: 49c12b7fa7876af3666d93a891d6edac4f7b50ca034c683bc667de3b4bd6410f
5
5
  SHA512:
6
- metadata.gz: f1aba30375539d2f16083bce2db47a3807abd2a2fa484c270d57a8954bbe3a21d9785408bba667c48e991b47805b38686a4b263602b2b57cf17f30fd32c59187
7
- data.tar.gz: e812e047b19ea89c9a0731de4f216dbacf3e1bdc62e363bc5f31fb843b19fc072a3b0e3272152ff215efd90591ec220bc23aff12d7a6dae755153c9fe6892a76
6
+ metadata.gz: 38194dea0e61f9a2801d74255e5a1db6cdae3739c45667a6dada382a9362698c4eaf46120e04ec2fdb51f6dea995fc30493c3957e3027dc2c3d767d2663b3f36
7
+ data.tar.gz: e4cfcb063713986a788a344f13492eacc76ae5f0db5111367fe4ef91bc8effdb333e850f3f043fb222fc6516e52533de10ed6bc17c99b20221f57f32134974b2
@@ -0,0 +1,35 @@
1
+ name: Tests
2
+
3
+ on: [push, pull_request]
4
+
5
+ concurrency:
6
+ group: tests-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
7
+ cancel-in-progress: true
8
+
9
+ jobs:
10
+ build:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ # macos-latest uses arm64, macos-13 uses x86
15
+ os: [ubuntu-latest]
16
+ ruby: ['3.3', 'head']
17
+
18
+ name: ${{matrix.os}}, ${{matrix.ruby}}
19
+
20
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
21
+
22
+ runs-on: ${{matrix.os}}
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ with:
26
+ submodules: recursive
27
+
28
+ - uses: ruby/setup-ruby@v1
29
+ with:
30
+ ruby-version: ${{matrix.ruby}}
31
+ bundler-cache: true # 'bundle install' and cache
32
+ - name: Compile C-extension
33
+ run: bundle exec rake compile
34
+ - name: Run tests
35
+ run: bundle exec rake test
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # 2024-09-09 Version 0.2
2
+
3
+ - Add UTF8 encoding option for multishot read.
4
+ - Implement `OpCtx` as wrapper for op specs. This removes the need to call
5
+ `rb_hash_aref` upon completion (except for pulling the ctx from the
6
+ `pending_ops` hash), leading to a significant performance improvement.
7
+
8
+ # 2024-09-08 Version 0.1
9
+
10
+ - First working version.
data/README.md CHANGED
@@ -100,7 +100,7 @@ ring.wait_for_completion
100
100
 
101
101
  Examples for using IOU can be found in the examples directory:
102
102
 
103
- - [v] Echo server
104
- - [v] HTTP server
105
- - [v] Event loop (in the style of EventMachine)
106
- - [ ] Fiber-based concurrency
103
+ - Echo server
104
+ - HTTP server
105
+ - Event loop (in the style of EventMachine)
106
+ - Fiber-based concurrency
data/TODO.md CHANGED
@@ -1,4 +1,30 @@
1
- # io_uring ops
1
+ ## io_uring ops
2
2
 
3
3
  - [ ] recv
4
4
  - [ ] send
5
+ - [ ] recvmsg
6
+ - [ ] sendmsg
7
+ - [ ] multishot recv
8
+ - [ ] multishot recvmsg
9
+ - [ ] poll
10
+ - [ ] multishot poll
11
+ - [ ] shutdown
12
+ - [ ] connect
13
+ - [ ] socket
14
+ - [ ] openat
15
+ - [ ] splice
16
+ - [ ] wait
17
+
18
+ - [ ] support for linking requests
19
+
20
+ ```ruby
21
+ ring.prep_write(fd: fd, buffer: 'foo', link: true)
22
+ ring.prep_slice(fd: fd, src: src_fd, len: 4096)
23
+ ```
24
+
25
+ - [ ] link timeout
26
+
27
+ ```ruby
28
+ # read or timeout in 3 seconds
29
+ ring.prep_read(fd: fd, buffer: +'', len: 4096, timeout: 3)
30
+ ```
@@ -66,4 +66,4 @@ event_loop.run do
66
66
  end
67
67
  end
68
68
 
69
- puts "Stopped"
69
+ puts "Stopped"
@@ -0,0 +1,105 @@
1
+ require_relative '../lib/iou'
2
+ require 'socket'
3
+ require 'fiber'
4
+
5
+ class ::Fiber
6
+ attr_accessor :__op_id
7
+ end
8
+
9
+ class Scheduler
10
+ class Cancel < Exception
11
+ end
12
+
13
+ attr_reader :ring
14
+
15
+ def initialize
16
+ @ring = IOU::Ring.new
17
+ @runqueue = []
18
+ end
19
+
20
+ def switchpoint
21
+ while true
22
+ f, v = @runqueue.shift
23
+ if f
24
+ return f.transfer(v)
25
+ end
26
+
27
+ @ring.process_completions
28
+ end
29
+ end
30
+
31
+ def fiber_wait(op_id)
32
+ Fiber.current.__op_id = op_id
33
+ v = switchpoint
34
+ Fiber.current.__op_id = nil
35
+ raise v if v.is_a?(Exception)
36
+
37
+ v
38
+ end
39
+
40
+ def read(**args)
41
+ f = Fiber.current
42
+ id = ring.prep_read(**args) do |c|
43
+ if c[:result] < 0
44
+ @runqueue << [f, RuntimeError.new('error')]
45
+ else
46
+ @runqueue << [f, c[:buffer]]
47
+ end
48
+ end
49
+ fiber_wait(id)
50
+ end
51
+
52
+ def write(**args)
53
+ f = Fiber.current
54
+ id = ring.prep_write(**args) do |c|
55
+ if c[:result] < 0
56
+ @runqueue << [f, RuntimeError.new('error')]
57
+ else
58
+ @runqueue << [f, c[:result]]
59
+ end
60
+ end
61
+ fiber_wait(id)
62
+ end
63
+
64
+ def sleep(interval)
65
+ f = Fiber.current
66
+ id = ring.prep_timeout(interval: interval) do |c|
67
+ if c[:result] == Errno::ECANCELED::Errno
68
+ @runqueue << [f, c[:result]]
69
+ else
70
+ @runqueue << [f, c[:result]]
71
+ end
72
+ end
73
+ fiber_wait(id)
74
+ end
75
+
76
+ def cancel_fiber_op(f)
77
+ op_id = f.__op_id
78
+ if op_id
79
+ ring.prep_cancel(op_id)
80
+ end
81
+ end
82
+
83
+ def move_on_after(interval)
84
+ f = Fiber.current
85
+ cancel_id = ring.prep_timeout(interval: interval) do |c|
86
+ if c[:result] != Errno::ECANCELED::Errno
87
+ cancel_fiber_op(f)
88
+ end
89
+ end
90
+ v = yield
91
+ ring.prep_cancel(cancel_id)
92
+ v
93
+ end
94
+ end
95
+
96
+ s = Scheduler.new
97
+
98
+ puts "Going to sleep..."
99
+ s.sleep 3
100
+ puts "Woke up"
101
+
102
+ s.move_on_after(1) do
103
+ puts "Going to sleep (move on after 1 second)"
104
+ s.sleep 3
105
+ end
@@ -22,9 +22,7 @@ def setup_connection(fd)
22
22
 
23
23
  parser = Http::Parser.new
24
24
  parser.on_message_complete = -> {
25
- http_send_response(fd, "Hello, world!\n") do
26
- @ring.prep_close(fd: fd)
27
- end
25
+ http_send_response(fd, "Hello, world!\n")
28
26
  }
29
27
 
30
28
  http_prep_read(fd, parser)
@@ -0,0 +1,34 @@
1
+ require_relative '../lib/iou'
2
+ require 'socket'
3
+ require 'http/parser'
4
+
5
+ socket = TCPServer.open('127.0.0.1', 1234)
6
+ puts 'Listening on port 1234... (multishot read)'
7
+
8
+ @ring = IOU::Ring.new
9
+ @buffer_group = @ring.setup_buffer_ring(count: 1024, size: 4096)
10
+
11
+ @ring.prep_accept(fd: socket.fileno, multishot: true) do |c|
12
+ http_handle_connection(c[:result]) if c[:result] > 0
13
+ end
14
+
15
+ def http_handle_connection(fd)
16
+ parser = Http::Parser.new
17
+ parser.on_message_complete = -> { http_send_response(fd, "Hello, world!\n") }
18
+
19
+ @ring.prep_read(fd: fd, multishot: true, buffer_group: @buffer_group) do |c|
20
+ if c[:result] > 0
21
+ parser << c[:buffer]
22
+ else
23
+ puts "Connection closed on fd #{fd}"
24
+ end
25
+ end
26
+ end
27
+
28
+ def http_send_response(fd, body)
29
+ msg = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: keep-alive\r\nContent-Length: #{body.bytesize}\r\n\r\n#{body}"
30
+ @ring.prep_write(fd: fd, buffer: msg)
31
+ end
32
+
33
+ trap('SIGINT') { exit! }
34
+ @ring.process_completions_loop
data/ext/iou/iou.h CHANGED
@@ -48,19 +48,54 @@ struct sa_data {
48
48
  socklen_t len;
49
49
  };
50
50
 
51
- typedef struct OpSpecData_t {
51
+ struct read_data {
52
+ VALUE buffer;
53
+ int buffer_offset;
54
+ unsigned bg_id;
55
+ int utf8_encoding;
56
+ };
57
+
58
+ enum op_type {
59
+ OP_accept,
60
+ OP_cancel,
61
+ OP_close,
62
+ OP_emit,
63
+ OP_nop,
64
+ OP_read,
65
+ OP_timeout,
66
+ OP_write
67
+ };
68
+
69
+ typedef struct OpCtx_t {
70
+ enum op_type type;
71
+ VALUE spec;
72
+ VALUE proc;
52
73
  union {
53
74
  struct __kernel_timespec ts;
54
75
  struct sa_data sa;
76
+ struct read_data rd;
55
77
  } data;
56
- } OpSpecData_t;
78
+ int stop_signal;
79
+ } OpCtx_t;
57
80
 
58
81
  extern VALUE mIOU;
59
- extern VALUE cOpSpecData;
82
+ extern VALUE cOpCtx;
83
+
84
+ enum op_type OpCtx_type_get(VALUE self);
85
+ void OpCtx_type_set(VALUE self, enum op_type type);
86
+
87
+ VALUE OpCtx_spec_get(VALUE self);
88
+ VALUE OpCtx_proc_get(VALUE self);
89
+
90
+ struct __kernel_timespec *OpCtx_ts_get(VALUE self);
91
+ void OpCtx_ts_set(VALUE self, VALUE value);
92
+
93
+ struct sa_data *OpCtx_sa_get(VALUE self);
60
94
 
61
- struct __kernel_timespec *OpSpecData_ts_get(VALUE self);
62
- void OpSpecData_ts_set(VALUE self, VALUE value);
95
+ struct read_data *OpCtx_rd_get(VALUE self);
96
+ void OpCtx_rd_set(VALUE self, VALUE buffer, int buffer_offset, unsigned bg_id, int utf8_encoding);
63
97
 
64
- struct sa_data *OpSpecData_sa_get(VALUE self);
98
+ int OpCtx_stop_signal_p(VALUE self);
99
+ void OpCtx_stop_signal_set(VALUE self);
65
100
 
66
101
  #endif // IOU_H
data/ext/iou/iou_ext.c CHANGED
@@ -1,9 +1,9 @@
1
1
  #include "iou.h"
2
2
 
3
3
  void Init_IOU();
4
- void Init_OpSpecData();
4
+ void Init_OpCtx();
5
5
 
6
6
  void Init_iou_ext(void) {
7
7
  Init_IOU();
8
- Init_OpSpecData();
8
+ Init_OpCtx();
9
9
  }
data/ext/iou/op_ctx.c ADDED
@@ -0,0 +1,125 @@
1
+ #include "iou.h"
2
+
3
+ VALUE cOpCtx;
4
+
5
+ static void OpCtx_mark(void *ptr) {
6
+ OpCtx_t *ctx = ptr;
7
+ rb_gc_mark_movable(ctx->spec);
8
+ rb_gc_mark_movable(ctx->proc);
9
+ }
10
+
11
+ static void OpCtx_compact(void *ptr) {
12
+ OpCtx_t *ctx = ptr;
13
+ ctx->spec = rb_gc_location(ctx->spec);
14
+ ctx->proc = rb_gc_location(ctx->proc);
15
+ }
16
+
17
+ static size_t OpCtx_size(const void *ptr) {
18
+ return sizeof(OpCtx_t);
19
+ }
20
+
21
+ static const rb_data_type_t OpCtx_type = {
22
+ "OpCtx",
23
+ {OpCtx_mark, 0, OpCtx_size, OpCtx_compact},
24
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
25
+ };
26
+
27
+ static VALUE OpCtx_allocate(VALUE klass) {
28
+ OpCtx_t *osd = ALLOC(OpCtx_t);
29
+
30
+ return TypedData_Wrap_Struct(klass, &OpCtx_type, osd);
31
+ }
32
+
33
+ VALUE OpCtx_initialize(VALUE self, VALUE spec, VALUE proc) {
34
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
35
+ RB_OBJ_WRITE(self, &osd->spec, spec);
36
+ RB_OBJ_WRITE(self, &osd->proc, proc);
37
+ memset(&osd->data, 0, sizeof(osd->data));
38
+ osd->stop_signal = 0;
39
+ return self;
40
+ }
41
+
42
+ VALUE OpCtx_spec(VALUE self) {
43
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
44
+ return osd->spec;
45
+ }
46
+
47
+ inline enum op_type OpCtx_type_get(VALUE self) {
48
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
49
+ return osd->type;
50
+ }
51
+
52
+ inline void OpCtx_type_set(VALUE self, enum op_type type) {
53
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
54
+ osd->type = type;
55
+ }
56
+
57
+ inline VALUE OpCtx_spec_get(VALUE self) {
58
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
59
+ return osd->spec;
60
+ }
61
+
62
+ inline VALUE OpCtx_proc_get(VALUE self) {
63
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
64
+ return osd->proc;
65
+ }
66
+
67
+ struct __kernel_timespec *OpCtx_ts_get(VALUE self) {
68
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
69
+ return &osd->data.ts;
70
+ }
71
+
72
+ inline struct __kernel_timespec double_to_timespec(double value) {
73
+ double integral;
74
+ double fraction = modf(value, &integral);
75
+ struct __kernel_timespec ts;
76
+ ts.tv_sec = integral;
77
+ ts.tv_nsec = floor(fraction * 1000000000);
78
+ return ts;
79
+ }
80
+
81
+ inline struct __kernel_timespec value_to_timespec(VALUE value) {
82
+ return double_to_timespec(NUM2DBL(value));
83
+ }
84
+
85
+ inline void OpCtx_ts_set(VALUE self, VALUE value) {
86
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
87
+ osd->data.ts = value_to_timespec(value);
88
+ }
89
+
90
+ inline struct sa_data *OpCtx_sa_get(VALUE self) {
91
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
92
+ return &osd->data.sa;
93
+ }
94
+
95
+ inline struct read_data *OpCtx_rd_get(VALUE self) {
96
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
97
+ return &osd->data.rd;
98
+ }
99
+
100
+ inline void OpCtx_rd_set(VALUE self, VALUE buffer, int buffer_offset, unsigned bg_id, int utf8_encoding) {
101
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
102
+ osd->data.rd.buffer = buffer;
103
+ osd->data.rd.buffer_offset = buffer_offset;
104
+ osd->data.rd.bg_id = bg_id;
105
+ osd->data.rd.utf8_encoding = utf8_encoding;
106
+ }
107
+
108
+ inline int OpCtx_stop_signal_p(VALUE self) {
109
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
110
+ return osd->stop_signal;
111
+ }
112
+
113
+ inline void OpCtx_stop_signal_set(VALUE self) {
114
+ OpCtx_t *osd = RTYPEDDATA_DATA(self);
115
+ osd->stop_signal = 1;
116
+ }
117
+
118
+ void Init_OpCtx(void) {
119
+ mIOU = rb_define_module("IOU");
120
+ cOpCtx = rb_define_class_under(mIOU, "OpCtx", rb_cObject);
121
+ rb_define_alloc_func(cOpCtx, OpCtx_allocate);
122
+
123
+ rb_define_method(cOpCtx, "initialize", OpCtx_initialize, 2);
124
+ rb_define_method(cOpCtx, "spec", OpCtx_spec, 0);
125
+ }
@@ -220,22 +220,28 @@ VALUE IOU_setup_buffer_ring(VALUE self, VALUE opts) {
220
220
  return UINT2NUM(bg_id);
221
221
  }
222
222
 
223
- inline void store_spec(IOU_t *iou, VALUE spec, VALUE id, VALUE op) {
223
+ static inline VALUE setup_op_ctx(IOU_t *iou, enum op_type type, VALUE op, VALUE id, VALUE spec) {
224
224
  rb_hash_aset(spec, SYM_id, id);
225
225
  rb_hash_aset(spec, SYM_op, op);
226
- if (rb_block_given_p())
227
- rb_hash_aset(spec, SYM_block, rb_block_proc());
228
- rb_hash_aset(iou->pending_ops, id, spec);
226
+ VALUE block_proc = rb_block_given_p() ? rb_block_proc() : Qnil;
227
+ if (block_proc != Qnil)
228
+ rb_hash_aset(spec, SYM_block, block_proc);
229
+ VALUE ctx = rb_funcall(cOpCtx, rb_intern("new"), 2, spec, block_proc);
230
+ OpCtx_type_set(ctx, type);
231
+ rb_hash_aset(iou->pending_ops, id, ctx);
232
+ return ctx;
229
233
  }
230
234
 
231
- VALUE IOU_emit(VALUE self, VALUE obj) {
235
+ VALUE IOU_emit(VALUE self, VALUE spec) {
232
236
  IOU_t *iou = get_iou(self);
233
237
  unsigned id_i = ++iou->op_counter;
234
238
  VALUE id = UINT2NUM(id_i);
235
239
 
236
240
  struct io_uring_sqe *sqe = get_sqe(iou);
237
241
  sqe->user_data = id_i;
238
- store_spec(iou, obj, id, SYM_emit);
242
+ VALUE ctx = setup_op_ctx(iou, OP_emit, SYM_emit, id, spec);
243
+ if (rb_hash_aref(spec, SYM_signal) == SYM_stop)
244
+ OpCtx_stop_signal_set(ctx);
239
245
 
240
246
  io_uring_prep_nop(sqe);
241
247
 
@@ -256,13 +262,11 @@ VALUE IOU_prep_accept(VALUE self, VALUE spec) {
256
262
  VALUE fd = values[0];
257
263
  VALUE multishot = rb_hash_aref(spec, SYM_multishot);
258
264
 
259
- VALUE spec_data = rb_funcall(cOpSpecData, rb_intern("new"), 0);
260
265
  struct io_uring_sqe *sqe = get_sqe(iou);
261
266
  sqe->user_data = id_i;
262
- rb_hash_aset(spec, SYM_spec_data, spec_data);
263
- store_spec(iou, spec, id, SYM_accept);
264
267
 
265
- struct sa_data *sa = OpSpecData_sa_get(spec_data);
268
+ VALUE ctx = setup_op_ctx(iou, OP_accept, SYM_accept, id, spec);
269
+ struct sa_data *sa = OpCtx_sa_get(ctx);
266
270
  if (RTEST(multishot))
267
271
  io_uring_prep_multishot_accept(sqe, NUM2INT(fd), &sa->addr, &sa->len, 0);
268
272
  else
@@ -310,7 +314,8 @@ VALUE IOU_prep_close(VALUE self, VALUE spec) {
310
314
 
311
315
  struct io_uring_sqe *sqe = get_sqe(iou);
312
316
  sqe->user_data = id_i;
313
- store_spec(iou, spec, id, SYM_close);
317
+
318
+ setup_op_ctx(iou, OP_close, SYM_close, id, spec);
314
319
 
315
320
  io_uring_prep_close(sqe, NUM2INT(fd));
316
321
  iou->unsubmitted_sqes++;
@@ -358,10 +363,13 @@ VALUE prep_read_multishot(IOU_t *iou, VALUE spec) {
358
363
  get_required_kwargs(spec, values, 2, SYM_fd, SYM_buffer_group);
359
364
  int fd = NUM2INT(values[0]);
360
365
  unsigned bg_id = NUM2UINT(values[1]);
366
+ int utf8 = RTEST(rb_hash_aref(spec, SYM_utf8));
361
367
 
362
368
  struct io_uring_sqe *sqe = get_sqe(iou);
363
369
  sqe->user_data = id_i;
364
- store_spec(iou, spec, id, SYM_read);
370
+
371
+ VALUE ctx = setup_op_ctx(iou, OP_read, SYM_read, id, spec);
372
+ OpCtx_rd_set(ctx, Qnil, 0, bg_id, utf8);
365
373
 
366
374
  io_uring_prep_read_multishot(sqe, fd, 0, -1, bg_id);
367
375
  iou->unsubmitted_sqes++;
@@ -387,10 +395,13 @@ VALUE IOU_prep_read(VALUE self, VALUE spec) {
387
395
 
388
396
  VALUE buffer_offset = rb_hash_aref(spec, SYM_buffer_offset);
389
397
  int buffer_offset_i = NIL_P(buffer_offset) ? 0 : NUM2INT(buffer_offset);
398
+ int utf8 = RTEST(rb_hash_aref(spec, SYM_utf8));
390
399
 
391
400
  struct io_uring_sqe *sqe = get_sqe(iou);
392
401
  sqe->user_data = id_i;
393
- store_spec(iou, spec, id, SYM_read);
402
+
403
+ VALUE ctx = setup_op_ctx(iou, OP_read, SYM_read, id, spec);
404
+ OpCtx_rd_set(ctx, buffer, buffer_offset_i, 0, utf8);
394
405
 
395
406
  void *ptr = prepare_read_buffer(buffer, len_i, buffer_offset_i);
396
407
  io_uring_prep_read(sqe, NUM2INT(fd), ptr, len_i, -1);
@@ -409,15 +420,13 @@ VALUE IOU_prep_timeout(VALUE self, VALUE spec) {
409
420
  VALUE multishot = rb_hash_aref(spec, SYM_multishot);
410
421
  unsigned flags = RTEST(multishot) ? IORING_TIMEOUT_MULTISHOT : 0;
411
422
 
412
- VALUE spec_data = rb_funcall(cOpSpecData, rb_intern("new"), 0);
413
- OpSpecData_ts_set(spec_data, interval);
414
-
415
423
  struct io_uring_sqe *sqe = get_sqe(iou);
416
424
  sqe->user_data = id_i;
417
- rb_hash_aset(spec, SYM_spec_data, spec_data);
418
- store_spec(iou, spec, id, SYM_timeout);
419
425
 
420
- io_uring_prep_timeout(sqe, OpSpecData_ts_get(spec_data), 0, flags);
426
+ VALUE ctx = setup_op_ctx(iou, OP_timeout, SYM_timeout, id, spec);
427
+ OpCtx_ts_set(ctx, interval);
428
+
429
+ io_uring_prep_timeout(sqe, OpCtx_ts_get(ctx), 0, flags);
421
430
  iou->unsubmitted_sqes++;
422
431
  return id;
423
432
  }
@@ -436,7 +445,8 @@ VALUE IOU_prep_write(VALUE self, VALUE spec) {
436
445
 
437
446
  struct io_uring_sqe *sqe = get_sqe(iou);
438
447
  sqe->user_data = id_i;
439
- store_spec(iou, spec, id, SYM_write);
448
+
449
+ setup_op_ctx(iou, OP_write, SYM_write, id, spec);
440
450
 
441
451
  io_uring_prep_write(sqe, NUM2INT(fd), RSTRING_PTR(buffer), nbytes, -1);
442
452
  iou->unsubmitted_sqes++;
@@ -476,106 +486,112 @@ void *wait_for_completion_without_gvl(void *ptr) {
476
486
  return NULL;
477
487
  }
478
488
 
479
- static inline void update_read_buffer_from_buffer_ring(IOU_t *iou, VALUE spec, struct io_uring_cqe *cqe) {
489
+ static inline void update_read_buffer_from_buffer_ring(IOU_t *iou, VALUE ctx, struct io_uring_cqe *cqe) {
480
490
  VALUE buf = Qnil;
481
491
  if (cqe->res == 0) {
482
492
  buf = rb_str_new_literal("");
483
493
  goto done;
484
494
  }
485
495
 
486
- unsigned bg_id = NUM2UINT(rb_hash_aref(spec, SYM_buffer_group));
496
+ struct read_data *rd = OpCtx_rd_get(ctx);
487
497
  unsigned buf_idx = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
488
498
 
489
- struct buf_ring_descriptor *desc = iou->brs + bg_id;
499
+ struct buf_ring_descriptor *desc = iou->brs + rd->bg_id;
490
500
  char *src = desc->buf_base + desc->buf_size * buf_idx;
491
- buf = rb_str_new(src, cqe->res);
501
+ buf = rd->utf8_encoding ? rb_utf8_str_new(src, cqe->res) : rb_str_new(src, cqe->res);
492
502
 
493
- // release buffer back to io_uring
503
+ // add buffer back to buffer ring
494
504
  io_uring_buf_ring_add(
495
505
  desc->br, src, desc->buf_size, buf_idx,
496
506
  io_uring_buf_ring_mask(desc->buf_count), 0
497
507
  );
498
508
  io_uring_buf_ring_advance(desc->br, 1);
499
509
  done:
500
- rb_hash_aset(spec, SYM_buffer, buf);
510
+ rb_hash_aset(OpCtx_spec_get(ctx), SYM_buffer, buf);
501
511
  RB_GC_GUARD(buf);
502
512
  return;
503
513
  }
504
514
 
505
- static inline void update_read_buffer(IOU_t *iou, VALUE spec, struct io_uring_cqe *cqe) {
515
+ static inline void update_read_buffer(IOU_t *iou, VALUE ctx, struct io_uring_cqe *cqe) {
506
516
  if (cqe->res < 0) return;
507
517
 
508
518
  if (cqe->flags & IORING_CQE_F_BUFFER) {
509
- update_read_buffer_from_buffer_ring(iou, spec, cqe);
519
+ update_read_buffer_from_buffer_ring(iou, ctx, cqe);
510
520
  return;
511
521
  }
512
522
 
513
523
  if (cqe->res == 0) return;
514
524
 
515
- VALUE buffer = rb_hash_aref(spec, SYM_buffer);
516
- VALUE buffer_offset = rb_hash_aref(spec, SYM_buffer_offset);
517
- int buffer_offset_i = NIL_P(buffer_offset) ? 0 : NUM2INT(buffer_offset);
518
- adjust_read_buffer_len(buffer, cqe->res, buffer_offset_i);
519
- }
520
-
521
- inline int is_stop_signal(VALUE op, VALUE spec) {
522
- return (op == SYM_emit) && (rb_hash_aref(spec, SYM_signal) == SYM_stop);
525
+ struct read_data *rd = OpCtx_rd_get(ctx);
526
+ adjust_read_buffer_len(rd->buffer, cqe->res, rd->buffer_offset);
523
527
  }
524
528
 
525
- static inline VALUE get_cqe_op_spec(IOU_t *iou, struct io_uring_cqe *cqe, int *stop_flag) {
529
+ static inline VALUE get_cqe_ctx(IOU_t *iou, struct io_uring_cqe *cqe, int *stop_flag, VALUE *spec) {
526
530
  VALUE id = UINT2NUM(cqe->user_data);
527
- VALUE spec = rb_hash_aref(iou->pending_ops, id);
531
+ VALUE ctx = rb_hash_aref(iou->pending_ops, id);
528
532
  VALUE result = INT2NUM(cqe->res);
529
- if (NIL_P(spec))
530
- return make_empty_op_with_result(id, result);
533
+ if (NIL_P(ctx)) {
534
+ *spec = make_empty_op_with_result(id, result);
535
+ return Qnil;
536
+ }
531
537
 
532
538
  // post completion work
533
- VALUE op = rb_hash_aref(spec, SYM_op);
534
- if (op == SYM_read)
535
- update_read_buffer(iou, spec, cqe);
536
- else if (stop_flag && is_stop_signal(op, spec))
537
- *stop_flag = 1;
539
+ switch (OpCtx_type_get(ctx)) {
540
+ case OP_read:
541
+ update_read_buffer(iou, ctx, cqe);
542
+ break;
543
+ case OP_emit:
544
+ if (stop_flag && OpCtx_stop_signal_p(ctx))
545
+ *stop_flag = 1;
546
+ break;
547
+ default:
548
+ }
538
549
 
539
550
  // for multishot ops, the IORING_CQE_F_MORE flag indicates more completions
540
551
  // will be coming, so we need to keep the spec. Otherwise, we remove it.
541
552
  if (!(cqe->flags & IORING_CQE_F_MORE))
542
553
  rb_hash_delete(iou->pending_ops, id);
543
554
 
544
- rb_hash_aset(spec, SYM_result, result);
545
- RB_GC_GUARD(spec);
546
- return spec;
555
+ *spec = OpCtx_spec_get(ctx);
556
+ rb_hash_aset(*spec, SYM_result, result);
557
+ RB_GC_GUARD(ctx);
558
+ return ctx;
547
559
  }
548
560
 
549
561
  VALUE IOU_wait_for_completion(VALUE self) {
550
562
  IOU_t *iou = get_iou(self);
551
563
 
552
- wait_for_completion_ctx_t ctx = {
564
+ wait_for_completion_ctx_t cqe_ctx = {
553
565
  .iou = iou
554
566
  };
555
567
 
556
- rb_thread_call_without_gvl(wait_for_completion_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
568
+ rb_thread_call_without_gvl(wait_for_completion_without_gvl, (void *)&cqe_ctx, RUBY_UBF_IO, 0);
557
569
 
558
- if (unlikely(ctx.ret < 0)) {
559
- rb_syserr_fail(-ctx.ret, strerror(-ctx.ret));
570
+ if (unlikely(cqe_ctx.ret < 0)) {
571
+ rb_syserr_fail(-cqe_ctx.ret, strerror(-cqe_ctx.ret));
560
572
  }
561
- io_uring_cqe_seen(&iou->ring, ctx.cqe);
562
- return get_cqe_op_spec(iou, ctx.cqe, 0);
573
+ io_uring_cqe_seen(&iou->ring, cqe_ctx.cqe);
574
+
575
+ VALUE spec = Qnil;
576
+ get_cqe_ctx(iou, cqe_ctx.cqe, 0, &spec);
577
+ return spec;
563
578
  }
564
579
 
565
- static inline void process_cqe(IOU_t *iou, struct io_uring_cqe *cqe, int *stop_flag) {
580
+ static inline void process_cqe(IOU_t *iou, struct io_uring_cqe *cqe, int block_given, int *stop_flag) {
566
581
  if (stop_flag) *stop_flag = 0;
567
- VALUE spec = get_cqe_op_spec(iou, cqe, stop_flag);
582
+ VALUE spec;
583
+ VALUE ctx = get_cqe_ctx(iou, cqe, stop_flag, &spec);
568
584
  if (stop_flag && *stop_flag) return;
569
585
 
570
- if (rb_block_given_p())
586
+ if (block_given)
571
587
  rb_yield(spec);
572
- else {
573
- VALUE block = rb_hash_aref(spec, SYM_block);
574
- if (RTEST(block))
575
- rb_proc_call_with_block_kw(block, 1, &spec, Qnil, Qnil);
588
+ else if (ctx != Qnil) {
589
+ VALUE proc = OpCtx_proc_get(ctx);
590
+ if (RTEST(proc))
591
+ rb_proc_call_with_block_kw(proc, 1, &spec, Qnil, Qnil);
576
592
  }
577
593
 
578
- RB_GC_GUARD(spec);
594
+ RB_GC_GUARD(ctx);
579
595
  }
580
596
 
581
597
  // copied from liburing/queue.c
@@ -585,7 +601,7 @@ static inline bool cq_ring_needs_flush(struct io_uring *ring) {
585
601
 
586
602
  // adapted from io_uring_peek_batch_cqe in liburing/queue.c
587
603
  // this peeks at cqes and handles each available cqe
588
- static inline int process_ready_cqes(IOU_t *iou, int *stop_flag) {
604
+ static inline int process_ready_cqes(IOU_t *iou, int block_given, int *stop_flag) {
589
605
  unsigned total_count = 0;
590
606
 
591
607
  iterate:
@@ -596,7 +612,7 @@ iterate:
596
612
  io_uring_for_each_cqe(&iou->ring, head, cqe) {
597
613
  ++count;
598
614
  if (stop_flag) *stop_flag = 0;
599
- process_cqe(iou, cqe, stop_flag);
615
+ process_cqe(iou, cqe, block_given, stop_flag);
600
616
  if (stop_flag && *stop_flag)
601
617
  break;
602
618
  }
@@ -618,6 +634,7 @@ done:
618
634
 
619
635
  VALUE IOU_process_completions(int argc, VALUE *argv, VALUE self) {
620
636
  IOU_t *iou = get_iou(self);
637
+ int block_given = rb_block_given_p();
621
638
  VALUE wait;
622
639
 
623
640
  rb_scan_args(argc, argv, "01", &wait);
@@ -639,15 +656,16 @@ VALUE IOU_process_completions(int argc, VALUE *argv, VALUE self) {
639
656
  }
640
657
  ++count;
641
658
  io_uring_cqe_seen(&iou->ring, ctx.cqe);
642
- process_cqe(iou, ctx.cqe, 0);
659
+ process_cqe(iou, ctx.cqe, block_given, 0);
643
660
  }
644
661
 
645
- count += process_ready_cqes(iou, 0);
662
+ count += process_ready_cqes(iou, block_given, 0);
646
663
  return UINT2NUM(count);
647
664
  }
648
665
 
649
666
  VALUE IOU_process_completions_loop(VALUE self) {
650
667
  IOU_t *iou = get_iou(self);
668
+ int block_given = rb_block_given_p();
651
669
  int stop_flag = 0;
652
670
  wait_for_completion_ctx_t ctx = { .iou = iou };
653
671
 
@@ -663,14 +681,14 @@ VALUE IOU_process_completions_loop(VALUE self) {
663
681
  rb_syserr_fail(-ctx.ret, strerror(-ctx.ret));
664
682
  }
665
683
  io_uring_cqe_seen(&iou->ring, ctx.cqe);
666
- process_cqe(iou, ctx.cqe, &stop_flag);
684
+ process_cqe(iou, ctx.cqe, block_given, &stop_flag);
667
685
  if (stop_flag) goto done;
668
686
 
669
- process_ready_cqes(iou, &stop_flag);
687
+ process_ready_cqes(iou, block_given, &stop_flag);
670
688
  if (stop_flag) goto done;
671
689
  }
672
690
  done:
673
- return self;
691
+ return self;
674
692
  }
675
693
 
676
694
  #define MAKE_SYM(sym) ID2SYM(rb_intern(sym))
data/lib/iou/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IOU
4
- VERSION = '0.1'
4
+ VERSION = '0.2'
5
5
  end
data/test/test_iou.rb CHANGED
@@ -16,7 +16,7 @@ class IOURingTest < IOURingBaseTest
16
16
  assert_equal({}, ring.pending_ops)
17
17
 
18
18
  id = ring.prep_timeout(interval: 1)
19
- spec = ring.pending_ops[id]
19
+ spec = ring.pending_ops[id].spec
20
20
  assert_equal id, spec[:id]
21
21
  assert_equal :timeout, spec[:op]
22
22
  assert_equal 1, spec[:interval]
@@ -46,7 +46,7 @@ class PrepTimeoutTest < IOURingBaseTest
46
46
  assert_equal id, c[:id]
47
47
  assert_equal :timeout, c[:op]
48
48
  assert_equal interval, c[:interval]
49
- assert_equal -Errno::ETIME::Errno, c[:result]
49
+ assert_equal (-Errno::ETIME::Errno), c[:result]
50
50
  end
51
51
 
52
52
  def test_prep_timeout_invalid_args
@@ -74,7 +74,7 @@ class PrepCancelTest < IOURingBaseTest
74
74
  assert_equal timeout_id, c[:id]
75
75
  assert_equal :timeout, c[:op]
76
76
  assert_equal interval, c[:interval]
77
- assert_equal -Errno::ECANCELED::Errno, c[:result]
77
+ assert_equal (-Errno::ECANCELED::Errno), c[:result]
78
78
  end
79
79
 
80
80
  def test_prep_cancel_kw
@@ -94,7 +94,7 @@ class PrepCancelTest < IOURingBaseTest
94
94
  assert_equal timeout_id, c[:id]
95
95
  assert_equal :timeout, c[:op]
96
96
  assert_equal interval, c[:interval]
97
- assert_equal -Errno::ECANCELED::Errno, c[:result]
97
+ assert_equal (-Errno::ECANCELED::Errno), c[:result]
98
98
  end
99
99
 
100
100
  def test_prep_cancel_invalid_args
@@ -111,7 +111,7 @@ class PrepCancelTest < IOURingBaseTest
111
111
  ring.submit
112
112
  c = ring.wait_for_completion
113
113
  assert_equal cancel_id, c[:id]
114
- assert_equal -Errno::ENOENT::Errno, c[:result]
114
+ assert_equal (-Errno::ENOENT::Errno), c[:result]
115
115
  end
116
116
  end
117
117
 
@@ -124,9 +124,9 @@ class PrepTimeoutMultishotTest < IOURingBaseTest
124
124
  t0 = monotonic_clock
125
125
  id = ring.prep_timeout(interval: interval, multishot: true) do |c|
126
126
  case c[:result]
127
- when -Errno::ETIME::Errno
127
+ when (-Errno::ETIME::Errno)
128
128
  count += 1
129
- when -Errno::ECANCELED::Errno
129
+ when (-Errno::ECANCELED::Errno)
130
130
  cancelled = true
131
131
  end
132
132
  end
@@ -151,7 +151,7 @@ class PrepTimeoutMultishotTest < IOURingBaseTest
151
151
 
152
152
  ring.prep_cancel(id)
153
153
  ring.submit
154
- c = ring.process_completions(true)
154
+ ring.process_completions(true)
155
155
  assert_equal true, cancelled
156
156
  assert_equal 3, count
157
157
  assert_nil ring.pending_ops[id]
@@ -207,7 +207,7 @@ class PrepWriteTest < IOURingBaseTest
207
207
  end
208
208
 
209
209
  def test_prep_write_invalid_fd
210
- r, w = IO.pipe
210
+ r, _w = IO.pipe
211
211
  s = 'foobar'
212
212
 
213
213
  id = ring.prep_write(fd: r.fileno, buffer: s)
@@ -220,7 +220,7 @@ class PrepWriteTest < IOURingBaseTest
220
220
  assert_equal id, c[:id]
221
221
  assert_equal :write, c[:op]
222
222
  assert_equal r.fileno, c[:fd]
223
- assert_equal -Errno::EBADF::Errno, c[:result]
223
+ assert_equal (-Errno::EBADF::Errno), c[:result]
224
224
  end
225
225
  end
226
226
 
@@ -266,7 +266,8 @@ class PrepNopTest < IOURingBaseTest
266
266
  assert_nil c[:op]
267
267
  assert_equal 0, c[:result]
268
268
  ensure
269
- signaller.kill rescue nil
269
+ signaller&.kill rescue nil
270
+ waiter&.kill rescue nil
270
271
  end
271
272
  end
272
273
 
@@ -301,9 +302,9 @@ class ProcessCompletionsTest < IOURingBaseTest
301
302
  def test_process_completions_with_block
302
303
  r, w = IO.pipe
303
304
 
304
- id1 = ring.prep_write(fd: w.fileno, buffer: 'foo')
305
- id2 = ring.prep_write(fd: w.fileno, buffer: 'bar')
306
- id3 = ring.prep_write(fd: w.fileno, buffer: 'baz')
305
+ ring.prep_write(fd: w.fileno, buffer: 'foo')
306
+ ring.prep_write(fd: w.fileno, buffer: 'bar')
307
+ ring.prep_write(fd: w.fileno, buffer: 'baz')
307
308
  ring.submit
308
309
  sleep 0.01
309
310
 
@@ -326,8 +327,8 @@ class ProcessCompletionsTest < IOURingBaseTest
326
327
  def test_process_completions_op_with_block
327
328
  cc = []
328
329
 
329
- id1 = ring.prep_timeout(interval: 0.01) { cc << 1 }
330
- id2 = ring.prep_timeout(interval: 0.02) { cc << 2 }
330
+ ring.prep_timeout(interval: 0.01) { cc << 1 }
331
+ ring.prep_timeout(interval: 0.02) { cc << 2 }
331
332
  ring.submit
332
333
 
333
334
  ret = ring.process_completions
@@ -344,8 +345,8 @@ class ProcessCompletionsTest < IOURingBaseTest
344
345
  def test_process_completions_op_with_block_no_submit
345
346
  cc = []
346
347
 
347
- id1 = ring.prep_timeout(interval: 0.01) { cc << 1 }
348
- id2 = ring.prep_timeout(interval: 0.02) { cc << 2 }
348
+ ring.prep_timeout(interval: 0.01) { cc << 1 }
349
+ ring.prep_timeout(interval: 0.02) { cc << 2 }
349
350
 
350
351
  ret = ring.process_completions
351
352
  assert_equal 0, ret
@@ -406,7 +407,7 @@ class PrepReadTest < IOURingBaseTest
406
407
  end
407
408
 
408
409
  def test_prep_read_bad_fd
409
- r, w = IO.pipe
410
+ _r, w = IO.pipe
410
411
 
411
412
  id = ring.prep_read(fd: w.fileno, buffer: +'', len: 8192)
412
413
  assert_equal 1, id
@@ -418,7 +419,7 @@ class PrepReadTest < IOURingBaseTest
418
419
  assert_equal id, c[:id]
419
420
  assert_equal :read, c[:op]
420
421
  assert_equal w.fileno, c[:fd]
421
- assert_equal -Errno::EBADF::Errno, c[:result]
422
+ assert_equal (-Errno::EBADF::Errno), c[:result]
422
423
  end
423
424
 
424
425
  def test_prep_read_with_block
@@ -513,7 +514,7 @@ end
513
514
 
514
515
  class PrepCloseTest < IOURingBaseTest
515
516
  def test_prep_close
516
- r, w = IO.pipe
517
+ _r, w = IO.pipe
517
518
  fd = w.fileno
518
519
 
519
520
  id = ring.prep_close(fd: fd)
@@ -538,7 +539,7 @@ class PrepCloseTest < IOURingBaseTest
538
539
  assert_equal id, c[:id]
539
540
  assert_equal :close, c[:op]
540
541
  assert_equal fd, c[:fd]
541
- assert_equal -Errno::EBADF::Errno, c[:result]
542
+ assert_equal (-Errno::EBADF::Errno), c[:result]
542
543
 
543
544
  end
544
545
 
@@ -560,7 +561,7 @@ class PrepCloseTest < IOURingBaseTest
560
561
  assert_equal id, c[:id]
561
562
  assert_equal :close, c[:op]
562
563
  assert_equal 9999, c[:fd]
563
- assert_equal -Errno::EBADF::Errno, c[:result]
564
+ assert_equal (-Errno::EBADF::Errno), c[:result]
564
565
  end
565
566
  end
566
567
 
@@ -581,7 +582,7 @@ class PrepAcceptTest < IOURingBaseTest
581
582
  ring.submit
582
583
 
583
584
  t = Thread.new do
584
- client = TCPSocket.new('127.0.0.1', @port)
585
+ TCPSocket.new('127.0.0.1', @port)
585
586
  end
586
587
 
587
588
  c = ring.wait_for_completion
@@ -611,7 +612,7 @@ class PrepAcceptTest < IOURingBaseTest
611
612
  assert_equal id, c[:id]
612
613
  assert_equal :accept, c[:op]
613
614
  assert_equal STDIN.fileno, c[:fd]
614
- assert_equal -Errno::ENOTSOCK::Errno, c[:result]
615
+ assert_equal (-Errno::ENOTSOCK::Errno), c[:result]
615
616
  end
616
617
 
617
618
  def test_prep_accept_multishot
@@ -622,7 +623,7 @@ class PrepAcceptTest < IOURingBaseTest
622
623
 
623
624
  connect = -> {
624
625
  tt << Thread.new do
625
- client = TCPSocket.new('127.0.0.1', @port)
626
+ TCPSocket.new('127.0.0.1', @port)
626
627
  end
627
628
  }
628
629
 
@@ -707,7 +708,6 @@ class PrepReadMultishotTest < IOURingBaseTest
707
708
  def test_prep_read_multishot
708
709
  r, w = IO.pipe
709
710
 
710
- bb = []
711
711
  bgid = ring.setup_buffer_ring(size: 4096, count: 1024)
712
712
  assert_equal 0, bgid
713
713
 
@@ -717,6 +717,10 @@ class PrepReadMultishotTest < IOURingBaseTest
717
717
 
718
718
  w << 'foo'
719
719
  c = ring.wait_for_completion
720
+
721
+ # make sure the OS supports this op (the liburing docs are not clear)
722
+ skip if c[:result] == (-Errno::EINVAL::Errno)
723
+
720
724
  assert_kind_of Hash, c
721
725
  assert_equal id, c[:id]
722
726
  assert_equal :read, c[:op]
@@ -747,13 +751,8 @@ class PrepReadMultishotTest < IOURingBaseTest
747
751
  end
748
752
 
749
753
  def test_prep_read_multishot_utf8
750
- # checking for UTF-8 incurs a serious performance degradation. We'll leave
751
- # it for later...
752
- skip
753
-
754
754
  r, w = IO.pipe
755
755
 
756
- bb = []
757
756
  bgid = ring.setup_buffer_ring(size: 4096, count: 1024)
758
757
  assert_equal 0, bgid
759
758
 
@@ -763,6 +762,10 @@ class PrepReadMultishotTest < IOURingBaseTest
763
762
 
764
763
  w << 'foo'
765
764
  c = ring.wait_for_completion
765
+
766
+ # make sure the OS supports this op (the liburing docs are not clear)
767
+ skip if c[:result] == (-Errno::EINVAL::Errno)
768
+
766
769
  assert_kind_of Hash, c
767
770
  assert_equal id, c[:id]
768
771
  assert_equal :read, c[:op]
@@ -792,3 +795,36 @@ class PrepReadMultishotTest < IOURingBaseTest
792
795
  assert_nil ring.pending_ops[id]
793
796
  end
794
797
  end
798
+
799
+ class OpCtxTest < IOURingBaseTest
800
+ def test_ctx_spec
801
+ id = ring.emit(foo: :bar)
802
+ assert_equal({ foo: :bar, id: 1, op: :emit }, ring.pending_ops[id].spec)
803
+ end
804
+
805
+ def test_ctx_type
806
+ id = ring.emit(v: 1)
807
+ assert_equal 1, id
808
+ assert_equal :emit, ring.pending_ops[id].spec[:op]
809
+
810
+ id = ring.prep_timeout(interval: 1)
811
+ assert_equal 2, id
812
+ assert_equal :timeout, ring.pending_ops[id].spec[:op]
813
+
814
+ id = ring.prep_read(fd: STDIN.fileno, buffer: +'', len: 42)
815
+ assert_equal 3, id
816
+ assert_equal :read, ring.pending_ops[id].spec[:op]
817
+
818
+ id = ring.prep_write(fd: STDOUT.fileno, buffer: '')
819
+ assert_equal 4, id
820
+ assert_equal :write, ring.pending_ops[id].spec[:op]
821
+
822
+ id = ring.prep_accept(fd: STDIN.fileno)
823
+ assert_equal 5, id
824
+ assert_equal :accept, ring.pending_ops[id].spec[:op]
825
+
826
+ id = ring.prep_close(fd: STDIN.fileno)
827
+ assert_equal 6, id
828
+ assert_equal :close, ring.pending_ops[id].spec[:op]
829
+ end
830
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iou
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-07 00:00:00.000000000 Z
11
+ date: 2024-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -75,8 +75,10 @@ extra_rdoc_files:
75
75
  - README.md
76
76
  files:
77
77
  - ".github/dependabot.yml"
78
+ - ".github/workflows/test.yml"
78
79
  - ".gitignore"
79
80
  - ".gitmodules"
81
+ - CHANGELOG.md
80
82
  - Gemfile
81
83
  - LICENSE
82
84
  - README.md
@@ -84,13 +86,15 @@ files:
84
86
  - TODO.md
85
87
  - examples/echo_server.rb
86
88
  - examples/event_loop.rb
89
+ - examples/fibers.rb
87
90
  - examples/http_server.rb
88
91
  - examples/http_server_multishot.rb
92
+ - examples/http_server_simpler.rb
89
93
  - ext/iou/extconf.rb
90
- - ext/iou/iou.c
91
94
  - ext/iou/iou.h
92
95
  - ext/iou/iou_ext.c
93
- - ext/iou/op_spec_data.c
96
+ - ext/iou/op_ctx.c
97
+ - ext/iou/ring.c
94
98
  - iou.gemspec
95
99
  - lib/iou.rb
96
100
  - lib/iou/version.rb
@@ -1,61 +0,0 @@
1
- #include "iou.h"
2
-
3
- VALUE cOpSpecData;
4
-
5
- static size_t OpSpecData_size(const void *ptr) {
6
- return sizeof(OpSpecData_t);
7
- }
8
-
9
- static const rb_data_type_t OpSpecData_type = {
10
- "OpSpecData",
11
- {0, 0, OpSpecData_size, 0},
12
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
13
- };
14
-
15
- static VALUE OpSpecData_allocate(VALUE klass) {
16
- OpSpecData_t *osd = ALLOC(OpSpecData_t);
17
-
18
- return TypedData_Wrap_Struct(klass, &OpSpecData_type, osd);
19
- }
20
-
21
- VALUE OpSpecData_initialize(VALUE self) {
22
- OpSpecData_t *osd = RTYPEDDATA_DATA(self);
23
- memset(&osd->data, 0, sizeof(osd->data));
24
- return self;
25
- }
26
-
27
- struct __kernel_timespec *OpSpecData_ts_get(VALUE self) {
28
- OpSpecData_t *osd = RTYPEDDATA_DATA(self);
29
- return &osd->data.ts;
30
- }
31
-
32
- inline struct __kernel_timespec double_to_timespec(double value) {
33
- double integral;
34
- double fraction = modf(value, &integral);
35
- struct __kernel_timespec ts;
36
- ts.tv_sec = integral;
37
- ts.tv_nsec = floor(fraction * 1000000000);
38
- return ts;
39
- }
40
-
41
- inline struct __kernel_timespec value_to_timespec(VALUE value) {
42
- return double_to_timespec(NUM2DBL(value));
43
- }
44
-
45
- void OpSpecData_ts_set(VALUE self, VALUE value) {
46
- OpSpecData_t *osd = RTYPEDDATA_DATA(self);
47
- osd->data.ts = value_to_timespec(value);
48
- }
49
-
50
- struct sa_data *OpSpecData_sa_get(VALUE self) {
51
- OpSpecData_t *osd = RTYPEDDATA_DATA(self);
52
- return &osd->data.sa;
53
- }
54
-
55
- void Init_OpSpecData(void) {
56
- mIOU = rb_define_module("IOU");
57
- cOpSpecData = rb_define_class_under(mIOU, "OpSpecData", rb_cObject);
58
- rb_define_alloc_func(cOpSpecData, OpSpecData_allocate);
59
-
60
- rb_define_method(cOpSpecData, "initialize", OpSpecData_initialize, 0);
61
- }