farce 0.1.0-arm-linux-gnu → 0.2.0-arm-linux-gnu

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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -1
  3. data/README.md +72 -16
  4. data/docs/gems/dry-types.md +8 -8
  5. data/docs/modes.md +67 -10
  6. data/ext/farce/atom.c +37 -22
  7. data/ext/farce/bounded_map.c +17 -13
  8. data/ext/farce/containers.h +15 -0
  9. data/ext/farce/counter.c +13 -18
  10. data/ext/farce/exchanger.c +5 -1
  11. data/ext/farce/farce.c +1 -1
  12. data/ext/farce/flag.c +39 -3
  13. data/ext/farce/io.c +3 -3
  14. data/ext/farce/lock.c +17 -23
  15. data/ext/farce/map.c +88 -66
  16. data/ext/farce/priority_queue.c +55 -43
  17. data/ext/farce/queue.c +10 -4
  18. data/ext/farce/reactor.c +52 -25
  19. data/ext/farce/reactor.h +1 -1
  20. data/ext/farce/signal.c +5 -1
  21. data/ext/farce/transaction.c +20 -24
  22. data/ext/farce/tree_map.c +33 -18
  23. data/ext/farce/trie.c +81 -43
  24. data/ext/farce/unshareable.c +168 -19
  25. data/ext/farce/unshared_io_pool.h +21 -12
  26. data/ext/farce/unshared_signal.c +13 -2
  27. data/ext/farce/unshared_wait.h +3 -0
  28. data/ext/farce/vector.c +82 -18
  29. data/ext/farce/weak_map.c +75 -63
  30. data/lib/farce/_yard/macros.rb +18 -3
  31. data/lib/farce/abstract/flag.rb +73 -0
  32. data/lib/farce/abstract/queue.rb +4 -3
  33. data/lib/farce/abstract/scheduler.rb +3 -3
  34. data/lib/farce/abstract/set.rb +17 -5
  35. data/lib/farce/abstract/sorted_set.rb +2 -2
  36. data/lib/farce/abstract/timer_queue.rb +49 -10
  37. data/lib/farce/abstract/vector.rb +510 -28
  38. data/lib/farce/atom.rb +1 -1
  39. data/lib/farce/class_mirror.rb +20 -7
  40. data/lib/farce/config.rb +7 -1
  41. data/lib/farce/engine/jvm/flag.rb +15 -3
  42. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  43. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  44. data/lib/farce/engine/ruby/shared/require_hook.rb +18 -0
  45. data/lib/farce/engine/ruby.rb +7 -0
  46. data/lib/farce/engine/shared/unshared_vector.rb +31 -4
  47. data/lib/farce/engine/shared/vector.rb +8 -0
  48. data/lib/farce/engine/shared/weak_map/index.rb +14 -1
  49. data/lib/farce/engine/truffleruby/native/flag.rb +15 -3
  50. data/lib/farce/envelope.rb +54 -7
  51. data/lib/farce/exchanger.rb +1 -1
  52. data/lib/farce/flag.rb +2 -0
  53. data/lib/farce/integrations/active_support.rb +1 -1
  54. data/lib/farce/integrations/bson.rb +1 -1
  55. data/lib/farce/integrations/cbor.rb +1 -1
  56. data/lib/farce/integrations/concurrent.rb +3 -3
  57. data/lib/farce/integrations/date.rb +8 -0
  58. data/lib/farce/integrations/dry_types.rb +7 -4
  59. data/lib/farce/integrations/json.rb +2 -2
  60. data/lib/farce/integrations/msgpack.rb +1 -1
  61. data/lib/farce/integrations/oj.rb +3 -4
  62. data/lib/farce/integrations/psych.rb +1 -1
  63. data/lib/farce/integrations/ractor_sharing.rb +1 -1
  64. data/lib/farce/integrations/sorted_set.rb +1 -1
  65. data/lib/farce/integrations/weakref.rb +9 -1
  66. data/lib/farce/integrations/yajl.rb +3 -3
  67. data/lib/farce/integrations.rb +33 -23
  68. data/lib/farce/internal/flag_waiting.rb +30 -0
  69. data/lib/farce/internal/freeze.rb +8 -3
  70. data/lib/farce/internal/map_value_modes.rb +1 -1
  71. data/lib/farce/internal/scheduler_lifecycle.rb +1 -1
  72. data/lib/farce/lazy.rb +4 -4
  73. data/lib/farce/lazy_ref.rb +3 -3
  74. data/lib/farce/lfu_map.rb +1 -1
  75. data/lib/farce/local/flag.rb +4 -0
  76. data/lib/farce/local/scoped.rb +1 -1
  77. data/lib/farce/lru_map.rb +1 -1
  78. data/lib/farce/map.rb +2 -2
  79. data/lib/farce/mode_manager/smart_rule.rb +138 -0
  80. data/lib/farce/mode_manager.rb +12 -3
  81. data/lib/farce/molecule.rb +2 -2
  82. data/lib/farce/pool.rb +1 -1
  83. data/lib/farce/port.rb +4 -4
  84. data/lib/farce/priority_queue.rb +1 -1
  85. data/lib/farce/proxy/register.rb +6 -0
  86. data/lib/farce/proxy/supervisor.rb +1 -1
  87. data/lib/farce/proxy.rb +7 -2
  88. data/lib/farce/queue.rb +1 -1
  89. data/lib/farce/scheduler.rb +2 -2
  90. data/lib/farce/set.rb +1 -1
  91. data/lib/farce/sorted_set.rb +1 -1
  92. data/lib/farce/timer_queue.rb +20 -6
  93. data/lib/farce/transaction/vector.rb +43 -9
  94. data/lib/farce/transaction/wrapper.rb +14 -0
  95. data/lib/farce/tree_map.rb +1 -1
  96. data/lib/farce/unsafe/lfu_map.rb +2 -0
  97. data/lib/farce/unsafe/lru_map.rb +2 -0
  98. data/lib/farce/unsafe/tree_map.rb +2 -0
  99. data/lib/farce/unshareable.rb +8 -19
  100. data/lib/farce/unshared/atom.rb +3 -0
  101. data/lib/farce/unshared/lfu_map.rb +2 -0
  102. data/lib/farce/unshared/lru_map.rb +2 -0
  103. data/lib/farce/unshared/map.rb +3 -0
  104. data/lib/farce/unshared/set.rb +1 -0
  105. data/lib/farce/unshared/sorted_set.rb +1 -0
  106. data/lib/farce/unshared/tree_map.rb +5 -1
  107. data/lib/farce/unshared/vector.rb +3 -0
  108. data/lib/farce/unshared/weak_atom.rb +5 -1
  109. data/lib/farce/unshared/weak_key_map.rb +3 -0
  110. data/lib/farce/unshared/weak_map.rb +3 -0
  111. data/lib/farce/unshared/weak_set.rb +1 -0
  112. data/lib/farce/unshared/weak_value_map.rb +3 -0
  113. data/lib/farce/vector.rb +44 -19
  114. data/lib/farce/version.rb +1 -1
  115. data/lib/farce.rb +11 -8
  116. metadata +5 -1
@@ -1,34 +1,69 @@
1
1
  #include "containers.h"
2
2
 
3
3
  typedef struct {
4
- unsigned char reserved;
5
- } containers_pin_marker_t;
4
+ VALUE owner_class;
5
+ } containers_marker_t;
6
6
 
7
7
  static ID pin_marker_id;
8
8
  static ID copy_marker_id;
9
9
  static ID move_marker_id;
10
+ static ID share_marker_id;
11
+ static ID owner_class_id;
12
+ static ID new_id;
10
13
  static ID pipe_id;
11
14
  static ID close_id;
12
15
  static VALUE cPinMarker;
16
+ static VALUE cNoncopyableMarker;
17
+ static VALUE cNonmovableMarker;
18
+ static VALUE cUnshareableMarker;
19
+ static VALUE eRactorError;
13
20
  static VALUE eIsolationError;
14
21
 
22
+ static void
23
+ containers_marker_mark(void *pointer)
24
+ {
25
+ containers_marker_t *marker = pointer;
26
+ rb_gc_mark_movable(marker->owner_class);
27
+ }
28
+
29
+ static void
30
+ containers_marker_compact(void *pointer)
31
+ {
32
+ containers_marker_t *marker = pointer;
33
+ containers_gc_update(&marker->owner_class);
34
+ }
35
+
15
36
  static size_t
16
- containers_pin_marker_size(const void *pointer)
37
+ containers_marker_size(const void *pointer)
17
38
  {
18
- return pointer == NULL ? 0 : sizeof(containers_pin_marker_t);
39
+ return pointer == NULL ? 0 : sizeof(containers_marker_t);
19
40
  }
20
41
 
21
42
  static const rb_data_type_t containers_pin_marker_type = {
22
43
  .wrap_struct_name = "Ractor::Containers pin marker",
23
44
  .function = {
24
- .dmark = NULL,
45
+ .dmark = containers_marker_mark,
25
46
  .dfree = RUBY_TYPED_DEFAULT_FREE,
26
- .dsize = containers_pin_marker_size,
27
- .dcompact = NULL,
47
+ .dsize = containers_marker_size,
48
+ .dcompact = containers_marker_compact,
28
49
  },
29
50
  .parent = NULL,
30
51
  .data = NULL,
31
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
52
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE,
53
+ };
54
+
55
+ /* Native class metadata avoids the unshareable generic ivar storage of T_DATA. */
56
+ static const rb_data_type_t containers_nonmovable_marker_type = {
57
+ .wrap_struct_name = "Farce nonmovable marker",
58
+ .function = {
59
+ .dmark = containers_marker_mark,
60
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
61
+ .dsize = containers_marker_size,
62
+ .dcompact = containers_marker_compact,
63
+ },
64
+ .parent = NULL,
65
+ .data = NULL,
66
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE,
32
67
  };
33
68
 
34
69
  static bool
@@ -51,30 +86,92 @@ containers_check_guard_target(VALUE object)
51
86
  }
52
87
 
53
88
  static VALUE
54
- containers_new_pin_marker(void)
89
+ containers_marker_initialize(VALUE marker, VALUE owner_class)
90
+ {
91
+ Check_Type(owner_class, T_CLASS);
92
+ rb_ivar_set(marker, owner_class_id, owner_class);
93
+ return marker;
94
+ }
95
+
96
+ static VALUE
97
+ containers_marker_owner_class(VALUE marker)
98
+ {
99
+ if (containers_pin_marker_p(marker) || rb_typeddata_is_kind_of(marker, &containers_nonmovable_marker_type)) {
100
+ return ((containers_marker_t *)RTYPEDDATA_DATA(marker))->owner_class;
101
+ }
102
+ return rb_ivar_get(marker, owner_class_id);
103
+ }
104
+
105
+ static VALUE
106
+ containers_marker_inspect(VALUE marker)
55
107
  {
56
- containers_pin_marker_t *marker_data;
108
+ return rb_sprintf("#<unshareable marker for %"PRIsVALUE">", containers_marker_owner_class(marker));
109
+ }
110
+
111
+ static VALUE
112
+ containers_new_pin_marker(VALUE owner_class)
113
+ {
114
+ containers_marker_t *marker_data;
57
115
  VALUE marker = TypedData_Make_Struct(
58
116
  cPinMarker,
59
- containers_pin_marker_t,
117
+ containers_marker_t,
60
118
  &containers_pin_marker_type,
61
119
  marker_data
62
120
  );
63
- marker_data->reserved = 0;
121
+ marker_data->owner_class = Qnil;
122
+ RB_OBJ_WRITE(marker, &marker_data->owner_class, owner_class);
64
123
  rb_obj_freeze(marker);
65
124
  return marker;
66
125
  }
67
126
 
68
127
  static VALUE
69
- containers_new_copy_marker(void)
128
+ containers_marker_reject_freeze(VALUE marker)
129
+ {
130
+ rb_raise(eRactorError, "%"PRIsVALUE" cannot be made Ractor-shareable", containers_marker_owner_class(marker));
131
+ }
132
+
133
+ static VALUE
134
+ containers_noncopyable_marker_new(VALUE klass, VALUE owner_class)
70
135
  {
71
- VALUE pair = rb_funcall(rb_cIO, pipe_id, 0);
136
+ VALUE pair = rb_funcall(klass, pipe_id, 0);
72
137
  VALUE reader = rb_ary_entry(pair, 0);
73
138
  VALUE writer = rb_ary_entry(pair, 1);
74
139
 
75
140
  rb_funcall(reader, close_id, 0);
76
141
  rb_funcall(writer, close_id, 0);
77
- return reader;
142
+ return containers_marker_initialize(reader, owner_class);
143
+ }
144
+
145
+ static VALUE
146
+ containers_nonmovable_marker_allocate(VALUE klass)
147
+ {
148
+ containers_marker_t *marker_data;
149
+ VALUE marker = TypedData_Make_Struct(
150
+ klass,
151
+ containers_marker_t,
152
+ &containers_nonmovable_marker_type,
153
+ marker_data
154
+ );
155
+ marker_data->owner_class = Qnil;
156
+ return marker;
157
+ }
158
+
159
+ static VALUE
160
+ containers_nonmovable_marker_initialize(VALUE marker, VALUE owner_class)
161
+ {
162
+ containers_marker_t *marker_data;
163
+ Check_Type(owner_class, T_CLASS);
164
+ TypedData_Get_Struct(marker, containers_marker_t, &containers_nonmovable_marker_type, marker_data);
165
+ RB_OBJ_WRITE(marker, &marker_data->owner_class, owner_class);
166
+ return marker;
167
+ }
168
+
169
+ static VALUE
170
+ containers_nonmovable_marker_initialize_copy(VALUE marker, VALUE original)
171
+ {
172
+ containers_marker_t *original_data;
173
+ TypedData_Get_Struct(original, containers_marker_t, &containers_nonmovable_marker_type, original_data);
174
+ return containers_nonmovable_marker_initialize(marker, original_data->owner_class);
78
175
  }
79
176
 
80
177
  static VALUE
@@ -91,7 +188,7 @@ containers_unshareable_pin(VALUE module, VALUE object)
91
188
  if (containers_pin_marker_p(marker)) return object;
92
189
 
93
190
  containers_check_guard_target(object);
94
- rb_ivar_set(object, pin_marker_id, containers_new_pin_marker());
191
+ rb_ivar_set(object, pin_marker_id, containers_new_pin_marker(rb_obj_class(object)));
95
192
  return object;
96
193
  }
97
194
 
@@ -110,7 +207,7 @@ containers_unshareable_prevent_copyable(VALUE module, VALUE object)
110
207
  if (rb_obj_is_kind_of(marker, rb_cIO)) return object;
111
208
 
112
209
  containers_check_guard_target(object);
113
- rb_ivar_set(object, copy_marker_id, containers_new_copy_marker());
210
+ rb_ivar_set(object, copy_marker_id, rb_funcall(cNoncopyableMarker, new_id, 1, rb_obj_class(object)));
114
211
  return object;
115
212
  }
116
213
 
@@ -126,10 +223,30 @@ containers_unshareable_prevent_movable(VALUE module, VALUE object)
126
223
 
127
224
  if (containers_pin_marker_p(rb_ivar_get(object, pin_marker_id))) return object;
128
225
  marker = rb_ivar_get(object, move_marker_id);
129
- if (rb_obj_is_kind_of(marker, rb_cTime)) return object;
226
+ if (rb_typeddata_is_kind_of(marker, &containers_nonmovable_marker_type)) return object;
130
227
 
131
228
  containers_check_guard_target(object);
132
- rb_ivar_set(object, move_marker_id, rb_time_new(0, 0));
229
+ rb_ivar_set(object, move_marker_id, rb_funcall(cNonmovableMarker, new_id, 1, rb_obj_class(object)));
230
+ return object;
231
+ }
232
+
233
+ static VALUE
234
+ containers_unshareable_prevent_shareable(VALUE module, VALUE object)
235
+ {
236
+ VALUE marker;
237
+ (void)module;
238
+
239
+ if (RB_SPECIAL_CONST_P(object)) {
240
+ rb_raise(rb_eTypeError, "cannot guard an immediate value");
241
+ }
242
+
243
+ if (containers_pin_marker_p(rb_ivar_get(object, pin_marker_id))) return object;
244
+ marker = rb_ivar_get(object, share_marker_id);
245
+ if (rb_obj_is_kind_of(marker, cUnshareableMarker)) return object;
246
+
247
+ containers_check_guard_target(object);
248
+ VALUE owner_class = rb_obj_class(object);
249
+ rb_ivar_set(object, share_marker_id, rb_class_new_instance(1, &owner_class, cUnshareableMarker));
133
250
  return object;
134
251
  }
135
252
 
@@ -141,15 +258,47 @@ containers_init_unshareable(VALUE namespace)
141
258
  pin_marker_id = rb_intern("__ractor_containers_pin__");
142
259
  copy_marker_id = rb_intern("__ractor_containers_prevent_copy__");
143
260
  move_marker_id = rb_intern("__ractor_containers_prevent_move__");
261
+ share_marker_id = rb_intern("__ractor_containers_prevent_share__");
262
+ owner_class_id = rb_intern("@owner_class");
263
+ new_id = rb_intern("new");
144
264
  pipe_id = rb_intern("pipe");
145
265
  close_id = rb_intern("close");
266
+ eRactorError = rb_const_get(rb_cRactor, rb_intern("Error"));
146
267
  eIsolationError = rb_const_get(rb_cRactor, rb_intern("IsolationError"));
147
268
 
148
269
  cPinMarker = rb_class_new(rb_cObject);
149
270
  rb_undef_alloc_func(cPinMarker);
271
+ rb_define_method(cPinMarker, "inspect", containers_marker_inspect, 0);
272
+ rb_define_method(cPinMarker, "to_s", containers_marker_inspect, 0);
150
273
  rb_global_variable(&cPinMarker);
151
274
 
275
+ /* Keep the native transfer restrictions while rejecting recursive freezing. */
276
+ cNoncopyableMarker = rb_define_class_under(module, "NoncopyableMarker", rb_cIO);
277
+ rb_define_singleton_method(cNoncopyableMarker, "new", containers_noncopyable_marker_new, 1);
278
+ rb_define_method(cNoncopyableMarker, "freeze", containers_marker_reject_freeze, 0);
279
+ rb_define_method(cNoncopyableMarker, "inspect", containers_marker_inspect, 0);
280
+ rb_define_method(cNoncopyableMarker, "to_s", containers_marker_inspect, 0);
281
+ cNonmovableMarker = rb_define_class_under(module, "NonmovableMarker", rb_cObject);
282
+ rb_define_alloc_func(cNonmovableMarker, containers_nonmovable_marker_allocate);
283
+ rb_define_method(cNonmovableMarker, "initialize", containers_nonmovable_marker_initialize, 1);
284
+ rb_define_method(cNonmovableMarker, "initialize_copy", containers_nonmovable_marker_initialize_copy, 1);
285
+ rb_define_method(cNonmovableMarker, "inspect", containers_marker_inspect, 0);
286
+ rb_define_method(cNonmovableMarker, "to_s", containers_marker_inspect, 0);
287
+ rb_define_method(cNonmovableMarker, "freeze", containers_marker_reject_freeze, 0);
288
+
289
+ /* Ruby 4.1's Ractor copier falls back to Marshal for extension data. */
290
+ rb_define_method(cNonmovableMarker, "marshal_dump", containers_marker_owner_class, 0);
291
+ rb_define_method(cNonmovableMarker, "marshal_load", containers_nonmovable_marker_initialize, 1);
292
+ cUnshareableMarker = rb_define_class_under(module, "UnshareableMarker", rb_cObject);
293
+ rb_define_method(cUnshareableMarker, "initialize", containers_marker_initialize, 1);
294
+ rb_define_method(cUnshareableMarker, "freeze", containers_marker_reject_freeze, 0);
295
+ rb_funcall(module, rb_intern("private_constant"), 3,
296
+ ID2SYM(rb_intern("NoncopyableMarker")),
297
+ ID2SYM(rb_intern("NonmovableMarker")),
298
+ ID2SYM(rb_intern("UnshareableMarker")));
299
+
152
300
  rb_define_singleton_method(module, "pin_to_current_ractor", containers_unshareable_pin, 1);
153
301
  rb_define_singleton_method(module, "prevent_copyable", containers_unshareable_prevent_copyable, 1);
154
302
  rb_define_singleton_method(module, "prevent_movable", containers_unshareable_prevent_movable, 1);
303
+ rb_define_singleton_method(module, "prevent_shareable", containers_unshareable_prevent_shareable, 1);
155
304
  }
@@ -40,8 +40,17 @@ static void
40
40
  farce_io_pool_mark(farce_io_pool_t *pool)
41
41
  {
42
42
  for (farce_io_slot_t *slot = pool->all; slot; slot = slot->all_next) {
43
- rb_gc_mark(slot->reader);
44
- rb_gc_mark(slot->writer);
43
+ rb_gc_mark_movable(slot->reader);
44
+ rb_gc_mark_movable(slot->writer);
45
+ }
46
+ }
47
+
48
+ static void
49
+ farce_io_pool_compact(farce_io_pool_t *pool)
50
+ {
51
+ for (farce_io_slot_t *slot = pool->all; slot; slot = slot->all_next) {
52
+ containers_gc_update(&slot->reader);
53
+ containers_gc_update(&slot->writer);
45
54
  }
46
55
  }
47
56
 
@@ -52,7 +61,7 @@ farce_io_pool_free(farce_io_pool_t *pool)
52
61
  while (slot) {
53
62
  farce_io_slot_t *next = slot->all_next;
54
63
  /* Ruby finalizes the IO objects and their owned descriptors. */
55
- free(slot);
64
+ ruby_xfree(slot);
56
65
  slot = next;
57
66
  }
58
67
  }
@@ -78,7 +87,7 @@ farce_io_pool_discard(farce_io_pool_t *pool, farce_io_slot_t *slot)
78
87
  pool->size--;
79
88
  VALUE reader = slot->reader;
80
89
  VALUE writer = slot->writer;
81
- free(slot);
90
+ ruby_xfree(slot);
82
91
  /* Cleanup must close both ends without masking the original exception. */
83
92
  VALUE error = rb_errinfo();
84
93
  int state;
@@ -92,7 +101,7 @@ farce_io_pool_discard(farce_io_pool_t *pool, farce_io_slot_t *slot)
92
101
  }
93
102
 
94
103
  static farce_io_slot_t *
95
- farce_io_pool_acquire(farce_io_pool_t *pool)
104
+ farce_io_pool_acquire(VALUE self, farce_io_pool_t *pool)
96
105
  {
97
106
  if (pool->idle) {
98
107
  farce_io_slot_t *slot = pool->idle;
@@ -116,10 +125,9 @@ farce_io_pool_acquire(farce_io_pool_t *pool)
116
125
  fcntl(read_fd, F_SETFL, read_flags | O_NONBLOCK) < 0 ||
117
126
  fcntl(write_fd, F_SETFL, write_flags | O_NONBLOCK) < 0) rb_sys_fail("fcntl");
118
127
  #endif
119
- farce_io_slot_t *slot = calloc(1, sizeof(*slot));
120
- if (!slot) rb_memerror();
121
- slot->reader = reader;
122
- slot->writer = writer;
128
+ farce_io_slot_t *slot = ZALLOC(farce_io_slot_t);
129
+ RB_OBJ_WRITE(self, &slot->reader, reader);
130
+ RB_OBJ_WRITE(self, &slot->writer, writer);
123
131
  slot->read_fd = read_fd;
124
132
  slot->write_fd = write_fd;
125
133
  slot->all_next = pool->all;
@@ -170,6 +178,7 @@ farce_io_pool_notify(farce_io_pool_t *pool)
170
178
  }
171
179
 
172
180
  typedef struct {
181
+ VALUE self;
173
182
  farce_io_pool_t *pool;
174
183
  farce_io_slot_t *slot;
175
184
  bool (*ready)(void *);
@@ -184,7 +193,7 @@ static VALUE
184
193
  farce_io_pool_wait_body(VALUE opaque)
185
194
  {
186
195
  farce_io_pool_wait_t *wait = (farce_io_pool_wait_t *)opaque;
187
- wait->slot = farce_io_pool_acquire(wait->pool);
196
+ wait->slot = farce_io_pool_acquire(wait->self, wait->pool);
188
197
  /* Acquiring a new pipe may run Ruby. Recheck the caller's condition before
189
198
  * registering under the GVL, including notifications during allocation. */
190
199
  if (wait->ready(wait->data)) { wait->completed = true; return Qtrue; }
@@ -225,9 +234,9 @@ farce_io_pool_wait_cleanup(VALUE opaque)
225
234
  }
226
235
 
227
236
  static bool
228
- farce_io_pool_wait(farce_io_pool_t *pool, bool finite, double deadline, bool (*ready)(void *), void *data)
237
+ farce_io_pool_wait(VALUE self, farce_io_pool_t *pool, bool finite, double deadline, bool (*ready)(void *), void *data)
229
238
  {
230
- farce_io_pool_wait_t wait = {.pool = pool, .finite = finite, .deadline = deadline, .ready = ready, .data = data};
239
+ farce_io_pool_wait_t wait = {.self = self, .pool = pool, .finite = finite, .deadline = deadline, .ready = ready, .data = data};
231
240
  return RTEST(rb_ensure(farce_io_pool_wait_body, (VALUE)&wait, farce_io_pool_wait_cleanup, (VALUE)&wait));
232
241
  }
233
242
 
@@ -21,6 +21,14 @@ unshared_signal_mark(void *pointer)
21
21
  farce_io_pool_mark(&signal->fiber_pool);
22
22
  }
23
23
 
24
+ /* Wait-list VALUEs are pinned by suspended C frames. Cached IOs can move. */
25
+ static void
26
+ unshared_signal_compact(void *pointer)
27
+ {
28
+ containers_unshared_signal_t *signal = pointer;
29
+ farce_io_pool_compact(&signal->fiber_pool);
30
+ }
31
+
24
32
  static void
25
33
  unshared_signal_free(void *pointer)
26
34
  {
@@ -42,7 +50,9 @@ static const rb_data_type_t unshared_signal_type = {
42
50
  .dmark = unshared_signal_mark,
43
51
  .dfree = unshared_signal_free,
44
52
  .dsize = unshared_signal_memsize,
53
+ .dcompact = unshared_signal_compact,
45
54
  },
55
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE,
46
56
  };
47
57
 
48
58
  static VALUE
@@ -170,6 +180,7 @@ unshared_signal_broadcast(VALUE self)
170
180
  }
171
181
 
172
182
  typedef struct {
183
+ VALUE self;
173
184
  containers_unshared_signal_t *signal;
174
185
  uint64_t observed;
175
186
  double deadline;
@@ -187,7 +198,7 @@ static VALUE
187
198
  unshared_signal_fiber_wait_body(VALUE opaque)
188
199
  {
189
200
  unshared_signal_fiber_wait_t *wait = (unshared_signal_fiber_wait_t *)opaque;
190
- return farce_io_pool_wait(&wait->signal->fiber_pool, wait->finite, wait->deadline,
201
+ return farce_io_pool_wait(wait->self, &wait->signal->fiber_pool, wait->finite, wait->deadline,
191
202
  unshared_signal_fiber_ready, wait) ? Qtrue : Qfalse;
192
203
  }
193
204
 
@@ -232,7 +243,7 @@ unshared_signal_wait(int argc, VALUE *argv, VALUE self)
232
243
  }
233
244
  if (signal->fiber_io && !NIL_P(rb_fiber_scheduler_current())) {
234
245
  /* Reuse owned pipe objects after each scheduler wait unwinds. */
235
- unshared_signal_fiber_wait_t wait = {signal, observed, deadline, finite};
246
+ unshared_signal_fiber_wait_t wait = {self, signal, observed, deadline, finite};
236
247
  signal->fiber_waiters++;
237
248
  rb_ensure(unshared_signal_fiber_wait_body, (VALUE)&wait,
238
249
  unshared_signal_fiber_wait_cleanup, (VALUE)&wait);
@@ -139,6 +139,9 @@ farce_unshared_wait(farce_unshared_wait_list_t *list, VALUE blocker, bool finite
139
139
  .linked = true,
140
140
  .notified = false,
141
141
  };
142
+ RB_OBJ_WRITTEN(blocker, Qundef, waiter.thread);
143
+ RB_OBJ_WRITTEN(blocker, Qundef, waiter.scheduler);
144
+ RB_OBJ_WRITTEN(blocker, Qundef, waiter.fiber);
142
145
  if (list->last) list->last->next = &waiter;
143
146
  else list->first = &waiter;
144
147
  list->last = &waiter;
data/ext/farce/vector.c CHANGED
@@ -60,11 +60,11 @@ static void
60
60
  vector_compact(void *pointer)
61
61
  {
62
62
  vector_t *vector = pointer;
63
- vector->updating_fiber = rb_gc_location(vector->updating_fiber);
64
- vector->updating_thread = rb_gc_location(vector->updating_thread);
63
+ containers_gc_update(&vector->updating_fiber);
64
+ containers_gc_update(&vector->updating_thread);
65
65
  if (!vector->values) return;
66
66
  for (size_t index = 0; index < vector->size; index++) {
67
- vector->values[index] = rb_gc_location(vector->values[index]);
67
+ containers_gc_update(&vector->values[index]);
68
68
  }
69
69
  }
70
70
 
@@ -92,7 +92,11 @@ static const rb_data_type_t vector_type = {
92
92
  .dsize = vector_memsize,
93
93
  .dcompact = vector_compact,
94
94
  },
95
- .flags = RUBY_TYPED_FROZEN_SHAREABLE,
95
+ .flags =
96
+ RUBY_TYPED_WB_PROTECTED |
97
+ RUBY_TYPED_FROZEN_SHAREABLE |
98
+ RUBY_TYPED_FREE_IMMEDIATELY |
99
+ FARCE_TYPED_THREAD_SAFE_FREE,
96
100
  };
97
101
 
98
102
  static void
@@ -133,6 +137,8 @@ get_vector(VALUE self)
133
137
  return vector;
134
138
  }
135
139
 
140
+ /* Buffer growth also runs under the state mutex. Keep the allocator pair
141
+ * consistent and avoid Ruby allocators that can trigger GC in that section. */
136
142
  static VALUE *
137
143
  vector_allocate_values(size_t capacity)
138
144
  {
@@ -455,9 +461,9 @@ vector_initialize(int argc, VALUE *argv, VALUE self)
455
461
  vector->capacity = capacity;
456
462
 
457
463
  for (size_t index = 0; index < size; index++) {
458
- VALUE value = RARRAY_AREF(source, (long)index);
464
+ VALUE value = rb_ary_entry(source, (long)index);
459
465
  containers_check_shareable(value);
460
- vector->values[index] = value;
466
+ RB_OBJ_WRITE(self, &vector->values[index], value);
461
467
  }
462
468
  vector->size = size;
463
469
  vector->initialized = true;
@@ -604,7 +610,7 @@ vector_store_internal(VALUE self, vector_t *vector, long long raw, VALUE value,
604
610
  rb_memerror();
605
611
  }
606
612
  if (index >= vector->size) vector->size = index + 1;
607
- vector->values[index] = value;
613
+ RB_OBJ_WRITE(self, &vector->values[index], value);
608
614
  vector_notify_waiters_locked(vector);
609
615
  pthread_mutex_unlock(&vector->lock);
610
616
  return value;
@@ -669,7 +675,7 @@ vector_push(int argc, VALUE *argv, VALUE self)
669
675
  pthread_mutex_unlock(&vector->lock);
670
676
  rb_memerror();
671
677
  }
672
- vector->values[vector->size++] = value;
678
+ RB_OBJ_WRITE(self, &vector->values[vector->size++], value);
673
679
  vector_notify_waiters_locked(vector);
674
680
  pthread_mutex_unlock(&vector->lock);
675
681
  return self;
@@ -719,7 +725,7 @@ vector_swap(int argc, VALUE *argv, VALUE self)
719
725
  }
720
726
  VALUE previous = index < vector->size ? vector->values[index] : Qnil;
721
727
  if (index >= vector->size) vector->size = index + 1;
722
- vector->values[index] = replacement;
728
+ RB_OBJ_WRITE(self, &vector->values[index], replacement);
723
729
  vector_notify_waiters_locked(vector);
724
730
  pthread_mutex_unlock(&vector->lock);
725
731
  return previous;
@@ -759,7 +765,7 @@ vector_store_if_absent_body(VALUE opaque)
759
765
  if (operation->index >= operation->vector->size) {
760
766
  operation->vector->size = operation->index + 1;
761
767
  }
762
- operation->vector->values[operation->index] = result;
768
+ RB_OBJ_WRITE(operation->self, &operation->vector->values[operation->index], result);
763
769
  vector_finish_update_locked(operation->vector);
764
770
  pthread_mutex_unlock(&operation->vector->lock);
765
771
  operation->complete = true;
@@ -817,7 +823,7 @@ vector_cas_body(VALUE opaque)
817
823
  rb_check_frozen(operation->self);
818
824
 
819
825
  pthread_mutex_lock(&operation->vector->lock);
820
- if (matches) operation->vector->values[operation->index] = operation->replacement;
826
+ if (matches) RB_OBJ_WRITE(operation->self, &operation->vector->values[operation->index], operation->replacement);
821
827
  vector_finish_update_locked(operation->vector);
822
828
  pthread_mutex_unlock(&operation->vector->lock);
823
829
  operation->complete = true;
@@ -869,7 +875,7 @@ vector_upsert_body(VALUE opaque)
869
875
  rb_check_frozen(operation->self);
870
876
  containers_check_shareable(result);
871
877
  pthread_mutex_lock(&operation->vector->lock);
872
- operation->vector->values[operation->index] = result;
878
+ RB_OBJ_WRITE(operation->self, &operation->vector->values[operation->index], result);
873
879
  vector_finish_update_locked(operation->vector);
874
880
  pthread_mutex_unlock(&operation->vector->lock);
875
881
  operation->complete = true;
@@ -899,7 +905,7 @@ vector_upsert(int argc, VALUE *argv, VALUE self)
899
905
  }
900
906
  if (index >= vector->size) vector->size = index + 1;
901
907
  if (NIL_P(vector->values[index])) {
902
- vector->values[index] = initial;
908
+ RB_OBJ_WRITE(self, &vector->values[index], initial);
903
909
  vector_notify_waiters_locked(vector);
904
910
  pthread_mutex_unlock(&vector->lock);
905
911
  return initial;
@@ -928,7 +934,7 @@ vector_update_body(VALUE opaque)
928
934
  if (operation->index >= operation->vector->size) {
929
935
  operation->vector->size = operation->index + 1;
930
936
  }
931
- operation->vector->values[operation->index] = result;
937
+ RB_OBJ_WRITE(operation->self, &operation->vector->values[operation->index], result);
932
938
  vector_finish_update_locked(operation->vector);
933
939
  pthread_mutex_unlock(&operation->vector->lock);
934
940
  operation->complete = true;
@@ -967,6 +973,60 @@ vector_update(int argc, VALUE *argv, VALUE self)
967
973
  return rb_ensure(vector_update_body, (VALUE)&operation, vector_operation_cleanup, (VALUE)&operation);
968
974
  }
969
975
 
976
+ static VALUE
977
+ vector_batch_update_body(VALUE opaque)
978
+ {
979
+ vector_operation_t *operation = (vector_operation_t *)opaque;
980
+ VALUE values = vector_snapshot(operation->self);
981
+ rb_yield(values);
982
+ rb_check_frozen(operation->self);
983
+ size_t size = (size_t)RARRAY_LEN(values);
984
+ for (size_t index = 0; index < size; index++) {
985
+ containers_check_shareable(rb_ary_entry(values, (long)index));
986
+ }
987
+ pthread_mutex_lock(&operation->vector->lock);
988
+ if (RB_OBJ_FROZEN(operation->self)) {
989
+ pthread_mutex_unlock(&operation->vector->lock);
990
+ rb_check_frozen(operation->self);
991
+ }
992
+ if (!vector_ensure_capacity(operation->vector, size)) {
993
+ pthread_mutex_unlock(&operation->vector->lock);
994
+ rb_memerror();
995
+ }
996
+ for (size_t index = 0; index < size; index++) {
997
+ RB_OBJ_WRITE(operation->self, &operation->vector->values[index], rb_ary_entry(values, (long)index));
998
+ }
999
+ for (size_t index = size; index < operation->vector->size; index++) {
1000
+ operation->vector->values[index] = Qnil;
1001
+ }
1002
+ operation->vector->size = size;
1003
+ vector_finish_update_locked(operation->vector);
1004
+ pthread_mutex_unlock(&operation->vector->lock);
1005
+ operation->complete = true;
1006
+ RB_GC_GUARD(values);
1007
+ return Qtrue;
1008
+ }
1009
+
1010
+ static VALUE
1011
+ vector_batch_update(int argc, VALUE *argv, VALUE self)
1012
+ {
1013
+ VALUE keywords = Qnil;
1014
+ rb_scan_args(argc, argv, "0:", &keywords);
1015
+ rb_check_frozen(self);
1016
+ rb_need_block();
1017
+ vector_timeout_t timeout = vector_parse_timeout(vector_timeout_keyword(keywords));
1018
+ vector_t *vector = get_vector(self);
1019
+ if (!vector_lock_for_update(vector, &timeout)) return Qfalse;
1020
+ if (RB_OBJ_FROZEN(self)) {
1021
+ pthread_mutex_unlock(&vector->lock);
1022
+ rb_check_frozen(self);
1023
+ }
1024
+ vector_operation_t operation = { .self = self, .vector = vector, .complete = false };
1025
+ vector->updating = true;
1026
+ pthread_mutex_unlock(&vector->lock);
1027
+ return rb_ensure(vector_batch_update_body, (VALUE)&operation, vector_operation_cleanup, (VALUE)&operation);
1028
+ }
1029
+
970
1030
  static bool
971
1031
  vector_values_equal(vector_t *vector, VALUE left, VALUE right)
972
1032
  {
@@ -1055,7 +1115,7 @@ unshared_vector_each(VALUE self)
1055
1115
  Check_Type(values, T_ARRAY);
1056
1116
  long limit = RARRAY_LEN(values);
1057
1117
  for (long index = 0; index < limit && index < RARRAY_LEN(values); index++) {
1058
- rb_yield(RARRAY_AREF(values, index));
1118
+ rb_yield(rb_ary_entry(values, index));
1059
1119
  }
1060
1120
  RB_GC_GUARD(values);
1061
1121
  return self;
@@ -1071,7 +1131,7 @@ unshared_vector_reverse_each(VALUE self)
1071
1131
  while (index > 0) {
1072
1132
  if (index > RARRAY_LEN(values)) index = RARRAY_LEN(values);
1073
1133
  if (index == 0) break;
1074
- rb_yield(RARRAY_AREF(values, --index));
1134
+ rb_yield(rb_ary_entry(values, --index));
1075
1135
  }
1076
1136
  RB_GC_GUARD(values);
1077
1137
  return self;
@@ -1097,6 +1157,9 @@ vector_transaction_apply(farce_transaction_entry_t *entry)
1097
1157
  copy->values = values;
1098
1158
  copy->capacity = capacity;
1099
1159
  copy->size = size;
1160
+ for (size_t i = 0; i < vector->size; i++) {
1161
+ RB_OBJ_WRITTEN(entry->source, Qundef, vector->values[i]);
1162
+ }
1100
1163
  }
1101
1164
 
1102
1165
  static void
@@ -1110,8 +1173,8 @@ vector_transaction_reserve(farce_transaction_entry_t *entry, VALUE fiber, VALUE
1110
1173
  {
1111
1174
  vector_t *vector = entry->source_data;
1112
1175
  vector->updating = FARCE_TRANSACTION_RESERVED;
1113
- vector->updating_fiber = fiber;
1114
- vector->updating_thread = thread;
1176
+ RB_OBJ_WRITE(entry->source, &vector->updating_fiber, fiber);
1177
+ RB_OBJ_WRITE(entry->source, &vector->updating_thread, thread);
1115
1178
  }
1116
1179
 
1117
1180
  static void
@@ -1177,6 +1240,7 @@ containers_init_vector(VALUE namespace)
1177
1240
  rb_define_method(cVector, "reverse_each", vector_reverse_each, 0);
1178
1241
  rb_define_method(cVector, "fetch", vector_fetch, 1);
1179
1242
  rb_define_method(cVector, "clear", vector_clear, 0);
1243
+ rb_define_method(cVector, "batch_update", vector_batch_update, -1);
1180
1244
  rb_define_method(cVector, "[]", vector_get_fast, 1);
1181
1245
  rb_define_method(cVector, "[]=", vector_set_fast, 2);
1182
1246
  rb_define_method(cVector, "get", vector_get, -1);