polyphony 0.45.2 → 0.47.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -0
  3. data/.gitmodules +0 -0
  4. data/CHANGELOG.md +39 -0
  5. data/Gemfile.lock +3 -3
  6. data/README.md +3 -3
  7. data/Rakefile +1 -1
  8. data/TODO.md +20 -28
  9. data/bin/test +4 -0
  10. data/examples/core/enumerable.rb +64 -0
  11. data/examples/io/raw.rb +14 -0
  12. data/examples/io/reline.rb +18 -0
  13. data/examples/performance/fiber_resume.rb +43 -0
  14. data/examples/performance/fiber_transfer.rb +13 -4
  15. data/examples/performance/multi_snooze.rb +0 -1
  16. data/examples/performance/thread-vs-fiber/compare.rb +59 -0
  17. data/examples/performance/thread-vs-fiber/em_server.rb +33 -0
  18. data/examples/performance/thread-vs-fiber/polyphony_server.rb +10 -21
  19. data/examples/performance/thread-vs-fiber/threaded_server.rb +22 -15
  20. data/examples/performance/thread_switch.rb +44 -0
  21. data/ext/liburing/liburing.h +585 -0
  22. data/ext/liburing/liburing/README.md +4 -0
  23. data/ext/liburing/liburing/barrier.h +73 -0
  24. data/ext/liburing/liburing/compat.h +15 -0
  25. data/ext/liburing/liburing/io_uring.h +343 -0
  26. data/ext/liburing/queue.c +333 -0
  27. data/ext/liburing/register.c +187 -0
  28. data/ext/liburing/setup.c +210 -0
  29. data/ext/liburing/syscall.c +54 -0
  30. data/ext/liburing/syscall.h +18 -0
  31. data/ext/polyphony/backend.h +1 -15
  32. data/ext/polyphony/backend_common.h +129 -0
  33. data/ext/polyphony/backend_io_uring.c +995 -0
  34. data/ext/polyphony/backend_io_uring_context.c +74 -0
  35. data/ext/polyphony/backend_io_uring_context.h +53 -0
  36. data/ext/polyphony/{libev_backend.c → backend_libev.c} +308 -297
  37. data/ext/polyphony/event.c +1 -1
  38. data/ext/polyphony/extconf.rb +31 -13
  39. data/ext/polyphony/fiber.c +60 -32
  40. data/ext/polyphony/libev.c +4 -0
  41. data/ext/polyphony/libev.h +8 -2
  42. data/ext/polyphony/liburing.c +8 -0
  43. data/ext/polyphony/playground.c +51 -0
  44. data/ext/polyphony/polyphony.c +9 -6
  45. data/ext/polyphony/polyphony.h +35 -19
  46. data/ext/polyphony/polyphony_ext.c +12 -4
  47. data/ext/polyphony/queue.c +100 -35
  48. data/ext/polyphony/runqueue.c +102 -0
  49. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  50. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  51. data/ext/polyphony/thread.c +42 -90
  52. data/lib/polyphony.rb +2 -2
  53. data/lib/polyphony/adapters/process.rb +0 -3
  54. data/lib/polyphony/adapters/trace.rb +2 -2
  55. data/lib/polyphony/core/exceptions.rb +0 -4
  56. data/lib/polyphony/core/global_api.rb +47 -23
  57. data/lib/polyphony/core/sync.rb +7 -5
  58. data/lib/polyphony/extensions/core.rb +14 -33
  59. data/lib/polyphony/extensions/debug.rb +13 -0
  60. data/lib/polyphony/extensions/fiber.rb +21 -3
  61. data/lib/polyphony/extensions/io.rb +15 -4
  62. data/lib/polyphony/extensions/openssl.rb +6 -0
  63. data/lib/polyphony/extensions/socket.rb +63 -10
  64. data/lib/polyphony/version.rb +1 -1
  65. data/polyphony.gemspec +1 -1
  66. data/test/helper.rb +36 -4
  67. data/test/io_uring_test.rb +55 -0
  68. data/test/stress.rb +4 -1
  69. data/test/test_backend.rb +63 -6
  70. data/test/test_ext.rb +1 -2
  71. data/test/test_fiber.rb +55 -20
  72. data/test/test_global_api.rb +132 -31
  73. data/test/test_io.rb +42 -0
  74. data/test/test_queue.rb +117 -0
  75. data/test/test_signal.rb +11 -8
  76. data/test/test_socket.rb +2 -2
  77. data/test/test_sync.rb +21 -0
  78. data/test/test_throttler.rb +3 -6
  79. data/test/test_trace.rb +7 -5
  80. metadata +36 -6
@@ -2,21 +2,29 @@
2
2
 
3
3
  void Init_Fiber();
4
4
  void Init_Polyphony();
5
- void Init_LibevBackend();
5
+ void Init_Backend();
6
6
  void Init_Queue();
7
7
  void Init_Event();
8
+ void Init_Runqueue();
8
9
  void Init_Thread();
9
10
  void Init_Tracing();
10
11
 
11
- void Init_polyphony_ext() {
12
- ev_set_allocator(xrealloc);
12
+ #ifdef POLYPHONY_PLAYGROUND
13
+ extern void playground();
14
+ #endif
13
15
 
16
+ void Init_polyphony_ext() {
14
17
  Init_Polyphony();
15
18
 
16
- Init_LibevBackend();
19
+ Init_Backend();
17
20
  Init_Queue();
18
21
  Init_Event();
22
+ Init_Runqueue();
19
23
  Init_Fiber();
20
24
  Init_Thread();
21
25
  Init_Tracing();
26
+
27
+ #ifdef POLYPHONY_PLAYGROUND
28
+ playground();
29
+ #endif
22
30
  }
@@ -4,6 +4,8 @@
4
4
  typedef struct queue {
5
5
  ring_buffer values;
6
6
  ring_buffer shift_queue;
7
+ ring_buffer push_queue;
8
+ unsigned int capacity;
7
9
  } Queue_t;
8
10
 
9
11
  VALUE cQueue = Qnil;
@@ -12,12 +14,14 @@ static void Queue_mark(void *ptr) {
12
14
  Queue_t *queue = ptr;
13
15
  ring_buffer_mark(&queue->values);
14
16
  ring_buffer_mark(&queue->shift_queue);
17
+ ring_buffer_mark(&queue->push_queue);
15
18
  }
16
19
 
17
20
  static void Queue_free(void *ptr) {
18
21
  Queue_t *queue = ptr;
19
22
  ring_buffer_free(&queue->values);
20
23
  ring_buffer_free(&queue->shift_queue);
24
+ ring_buffer_free(&queue->push_queue);
21
25
  xfree(ptr);
22
26
  }
23
27
 
@@ -41,36 +45,77 @@ static VALUE Queue_allocate(VALUE klass) {
41
45
  #define GetQueue(obj, queue) \
42
46
  TypedData_Get_Struct((obj), Queue_t, &Queue_type, (queue))
43
47
 
44
- static VALUE Queue_initialize(VALUE self) {
48
+ static VALUE Queue_initialize(int argc, VALUE *argv, VALUE self) {
45
49
  Queue_t *queue;
46
50
  GetQueue(self, queue);
47
51
 
48
52
  ring_buffer_init(&queue->values);
49
53
  ring_buffer_init(&queue->shift_queue);
54
+ ring_buffer_init(&queue->push_queue);
55
+ queue->capacity = (argc == 1) ? NUM2UINT(argv[0]) : 0;
50
56
 
51
57
  return self;
52
58
  }
53
59
 
60
+ inline void queue_resume_first_blocked_fiber(ring_buffer *queue) {
61
+ if (queue->count) {
62
+ VALUE fiber = ring_buffer_shift(queue);
63
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
64
+ }
65
+ }
66
+
67
+ inline void queue_resume_all_blocked_fibers(ring_buffer *queue) {
68
+ while (queue->count) {
69
+ VALUE fiber = ring_buffer_shift(queue);
70
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
71
+ }
72
+ }
73
+
74
+ inline void queue_resume_blocked_fibers_to_capacity(Queue_t *queue) {
75
+ for (unsigned int i = queue->values.count; (i < queue->capacity) && queue->push_queue.count; i++) {
76
+ VALUE fiber = ring_buffer_shift(&queue->push_queue);
77
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
78
+ }
79
+ }
80
+
81
+ inline void capped_queue_block_push(Queue_t *queue) {
82
+ VALUE fiber = rb_fiber_current();
83
+ VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
84
+ VALUE switchpoint_result;
85
+ while (1) {
86
+ if (queue->capacity > queue->values.count) Fiber_make_runnable(fiber, Qnil);
87
+
88
+ ring_buffer_push(&queue->push_queue, fiber);
89
+ switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
90
+ ring_buffer_delete(&queue->push_queue, fiber);
91
+
92
+ RAISE_IF_EXCEPTION(switchpoint_result);
93
+ RB_GC_GUARD(switchpoint_result);
94
+ if (queue->capacity > queue->values.count) break;
95
+ }
96
+ }
97
+
54
98
  VALUE Queue_push(VALUE self, VALUE value) {
55
99
  Queue_t *queue;
56
100
  GetQueue(self, queue);
57
101
 
58
- if (queue->shift_queue.count > 0) {
59
- VALUE fiber = ring_buffer_shift(&queue->shift_queue);
60
- if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
61
- }
102
+ if (queue->capacity) capped_queue_block_push(queue);
103
+
104
+ queue_resume_first_blocked_fiber(&queue->shift_queue);
62
105
  ring_buffer_push(&queue->values, value);
106
+
63
107
  return self;
64
108
  }
65
109
 
66
110
  VALUE Queue_unshift(VALUE self, VALUE value) {
67
111
  Queue_t *queue;
68
112
  GetQueue(self, queue);
69
- if (queue->shift_queue.count > 0) {
70
- VALUE fiber = ring_buffer_shift(&queue->shift_queue);
71
- if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
72
- }
113
+
114
+ if (queue->capacity) capped_queue_block_push(queue);
115
+
116
+ queue_resume_first_blocked_fiber(&queue->shift_queue);
73
117
  ring_buffer_unshift(&queue->values, value);
118
+
74
119
  return self;
75
120
  }
76
121
 
@@ -83,42 +128,63 @@ VALUE Queue_shift(VALUE self) {
83
128
  VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
84
129
 
85
130
  while (1) {
86
- ring_buffer_push(&queue->shift_queue, fiber);
87
- if (queue->values.count > 0) Fiber_make_runnable(fiber, Qnil);
131
+ if (queue->values.count) Fiber_make_runnable(fiber, Qnil);
88
132
 
133
+ ring_buffer_push(&queue->shift_queue, fiber);
89
134
  VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
90
135
  ring_buffer_delete(&queue->shift_queue, fiber);
91
136
 
92
- TEST_RESUME_EXCEPTION(switchpoint_result);
137
+ RAISE_IF_EXCEPTION(switchpoint_result);
93
138
  RB_GC_GUARD(switchpoint_result);
94
-
95
- if (queue->values.count > 0)
96
- return ring_buffer_shift(&queue->values);
139
+ if (queue->values.count) break;
97
140
  }
98
-
99
- return Qnil;
141
+ VALUE value = ring_buffer_shift(&queue->values);
142
+ if ((queue->capacity) && (queue->capacity > queue->values.count))
143
+ queue_resume_first_blocked_fiber(&queue->push_queue);
144
+ RB_GC_GUARD(value);
145
+ return value;
100
146
  }
101
147
 
102
- VALUE Queue_shift_no_wait(VALUE self) {
103
- Queue_t *queue;
148
+ VALUE Queue_delete(VALUE self, VALUE value) {
149
+ Queue_t *queue;
104
150
  GetQueue(self, queue);
105
151
 
106
- return ring_buffer_shift(&queue->values);
152
+ ring_buffer_delete(&queue->values, value);
153
+
154
+ if (queue->capacity && (queue->capacity > queue->values.count))
155
+ queue_resume_first_blocked_fiber(&queue->push_queue);
156
+
157
+ return self;
107
158
  }
108
159
 
109
- VALUE Queue_delete(VALUE self, VALUE value) {
160
+ VALUE Queue_cap(VALUE self, VALUE cap) {
161
+ unsigned int new_capacity = NUM2UINT(cap);
110
162
  Queue_t *queue;
111
163
  GetQueue(self, queue);
112
-
113
- ring_buffer_delete(&queue->values, value);
164
+ queue->capacity = new_capacity;
165
+
166
+ if (queue->capacity)
167
+ queue_resume_blocked_fibers_to_capacity(queue);
168
+ else
169
+ queue_resume_all_blocked_fibers(&queue->push_queue);
170
+
114
171
  return self;
115
172
  }
116
173
 
174
+ VALUE Queue_capped_p(VALUE self) {
175
+ Queue_t *queue;
176
+ GetQueue(self, queue);
177
+
178
+ return queue->capacity ? UINT2NUM(queue->capacity) : Qnil;
179
+ }
180
+
117
181
  VALUE Queue_clear(VALUE self) {
118
182
  Queue_t *queue;
119
183
  GetQueue(self, queue);
120
184
 
121
185
  ring_buffer_clear(&queue->values);
186
+ if (queue->capacity) queue_resume_blocked_fibers_to_capacity(queue);
187
+
122
188
  return self;
123
189
  }
124
190
 
@@ -134,6 +200,7 @@ VALUE Queue_shift_each(VALUE self) {
134
200
  GetQueue(self, queue);
135
201
 
136
202
  ring_buffer_shift_each(&queue->values);
203
+ if (queue->capacity) queue_resume_blocked_fibers_to_capacity(queue);
137
204
  return self;
138
205
  }
139
206
 
@@ -141,7 +208,9 @@ VALUE Queue_shift_all(VALUE self) {
141
208
  Queue_t *queue;
142
209
  GetQueue(self, queue);
143
210
 
144
- return ring_buffer_shift_all(&queue->values);
211
+ VALUE result = ring_buffer_shift_all(&queue->values);
212
+ if (queue->capacity) queue_resume_blocked_fibers_to_capacity(queue);
213
+ return result;
145
214
  }
146
215
 
147
216
  VALUE Queue_flush_waiters(VALUE self, VALUE value) {
@@ -160,14 +229,14 @@ VALUE Queue_empty_p(VALUE self) {
160
229
  Queue_t *queue;
161
230
  GetQueue(self, queue);
162
231
 
163
- return (queue->values.count == 0) ? Qtrue : Qfalse;
232
+ return (!queue->values.count) ? Qtrue : Qfalse;
164
233
  }
165
234
 
166
235
  VALUE Queue_pending_p(VALUE self) {
167
236
  Queue_t *queue;
168
237
  GetQueue(self, queue);
169
238
 
170
- return (queue->shift_queue.count > 0) ? Qtrue : Qfalse;
239
+ return (queue->shift_queue.count) ? Qtrue : Qfalse;
171
240
  }
172
241
 
173
242
  VALUE Queue_size_m(VALUE self) {
@@ -177,26 +246,22 @@ VALUE Queue_size_m(VALUE self) {
177
246
  return INT2NUM(queue->values.count);
178
247
  }
179
248
 
180
- void Queue_trace(VALUE self) {
181
- Queue_t *queue;
182
- GetQueue(self, queue);
183
-
184
- printf("run queue size: %d count: %d\n", queue->values.size, queue->values.count);
185
- }
186
-
187
249
  void Init_Queue() {
188
250
  cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
189
251
  rb_define_alloc_func(cQueue, Queue_allocate);
190
252
 
191
- rb_define_method(cQueue, "initialize", Queue_initialize, 0);
253
+ rb_define_method(cQueue, "initialize", Queue_initialize, -1);
192
254
  rb_define_method(cQueue, "push", Queue_push, 1);
193
255
  rb_define_method(cQueue, "<<", Queue_push, 1);
194
256
  rb_define_method(cQueue, "unshift", Queue_unshift, 1);
195
257
 
196
258
  rb_define_method(cQueue, "shift", Queue_shift, 0);
197
259
  rb_define_method(cQueue, "pop", Queue_shift, 0);
198
- rb_define_method(cQueue, "shift_no_wait", Queue_shift_no_wait, 0);
199
260
  rb_define_method(cQueue, "delete", Queue_delete, 1);
261
+ rb_define_method(cQueue, "clear", Queue_clear, 0);
262
+
263
+ rb_define_method(cQueue, "cap", Queue_cap, 1);
264
+ rb_define_method(cQueue, "capped?", Queue_capped_p, 0);
200
265
 
201
266
  rb_define_method(cQueue, "shift_each", Queue_shift_each, 0);
202
267
  rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
@@ -0,0 +1,102 @@
1
+ #include "polyphony.h"
2
+ #include "runqueue_ring_buffer.h"
3
+
4
+ typedef struct queue {
5
+ runqueue_ring_buffer entries;
6
+ } Runqueue_t;
7
+
8
+ VALUE cRunqueue = Qnil;
9
+
10
+ static void Runqueue_mark(void *ptr) {
11
+ Runqueue_t *runqueue = ptr;
12
+ runqueue_ring_buffer_mark(&runqueue->entries);
13
+ }
14
+
15
+ static void Runqueue_free(void *ptr) {
16
+ Runqueue_t *runqueue = ptr;
17
+ runqueue_ring_buffer_free(&runqueue->entries);
18
+ xfree(ptr);
19
+ }
20
+
21
+ static size_t Runqueue_size(const void *ptr) {
22
+ return sizeof(Runqueue_t);
23
+ }
24
+
25
+ static const rb_data_type_t Runqueue_type = {
26
+ "Runqueue",
27
+ {Runqueue_mark, Runqueue_free, Runqueue_size,},
28
+ 0, 0, 0
29
+ };
30
+
31
+ static VALUE Runqueue_allocate(VALUE klass) {
32
+ Runqueue_t *runqueue;
33
+
34
+ runqueue = ALLOC(Runqueue_t);
35
+ return TypedData_Wrap_Struct(klass, &Runqueue_type, runqueue);
36
+ }
37
+
38
+ #define GetRunqueue(obj, runqueue) \
39
+ TypedData_Get_Struct((obj), Runqueue_t, &Runqueue_type, (runqueue))
40
+
41
+ static VALUE Runqueue_initialize(VALUE self) {
42
+ Runqueue_t *runqueue;
43
+ GetRunqueue(self, runqueue);
44
+
45
+ runqueue_ring_buffer_init(&runqueue->entries);
46
+
47
+ return self;
48
+ }
49
+
50
+ void Runqueue_push(VALUE self, VALUE fiber, VALUE value, int reschedule) {
51
+ Runqueue_t *runqueue;
52
+ GetRunqueue(self, runqueue);
53
+
54
+ if (reschedule) runqueue_ring_buffer_delete(&runqueue->entries, fiber);
55
+ runqueue_ring_buffer_push(&runqueue->entries, fiber, value);
56
+ }
57
+
58
+ void Runqueue_unshift(VALUE self, VALUE fiber, VALUE value, int reschedule) {
59
+ Runqueue_t *runqueue;
60
+ GetRunqueue(self, runqueue);
61
+ if (reschedule) runqueue_ring_buffer_delete(&runqueue->entries, fiber);
62
+ runqueue_ring_buffer_unshift(&runqueue->entries, fiber, value);
63
+ }
64
+
65
+ runqueue_entry Runqueue_shift(VALUE self) {
66
+ Runqueue_t *runqueue;
67
+ GetRunqueue(self, runqueue);
68
+ return runqueue_ring_buffer_shift(&runqueue->entries);
69
+ }
70
+
71
+ void Runqueue_delete(VALUE self, VALUE fiber) {
72
+ Runqueue_t *runqueue;
73
+ GetRunqueue(self, runqueue);
74
+ runqueue_ring_buffer_delete(&runqueue->entries, fiber);
75
+ }
76
+
77
+ void Runqueue_clear(VALUE self) {
78
+ Runqueue_t *runqueue;
79
+ GetRunqueue(self, runqueue);
80
+ runqueue_ring_buffer_clear(&runqueue->entries);
81
+ }
82
+
83
+ long Runqueue_len(VALUE self) {
84
+ Runqueue_t *runqueue;
85
+ GetRunqueue(self, runqueue);
86
+
87
+ return runqueue->entries.count;
88
+ }
89
+
90
+ int Runqueue_empty_p(VALUE self) {
91
+ Runqueue_t *runqueue;
92
+ GetRunqueue(self, runqueue);
93
+
94
+ return (runqueue->entries.count == 0);
95
+ }
96
+
97
+ void Init_Runqueue() {
98
+ cRunqueue = rb_define_class_under(mPolyphony, "Runqueue", rb_cData);
99
+ rb_define_alloc_func(cRunqueue, Runqueue_allocate);
100
+
101
+ rb_define_method(cRunqueue, "initialize", Runqueue_initialize, 0);
102
+ }
@@ -0,0 +1,85 @@
1
+ #include "polyphony.h"
2
+ #include "runqueue_ring_buffer.h"
3
+
4
+ void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
5
+ buffer->size = 1;
6
+ buffer->count = 0;
7
+ buffer->entries = malloc(buffer->size * sizeof(runqueue_entry));
8
+ buffer->head = 0;
9
+ buffer->tail = 0;
10
+ }
11
+
12
+ void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
13
+ free(buffer->entries);
14
+ }
15
+
16
+ int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer) {
17
+ return buffer->count == 0;
18
+ }
19
+
20
+ static runqueue_entry nil_runqueue_entry = {(Qnil), (Qnil)};
21
+
22
+ runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
23
+ if (buffer->count == 0) return nil_runqueue_entry;
24
+
25
+ runqueue_entry value = buffer->entries[buffer->head];
26
+ buffer->head = (buffer->head + 1) % buffer->size;
27
+ buffer->count--;
28
+ return value;
29
+ }
30
+
31
+ void runqueue_ring_buffer_resize(runqueue_ring_buffer *buffer) {
32
+ unsigned int old_size = buffer->size;
33
+ buffer->size = old_size == 1 ? 4 : old_size * 2;
34
+ buffer->entries = realloc(buffer->entries, buffer->size * sizeof(runqueue_entry));
35
+ for (unsigned int idx = 0; idx < buffer->head && idx < buffer->tail; idx++)
36
+ buffer->entries[old_size + idx] = buffer->entries[idx];
37
+ buffer->tail = buffer->head + buffer->count;
38
+ }
39
+
40
+ void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
41
+ if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
42
+
43
+ buffer->head = (buffer->head - 1) % buffer->size;
44
+ buffer->entries[buffer->head].fiber = fiber;
45
+ buffer->entries[buffer->head].value = value;
46
+ buffer->count++;
47
+ }
48
+
49
+ void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
50
+ if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
51
+
52
+ buffer->entries[buffer->tail].fiber = fiber;
53
+ buffer->entries[buffer->tail].value = value;
54
+ buffer->tail = (buffer->tail + 1) % buffer->size;
55
+ buffer->count++;
56
+ }
57
+
58
+ void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
59
+ for (unsigned int i = 0; i < buffer->count; i++) {
60
+ rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].fiber);
61
+ rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].value);
62
+ }
63
+ }
64
+
65
+ void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int idx) {
66
+ for (unsigned int idx2 = idx; idx2 != buffer->tail; idx2 = (idx2 + 1) % buffer->size) {
67
+ buffer->entries[idx2] = buffer->entries[(idx2 + 1) % buffer->size];
68
+ }
69
+ buffer->count--;
70
+ buffer->tail = (buffer->tail - 1) % buffer->size;
71
+ }
72
+
73
+ void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
74
+ for (unsigned int i = 0; i < buffer->count; i++) {
75
+ unsigned int idx = (buffer->head + i) % buffer->size;
76
+ if (buffer->entries[idx].fiber == fiber) {
77
+ runqueue_ring_buffer_delete_at(buffer, idx);
78
+ return;
79
+ }
80
+ }
81
+ }
82
+
83
+ void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
84
+ buffer->count = buffer->head = buffer->tail = 0;
85
+ }