farce 0.4.0 → 0.4.1

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.
data/ext/farce/lock.c CHANGED
@@ -1,19 +1,13 @@
1
1
  #include "containers.h"
2
+ #include "native_wait.h"
2
3
  #include "ruby/fiber/scheduler.h"
3
- #include "ruby/io.h"
4
-
5
- #include <errno.h>
6
- #include <fcntl.h>
7
- #include <unistd.h>
8
4
 
9
5
  static VALUE cLock;
10
6
 
11
7
  typedef struct containers_lock_waiter containers_lock_waiter_t;
12
8
 
13
9
  struct containers_lock_waiter {
14
- int read_fd;
15
- int write_fd;
16
- bool notified;
10
+ containers_waiter_t notification;
17
11
  containers_lock_waiter_t *next;
18
12
  };
19
13
 
@@ -29,7 +23,6 @@ typedef struct {
29
23
  typedef struct {
30
24
  containers_lock_t *lock;
31
25
  containers_lock_waiter_t waiter;
32
- bool completed;
33
26
  } containers_lock_wait_context_t;
34
27
 
35
28
  typedef struct {
@@ -51,35 +44,14 @@ typedef struct {
51
44
  VALUE opaque;
52
45
  } containers_lock_call_context_t;
53
46
 
54
- static void
55
- containers_lock_set_fd_flags(int fd)
56
- {
57
- #ifdef _WIN32
58
- (void)fd;
59
- #else
60
- int flags = fcntl(fd, F_GETFL, 0);
61
- if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
62
- flags = fcntl(fd, F_GETFD, 0);
63
- if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
64
- #endif
65
- }
66
-
67
- /* Called with lock->guard held. Mutex wakeups only need to make one waiter
68
- * runnable; the selected waiter remains linked until its ensure cleanup. */
47
+ /* Called with lock->guard held. The selected waiter remains linked until
48
+ * ensure cleanup, and its notification latches before backend selection. */
69
49
  static void
70
50
  containers_lock_notify_one_locked(containers_lock_t *lock)
71
51
  {
72
- unsigned char byte = 1;
73
52
  for (containers_lock_waiter_t *waiter = lock->waiters; waiter; waiter = waiter->next) {
74
- if (waiter->notified) continue;
75
- ssize_t result;
76
- do {
77
- result = write(waiter->write_fd, &byte, 1);
78
- } while (result < 0 && errno == EINTR);
79
- if (result == 1 || (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
80
- waiter->notified = true;
81
- return;
82
- }
53
+ if (waiter->notification.notified) continue;
54
+ if (containers_waiter_notify(&waiter->notification)) return;
83
55
  }
84
56
  }
85
57
 
@@ -104,7 +76,7 @@ RUBY_REFERENCES(containers_lock_references) = {
104
76
  };
105
77
 
106
78
  static const rb_data_type_t containers_lock_type = {
107
- .wrap_struct_name = "Ractor::Containers::Lock",
79
+ .wrap_struct_name = "Farce::Internal::Lock",
108
80
  .function = {
109
81
  .dmark = RUBY_REFS_LIST_PTR(containers_lock_references),
110
82
  .dfree = containers_lock_free,
@@ -188,19 +160,13 @@ containers_lock_initialize_copy(VALUE self, VALUE other)
188
160
  return containers_publish_native_with_references(self, containers_lock_validate_references);
189
161
  }
190
162
 
191
- static bool
192
- containers_lock_wait_for_descriptor(int fd)
193
- {
194
- return containers_wait_for_readable(fd, Qnil);
195
- }
196
-
197
163
  static VALUE
198
164
  containers_lock_wait_body(VALUE opaque)
199
165
  {
200
166
  containers_lock_wait_context_t *context = (containers_lock_wait_context_t *)opaque;
201
- bool awakened = containers_lock_wait_for_descriptor(context->waiter.read_fd);
202
- context->completed = true;
203
- return awakened ? Qtrue : Qfalse;
167
+ return containers_waiter_wait(
168
+ &context->waiter.notification, &context->lock->guard, false, 0, NULL
169
+ ) ? Qtrue : Qfalse;
204
170
  }
205
171
 
206
172
  static VALUE
@@ -221,12 +187,13 @@ containers_lock_wait_cleanup(VALUE opaque)
221
187
  }
222
188
  previous = *link;
223
189
  }
224
- if (!context->completed && context->waiter.notified && NIL_P(context->lock->owner)) {
190
+ /* A wake does not reserve ownership. Pass the baton even on a normal
191
+ * return, since cancellation can arrive before the caller claims the lock. */
192
+ if (context->waiter.notification.notified && NIL_P(context->lock->owner)) {
225
193
  containers_lock_notify_one_locked(context->lock);
226
194
  }
227
195
  pthread_mutex_unlock(&context->lock->guard);
228
- close(context->waiter.read_fd);
229
- close(context->waiter.write_fd);
196
+ containers_waiter_close(&context->waiter.notification);
230
197
  return Qnil;
231
198
  }
232
199
 
@@ -234,24 +201,11 @@ containers_lock_wait_cleanup(VALUE opaque)
234
201
  static void
235
202
  containers_lock_wait_once(containers_lock_t *lock)
236
203
  {
237
- int descriptors[2];
238
- if (pipe(descriptors) != 0) {
239
- pthread_mutex_unlock(&lock->guard);
240
- rb_sys_fail("pipe");
241
- }
242
- containers_lock_set_fd_flags(descriptors[0]);
243
- containers_lock_set_fd_flags(descriptors[1]);
244
-
245
204
  containers_lock_wait_context_t context = {
246
205
  .lock = lock,
247
- .waiter = {
248
- .read_fd = descriptors[0],
249
- .write_fd = descriptors[1],
250
- .notified = false,
251
- .next = NULL,
252
- },
253
- .completed = false,
206
+ .waiter = {.next = NULL},
254
207
  };
208
+ containers_waiter_init(&context.waiter.notification);
255
209
  if (lock->last_waiter) lock->last_waiter->next = &context.waiter;
256
210
  else lock->waiters = &context.waiter;
257
211
  lock->last_waiter = &context.waiter;
data/ext/farce/map.c CHANGED
@@ -1,16 +1,14 @@
1
1
  #include "containers.h"
2
+ #include "native_wait.h"
2
3
  #include "transaction.h"
3
4
  #include "ruby/fiber/scheduler.h"
4
- #include "ruby/io.h"
5
5
 
6
6
  #include <errno.h>
7
- #include <fcntl.h>
8
7
  #include <math.h>
9
8
  #include <stdint.h>
10
9
  #include <stdatomic.h>
11
10
  #include <stdlib.h>
12
11
  #include <time.h>
13
- #include <unistd.h>
14
12
 
15
13
  #define MAP_INITIAL_CAPACITY 16
16
14
 
@@ -42,9 +40,7 @@ typedef struct {
42
40
  } map_slot_t;
43
41
 
44
42
  typedef struct map_waiter {
45
- int read_fd;
46
- int write_fd;
47
- bool notified;
43
+ containers_waiter_t notification;
48
44
  struct map_waiter *next;
49
45
  } map_waiter_t;
50
46
 
@@ -141,46 +137,12 @@ map_parse_timeout(VALUE timeout)
141
137
  return parsed;
142
138
  }
143
139
 
144
- static void
145
- map_set_fd_flags(int fd)
146
- {
147
- #ifdef _WIN32
148
- (void)fd;
149
- #else
150
- int flags = fcntl(fd, F_GETFL, 0);
151
- if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
152
- flags = fcntl(fd, F_GETFD, 0);
153
- if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
154
- #endif
155
- }
156
-
157
- static void
158
- map_waiter_initialize(map_waiter_t *waiter)
159
- {
160
- int descriptors[2];
161
- if (pipe(descriptors) != 0) rb_sys_fail("pipe");
162
- waiter->read_fd = descriptors[0];
163
- waiter->write_fd = descriptors[1];
164
- waiter->notified = false;
165
- waiter->next = NULL;
166
- map_set_fd_flags(waiter->read_fd);
167
- map_set_fd_flags(waiter->write_fd);
168
- }
169
-
170
140
  static void
171
141
  map_notify_waiters_locked(map_t *map)
172
142
  {
173
- unsigned char byte = 1;
174
143
  map->generation++;
175
144
  for (map_waiter_t *waiter = map->waiters; waiter; waiter = waiter->next) {
176
- if (waiter->notified) continue;
177
- ssize_t result;
178
- do {
179
- result = write(waiter->write_fd, &byte, 1);
180
- } while (result < 0 && errno == EINTR);
181
- if (result == 1 || (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
182
- waiter->notified = true;
183
- }
145
+ (void)containers_waiter_notify(&waiter->notification);
184
146
  }
185
147
  }
186
148
 
@@ -260,7 +222,7 @@ map_memsize(const void *pointer)
260
222
  }
261
223
 
262
224
  static const rb_data_type_t map_type = {
263
- .wrap_struct_name = "Ractor::Containers::Map",
225
+ .wrap_struct_name = "Farce::Internal::Map",
264
226
  .function = {
265
227
  .dmark = map_mark,
266
228
  .dfree = map_free,
@@ -430,13 +392,10 @@ static VALUE
430
392
  map_wait_body(VALUE opaque)
431
393
  {
432
394
  map_wait_context_t *context = (map_wait_context_t *)opaque;
433
- VALUE wait_timeout = Qnil;
434
- if (context->timeout->finite) {
435
- double remaining = context->timeout->deadline - map_monotonic_now();
436
- if (remaining <= 0) return Qfalse;
437
- wait_timeout = DBL2NUM(remaining);
438
- }
439
- return containers_wait_for_readable(context->waiter->read_fd, wait_timeout) ? Qtrue : Qfalse;
395
+ return containers_waiter_wait(
396
+ &context->waiter->notification, &context->map->lock,
397
+ context->timeout->finite, context->timeout->deadline, NULL
398
+ ) ? Qtrue : Qfalse;
440
399
  }
441
400
 
442
401
  static VALUE
@@ -451,8 +410,7 @@ map_wait_cleanup(VALUE opaque)
451
410
  pthread_mutex_unlock(&context->map->lock);
452
411
  context->registered = false;
453
412
  }
454
- close(context->waiter->read_fd);
455
- close(context->waiter->write_fd);
413
+ containers_waiter_close(&context->waiter->notification);
456
414
  return Qnil;
457
415
  }
458
416
 
@@ -460,13 +418,11 @@ static bool
460
418
  map_wait_once(map_t *map, uint64_t generation, map_timeout_t *timeout)
461
419
  {
462
420
  map_waiter_t waiter;
463
- map_waiter_initialize(&waiter);
421
+ containers_waiter_init(&waiter.notification);
464
422
 
465
423
  pthread_mutex_lock(&map->lock);
466
424
  if (map->generation != generation) {
467
425
  pthread_mutex_unlock(&map->lock);
468
- close(waiter.read_fd);
469
- close(waiter.write_fd);
470
426
  return true;
471
427
  }
472
428
  waiter.next = map->waiters;
@@ -0,0 +1,139 @@
1
+ #include "native_wait.h"
2
+ #include "ruby/fiber/scheduler.h"
3
+
4
+ #include <errno.h>
5
+ #include <fcntl.h>
6
+ #include <time.h>
7
+ #include <unistd.h>
8
+
9
+ static double
10
+ waiter_monotonic_now(void)
11
+ {
12
+ struct timespec now;
13
+ #ifdef CLOCK_MONOTONIC
14
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
15
+ return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0;
16
+ }
17
+ #endif
18
+ struct timeval fallback;
19
+ gettimeofday(&fallback, NULL);
20
+ return (double)fallback.tv_sec + (double)fallback.tv_usec / 1000000.0;
21
+ }
22
+
23
+ static void
24
+ waiter_set_fd_flags(int fd)
25
+ {
26
+ #ifndef _WIN32
27
+ int flags = fcntl(fd, F_GETFL, 0);
28
+ if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
29
+ flags = fcntl(fd, F_GETFD, 0);
30
+ if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
31
+ #endif
32
+ }
33
+
34
+ void
35
+ containers_waiter_init(containers_waiter_t *waiter)
36
+ {
37
+ containers_futex_init(&waiter->futex);
38
+ waiter->read_fd = waiter->write_fd = -1;
39
+ waiter->mode = CONTAINERS_WAITER_PENDING;
40
+ waiter->notified = false;
41
+ }
42
+
43
+ bool
44
+ containers_waiter_notify(containers_waiter_t *waiter)
45
+ {
46
+ if (waiter->notified) return true;
47
+ if (waiter->mode == CONTAINERS_WAITER_FUTEX) {
48
+ waiter->notified = true;
49
+ containers_futex_notify(&waiter->futex);
50
+ return true;
51
+ }
52
+
53
+ /* A pending waiter has not selected its backend yet. Latch its wake so
54
+ * setup can return immediately or a future kernel wait sees a mismatch. */
55
+ atomic_store_explicit(&waiter->futex.value, 1, memory_order_release);
56
+ if (waiter->mode == CONTAINERS_WAITER_PENDING) {
57
+ waiter->notified = true;
58
+ return true;
59
+ }
60
+
61
+ unsigned char byte = 1;
62
+ ssize_t result;
63
+ do {
64
+ result = write(waiter->write_fd, &byte, 1);
65
+ } while (result < 0 && errno == EINTR);
66
+ if (result == 1 || (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
67
+ waiter->notified = true;
68
+ }
69
+ return waiter->notified;
70
+ }
71
+
72
+ bool
73
+ containers_waiter_wait(
74
+ containers_waiter_t *waiter,
75
+ pthread_mutex_t *guard,
76
+ bool finite,
77
+ double deadline,
78
+ int *pipe_error
79
+ )
80
+ {
81
+ if (pipe_error) *pipe_error = 0;
82
+ if (atomic_load_explicit(&waiter->futex.value, memory_order_acquire) == 1) return true;
83
+ if (finite && deadline <= waiter_monotonic_now()) return false;
84
+
85
+ /* Selection can enter Ruby or probe the kernel. The registration stays
86
+ * pending, with no native container mutex held, throughout this work. */
87
+ VALUE scheduler = rb_fiber_scheduler_current();
88
+ /* Blocking futex waits failed the cross-core thread handoff performance
89
+ * gate. Keep Ruby's managed IO path for threads and use futexes only when
90
+ * the owning event loop can complete them asynchronously. */
91
+ bool use_futex = !NIL_P(scheduler) && containers_futex_scheduler_supported(scheduler);
92
+
93
+ int descriptors[2] = {-1, -1};
94
+ if (!use_futex) {
95
+ if (pipe(descriptors) != 0) {
96
+ int error = errno;
97
+ if (atomic_load_explicit(&waiter->futex.value, memory_order_acquire) == 1) return true;
98
+ if (pipe_error) {
99
+ *pipe_error = error;
100
+ return false;
101
+ }
102
+ rb_syserr_fail(error, "pipe");
103
+ }
104
+ waiter_set_fd_flags(descriptors[0]);
105
+ waiter_set_fd_flags(descriptors[1]);
106
+ }
107
+
108
+ /* No Ruby calls occur between creating descriptors and recording ownership.
109
+ * A notifier either latches a pending wake or observes the complete backend. */
110
+ if (guard) pthread_mutex_lock(guard);
111
+ waiter->read_fd = descriptors[0];
112
+ waiter->write_fd = descriptors[1];
113
+ waiter->mode = use_futex ? CONTAINERS_WAITER_FUTEX : CONTAINERS_WAITER_IO;
114
+ bool notified = waiter->notified;
115
+ if (guard) pthread_mutex_unlock(guard);
116
+ if (notified) return true;
117
+
118
+ VALUE timeout = Qnil;
119
+ if (finite) {
120
+ double remaining = deadline - waiter_monotonic_now();
121
+ if (remaining <= 0) {
122
+ return atomic_load_explicit(&waiter->futex.value, memory_order_acquire) == 1;
123
+ }
124
+ timeout = DBL2NUM(remaining);
125
+ }
126
+
127
+ if (use_futex) {
128
+ return containers_futex_scheduler_wait(scheduler, &waiter->futex, timeout);
129
+ }
130
+ return containers_wait_for_readable(waiter->read_fd, timeout);
131
+ }
132
+
133
+ void
134
+ containers_waiter_close(containers_waiter_t *waiter)
135
+ {
136
+ if (waiter->read_fd >= 0) close(waiter->read_fd);
137
+ if (waiter->write_fd >= 0) close(waiter->write_fd);
138
+ waiter->read_fd = waiter->write_fd = -1;
139
+ }
@@ -0,0 +1,37 @@
1
+ #ifndef FARCE_NATIVE_WAIT_H
2
+ #define FARCE_NATIVE_WAIT_H
3
+
4
+ #include "containers.h"
5
+ #include "futex.h"
6
+
7
+ typedef enum {
8
+ CONTAINERS_WAITER_PENDING,
9
+ CONTAINERS_WAITER_FUTEX,
10
+ CONTAINERS_WAITER_IO
11
+ } containers_waiter_mode_t;
12
+
13
+ typedef struct {
14
+ containers_futex_t futex;
15
+ int read_fd, write_fd;
16
+ containers_waiter_mode_t mode;
17
+ bool notified;
18
+ } containers_waiter_t;
19
+
20
+ /* Initialize before linking under the container's guard. Notification is
21
+ * native-only and uses that same guard. A NULL guard denotes owner-local state. */
22
+ void containers_waiter_init(containers_waiter_t *waiter);
23
+ bool containers_waiter_notify(containers_waiter_t *waiter);
24
+ /* Run outside the guard and inside the caller's cleanup ensure. The absolute
25
+ * monotonic deadline includes backend setup. Optional pipe_error lets callers
26
+ * retain their own descriptor-pressure recovery; other errors raise normally. */
27
+ bool containers_waiter_wait(
28
+ containers_waiter_t *waiter,
29
+ pthread_mutex_t *guard,
30
+ bool finite,
31
+ double deadline,
32
+ int *pipe_error
33
+ );
34
+ /* Unlink under the guard first. Waiting has already drained native requests. */
35
+ void containers_waiter_close(containers_waiter_t *waiter);
36
+
37
+ #endif
@@ -1,4 +1,5 @@
1
1
  #include "containers.h"
2
+ #include "native_wait.h"
2
3
  #include "unshared_wait.h"
3
4
  #include <ruby/atomic.h>
4
5
  #include <ruby/fiber/scheduler.h>
@@ -348,9 +349,7 @@ priority_queue_bucket_unlink(
348
349
  }
349
350
 
350
351
  typedef struct priority_queue_lock_waiter {
351
- int read_fd;
352
- int write_fd;
353
- bool notified;
352
+ containers_waiter_t notification;
354
353
  struct priority_queue_lock_waiter *next;
355
354
  } priority_queue_lock_waiter_t;
356
355
 
@@ -753,19 +752,6 @@ priority_queue_get(VALUE self)
753
752
  return queue;
754
753
  }
755
754
 
756
- static void
757
- priority_queue_lock_set_fd_flags(int fd)
758
- {
759
- #ifdef _WIN32
760
- (void)fd;
761
- #else
762
- int flags = fcntl(fd, F_GETFL, 0);
763
- if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
764
- flags = fcntl(fd, F_GETFD, 0);
765
- if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
766
- #endif
767
- }
768
-
769
755
  /* Called with queue->lock held. A logical unlock only needs to make one
770
756
  * waiter runnable. The selected waiter remains linked until ensure cleanup. */
771
757
  static void
@@ -777,20 +763,9 @@ priority_queue_lock_notify_one_locked(priority_queue_t *queue)
777
763
  * next waiter. Reentrant operations can register for a later unlock. */
778
764
  farce_unshared_wait_notify_all(&queue->unshared_lock_waiters);
779
765
  }
780
- unsigned char byte = 1;
781
- priority_queue_lock_waiter_t *waiter;
782
-
783
- for (waiter = queue->waiters; waiter; waiter = waiter->next) {
784
- ssize_t result;
785
- if (waiter->notified) continue;
786
- do {
787
- result = write(waiter->write_fd, &byte, 1);
788
- } while (result < 0 && errno == EINTR);
789
- if (result == 1 ||
790
- (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
791
- waiter->notified = true;
792
- return;
793
- }
766
+ for (priority_queue_lock_waiter_t *waiter = queue->waiters; waiter; waiter = waiter->next) {
767
+ if (waiter->notification.notified) continue;
768
+ if (containers_waiter_notify(&waiter->notification)) return;
794
769
  }
795
770
  }
796
771
 
@@ -799,18 +774,16 @@ typedef struct {
799
774
  priority_queue_lock_waiter_t waiter;
800
775
  } priority_queue_lock_wait_context_t;
801
776
 
802
- static bool
803
- priority_queue_lock_wait_for_descriptor(int fd)
804
- {
805
- return containers_wait_for_readable(fd, Qnil);
806
- }
807
-
808
777
  static VALUE
809
778
  priority_queue_lock_wait_body(VALUE opaque)
810
779
  {
811
780
  priority_queue_lock_wait_context_t *context =
812
781
  (priority_queue_lock_wait_context_t *)opaque;
813
- bool awakened = priority_queue_lock_wait_for_descriptor(context->waiter.read_fd);
782
+ bool awakened = containers_waiter_wait(
783
+ &context->waiter.notification,
784
+ context->queue->shared ? &context->queue->lock : NULL,
785
+ false, 0, NULL
786
+ );
814
787
  return awakened ? Qtrue : Qfalse;
815
788
  }
816
789
 
@@ -835,10 +808,10 @@ priority_queue_lock_wait_cleanup(VALUE opaque)
835
808
  }
836
809
  /* A notification only makes this waiter runnable; it does not reserve
837
810
  * ownership. Pass the baton whenever a notified waiter leaves while the
838
- * queue is still ownerless. This covers both interruption inside rb_io_wait
811
+ * queue is still ownerless. This covers both interruption while waiting
839
812
  * and cancellation after a normal wake but before the acquisition loop can
840
813
  * claim ownership, so later waiters cannot be stranded. */
841
- if (context->waiter.notified &&
814
+ if (context->waiter.notification.notified &&
842
815
  NIL_P(context->queue->owner_fiber) &&
843
816
  !priority_queue_publication_busy(
844
817
  RUBY_ATOMIC_LOAD(context->queue->publication_state)
@@ -846,8 +819,7 @@ priority_queue_lock_wait_cleanup(VALUE opaque)
846
819
  priority_queue_lock_notify_one_locked(context->queue);
847
820
  }
848
821
  priority_queue_mutex_unlock(context->queue);
849
- close(context->waiter.read_fd);
850
- close(context->waiter.write_fd);
822
+ containers_waiter_close(&context->waiter.notification);
851
823
  return Qnil;
852
824
  }
853
825
 
@@ -859,25 +831,11 @@ priority_queue_lock_wait_once(priority_queue_t *queue)
859
831
  (void)farce_unshared_wait(&queue->unshared_lock_waiters, queue->unshared_self, false, 0);
860
832
  return;
861
833
  }
862
- int descriptors[2];
863
- priority_queue_lock_wait_context_t context;
864
-
865
- if (pipe(descriptors) != 0) {
866
- priority_queue_mutex_unlock(queue);
867
- rb_sys_fail("pipe");
868
- }
869
- priority_queue_lock_set_fd_flags(descriptors[0]);
870
- priority_queue_lock_set_fd_flags(descriptors[1]);
871
-
872
- context = (priority_queue_lock_wait_context_t){
834
+ priority_queue_lock_wait_context_t context = {
873
835
  .queue = queue,
874
- .waiter = {
875
- .read_fd = descriptors[0],
876
- .write_fd = descriptors[1],
877
- .notified = false,
878
- .next = NULL,
879
- },
836
+ .waiter = {.next = NULL},
880
837
  };
838
+ containers_waiter_init(&context.waiter.notification);
881
839
  if (queue->last_waiter) queue->last_waiter->next = &context.waiter;
882
840
  else queue->waiters = &context.waiter;
883
841
  queue->last_waiter = &context.waiter;
data/ext/farce/queue.c CHANGED
@@ -224,7 +224,7 @@ queue_memsize(const void *pointer)
224
224
  }
225
225
 
226
226
  static const rb_data_type_t queue_type = {
227
- .wrap_struct_name = "Ractor::Containers::Queue",
227
+ .wrap_struct_name = "Farce::Internal::Queue",
228
228
  .function = {
229
229
  .dmark = queue_mark,
230
230
  .dfree = queue_free,