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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +80 -1
- data/README.md +72 -16
- data/docs/gems/dry-types.md +8 -8
- data/docs/modes.md +67 -10
- data/ext/farce/atom.c +37 -22
- data/ext/farce/bounded_map.c +17 -13
- data/ext/farce/containers.h +15 -0
- data/ext/farce/counter.c +13 -18
- data/ext/farce/exchanger.c +5 -1
- data/ext/farce/farce.c +1 -1
- data/ext/farce/flag.c +39 -3
- data/ext/farce/io.c +3 -3
- data/ext/farce/lock.c +17 -23
- data/ext/farce/map.c +88 -66
- data/ext/farce/priority_queue.c +55 -43
- data/ext/farce/queue.c +10 -4
- data/ext/farce/reactor.c +52 -25
- data/ext/farce/reactor.h +1 -1
- data/ext/farce/signal.c +5 -1
- data/ext/farce/transaction.c +20 -24
- data/ext/farce/tree_map.c +33 -18
- data/ext/farce/trie.c +81 -43
- data/ext/farce/unshareable.c +168 -19
- data/ext/farce/unshared_io_pool.h +21 -12
- data/ext/farce/unshared_signal.c +13 -2
- data/ext/farce/unshared_wait.h +3 -0
- data/ext/farce/vector.c +82 -18
- data/ext/farce/weak_map.c +75 -63
- data/lib/farce/_yard/macros.rb +18 -3
- data/lib/farce/abstract/flag.rb +73 -0
- data/lib/farce/abstract/queue.rb +4 -3
- data/lib/farce/abstract/scheduler.rb +3 -3
- data/lib/farce/abstract/set.rb +17 -5
- data/lib/farce/abstract/sorted_set.rb +2 -2
- data/lib/farce/abstract/timer_queue.rb +49 -10
- data/lib/farce/abstract/vector.rb +510 -28
- data/lib/farce/atom.rb +1 -1
- data/lib/farce/class_mirror.rb +20 -7
- data/lib/farce/config.rb +7 -1
- data/lib/farce/engine/jvm/flag.rb +15 -3
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/shared/require_hook.rb +18 -0
- data/lib/farce/engine/ruby.rb +7 -0
- data/lib/farce/engine/shared/unshared_vector.rb +31 -4
- data/lib/farce/engine/shared/vector.rb +8 -0
- data/lib/farce/engine/shared/weak_map/index.rb +14 -1
- data/lib/farce/engine/truffleruby/native/flag.rb +15 -3
- data/lib/farce/envelope.rb +54 -7
- data/lib/farce/exchanger.rb +1 -1
- data/lib/farce/flag.rb +2 -0
- data/lib/farce/integrations/active_support.rb +1 -1
- data/lib/farce/integrations/bson.rb +1 -1
- data/lib/farce/integrations/cbor.rb +1 -1
- data/lib/farce/integrations/concurrent.rb +3 -3
- data/lib/farce/integrations/date.rb +8 -0
- data/lib/farce/integrations/dry_types.rb +7 -4
- data/lib/farce/integrations/json.rb +2 -2
- data/lib/farce/integrations/msgpack.rb +1 -1
- data/lib/farce/integrations/oj.rb +3 -4
- data/lib/farce/integrations/psych.rb +1 -1
- data/lib/farce/integrations/ractor_sharing.rb +1 -1
- data/lib/farce/integrations/sorted_set.rb +1 -1
- data/lib/farce/integrations/weakref.rb +9 -1
- data/lib/farce/integrations/yajl.rb +3 -3
- data/lib/farce/integrations.rb +33 -23
- data/lib/farce/internal/flag_waiting.rb +30 -0
- data/lib/farce/internal/freeze.rb +8 -3
- data/lib/farce/internal/map_value_modes.rb +1 -1
- data/lib/farce/internal/scheduler_lifecycle.rb +1 -1
- data/lib/farce/lazy.rb +4 -4
- data/lib/farce/lazy_ref.rb +3 -3
- data/lib/farce/lfu_map.rb +1 -1
- data/lib/farce/local/flag.rb +4 -0
- data/lib/farce/local/scoped.rb +1 -1
- data/lib/farce/lru_map.rb +1 -1
- data/lib/farce/map.rb +2 -2
- data/lib/farce/mode_manager/smart_rule.rb +138 -0
- data/lib/farce/mode_manager.rb +12 -3
- data/lib/farce/molecule.rb +2 -2
- data/lib/farce/pool.rb +1 -1
- data/lib/farce/port.rb +4 -4
- data/lib/farce/priority_queue.rb +1 -1
- data/lib/farce/proxy/register.rb +6 -0
- data/lib/farce/proxy/supervisor.rb +1 -1
- data/lib/farce/proxy.rb +7 -2
- data/lib/farce/queue.rb +1 -1
- data/lib/farce/scheduler.rb +2 -2
- data/lib/farce/set.rb +1 -1
- data/lib/farce/sorted_set.rb +1 -1
- data/lib/farce/timer_queue.rb +20 -6
- data/lib/farce/transaction/vector.rb +43 -9
- data/lib/farce/transaction/wrapper.rb +14 -0
- data/lib/farce/tree_map.rb +1 -1
- data/lib/farce/unsafe/lfu_map.rb +2 -0
- data/lib/farce/unsafe/lru_map.rb +2 -0
- data/lib/farce/unsafe/tree_map.rb +2 -0
- data/lib/farce/unshareable.rb +8 -19
- data/lib/farce/unshared/atom.rb +3 -0
- data/lib/farce/unshared/lfu_map.rb +2 -0
- data/lib/farce/unshared/lru_map.rb +2 -0
- data/lib/farce/unshared/map.rb +3 -0
- data/lib/farce/unshared/set.rb +1 -0
- data/lib/farce/unshared/sorted_set.rb +1 -0
- data/lib/farce/unshared/tree_map.rb +5 -1
- data/lib/farce/unshared/vector.rb +3 -0
- data/lib/farce/unshared/weak_atom.rb +5 -1
- data/lib/farce/unshared/weak_key_map.rb +3 -0
- data/lib/farce/unshared/weak_map.rb +3 -0
- data/lib/farce/unshared/weak_set.rb +1 -0
- data/lib/farce/unshared/weak_value_map.rb +3 -0
- data/lib/farce/vector.rb +44 -19
- data/lib/farce/version.rb +1 -1
- data/lib/farce.rb +11 -8
- metadata +5 -1
data/ext/farce/unshareable.c
CHANGED
|
@@ -1,34 +1,69 @@
|
|
|
1
1
|
#include "containers.h"
|
|
2
2
|
|
|
3
3
|
typedef struct {
|
|
4
|
-
|
|
5
|
-
}
|
|
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
|
-
|
|
37
|
+
containers_marker_size(const void *pointer)
|
|
17
38
|
{
|
|
18
|
-
return pointer == NULL ? 0 : sizeof(
|
|
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 =
|
|
45
|
+
.dmark = containers_marker_mark,
|
|
25
46
|
.dfree = RUBY_TYPED_DEFAULT_FREE,
|
|
26
|
-
.dsize =
|
|
27
|
-
.dcompact =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
+
containers_marker_t,
|
|
60
118
|
&containers_pin_marker_type,
|
|
61
119
|
marker_data
|
|
62
120
|
);
|
|
63
|
-
marker_data->
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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 (
|
|
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,
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
120
|
-
|
|
121
|
-
slot->
|
|
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
|
|
data/ext/farce/unshared_signal.c
CHANGED
|
@@ -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);
|
data/ext/farce/unshared_wait.h
CHANGED
|
@@ -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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
464
|
+
VALUE value = rb_ary_entry(source, (long)index);
|
|
459
465
|
containers_check_shareable(value);
|
|
460
|
-
vector->values[index]
|
|
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]
|
|
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++]
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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(
|
|
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(
|
|
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
|
|
1114
|
-
vector->updating_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);
|