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/weak_map.c
CHANGED
|
@@ -84,6 +84,7 @@ typedef struct {
|
|
|
84
84
|
} weak_map_timeout_t;
|
|
85
85
|
|
|
86
86
|
typedef struct {
|
|
87
|
+
VALUE self;
|
|
87
88
|
VALUE fiber;
|
|
88
89
|
VALUE thread;
|
|
89
90
|
VALUE scheduler;
|
|
@@ -186,21 +187,21 @@ static void
|
|
|
186
187
|
weak_map_compact(void *pointer)
|
|
187
188
|
{
|
|
188
189
|
weak_map_t *map = pointer;
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
containers_gc_update(&map->comparing_owner);
|
|
191
|
+
containers_gc_update(&map->comparing_thread);
|
|
191
192
|
for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
containers_gc_update(&entry->key);
|
|
194
|
+
containers_gc_update(&entry->current);
|
|
195
|
+
containers_gc_update(&entry->fiber);
|
|
196
|
+
containers_gc_update(&entry->thread);
|
|
196
197
|
}
|
|
197
198
|
if (!map->slots) return;
|
|
198
199
|
|
|
199
200
|
for (size_t index = 0; index < map->capacity; index++) {
|
|
200
201
|
weak_map_slot_t *slot = &map->slots[index];
|
|
201
202
|
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
containers_gc_update(&slot->key);
|
|
204
|
+
containers_gc_update(&slot->value);
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
207
|
|
|
@@ -257,7 +258,11 @@ static const rb_data_type_t weak_map_type = {
|
|
|
257
258
|
.dcompact = weak_map_compact,
|
|
258
259
|
.handle_weak_references = weak_map_handle_weak_references,
|
|
259
260
|
},
|
|
260
|
-
.flags =
|
|
261
|
+
.flags =
|
|
262
|
+
RUBY_TYPED_WB_PROTECTED |
|
|
263
|
+
RUBY_TYPED_FROZEN_SHAREABLE |
|
|
264
|
+
RUBY_TYPED_FREE_IMMEDIATELY |
|
|
265
|
+
FARCE_TYPED_THREAD_SAFE_FREE,
|
|
261
266
|
};
|
|
262
267
|
|
|
263
268
|
static void
|
|
@@ -273,6 +278,8 @@ weak_map_validate_references(VALUE self)
|
|
|
273
278
|
}
|
|
274
279
|
}
|
|
275
280
|
|
|
281
|
+
/* Buffer growth also runs under the state mutex. Keep the allocator pair
|
|
282
|
+
* consistent and avoid Ruby allocators that can trigger GC in that section. */
|
|
276
283
|
static weak_map_slot_t *
|
|
277
284
|
weak_map_allocate_slots(size_t capacity)
|
|
278
285
|
{
|
|
@@ -469,8 +476,8 @@ weak_map_keys_equal(
|
|
|
469
476
|
int state = 0;
|
|
470
477
|
|
|
471
478
|
map->comparing = true;
|
|
472
|
-
map->comparing_owner
|
|
473
|
-
map->comparing_thread
|
|
479
|
+
RB_OBJ_WRITE(execution->self, &map->comparing_owner, execution->fiber);
|
|
480
|
+
RB_OBJ_WRITE(execution->self, &map->comparing_thread, execution->thread);
|
|
474
481
|
pthread_mutex_unlock(&map->lock);
|
|
475
482
|
|
|
476
483
|
VALUE result = rb_protect(weak_map_eql_protected, (VALUE)&arguments, &state);
|
|
@@ -558,11 +565,11 @@ weak_map_begin_reservation_locked(
|
|
|
558
565
|
st_index_t hash, const weak_map_execution_context_t *execution
|
|
559
566
|
)
|
|
560
567
|
{
|
|
561
|
-
entry->key
|
|
562
|
-
entry->current
|
|
568
|
+
RB_OBJ_WRITE(execution->self, &entry->key, key);
|
|
569
|
+
RB_OBJ_WRITE(execution->self, &entry->current, current);
|
|
563
570
|
entry->hash = hash;
|
|
564
|
-
entry->fiber
|
|
565
|
-
entry->thread
|
|
571
|
+
RB_OBJ_WRITE(execution->self, &entry->fiber, execution->fiber);
|
|
572
|
+
RB_OBJ_WRITE(execution->self, &entry->thread, execution->thread);
|
|
566
573
|
entry->invalidated = false;
|
|
567
574
|
entry->next = map->reservations;
|
|
568
575
|
map->reservations = entry;
|
|
@@ -726,7 +733,7 @@ weak_map_initialize_entry(VALUE key, VALUE value, VALUE opaque)
|
|
|
726
733
|
key = weak_map_prepare_key(context->map, key);
|
|
727
734
|
containers_check_shareable(value);
|
|
728
735
|
st_index_t hash = weak_map_key_hash(context->map, key);
|
|
729
|
-
weak_map_execution_context_t execution;
|
|
736
|
+
weak_map_execution_context_t execution = {.self = context->self};
|
|
730
737
|
weak_map_lock_state(context->map, &execution);
|
|
731
738
|
weak_map_store_locked(context->self, context->map, key, value, hash, &execution);
|
|
732
739
|
pthread_mutex_unlock(&context->map->lock);
|
|
@@ -782,7 +789,7 @@ weak_map_get(VALUE self, VALUE key)
|
|
|
782
789
|
st_index_t hash = weak_map_key_hash(map, key);
|
|
783
790
|
bool found;
|
|
784
791
|
VALUE result = Qnil;
|
|
785
|
-
weak_map_execution_context_t execution;
|
|
792
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
786
793
|
weak_map_lock_state(map, &execution);
|
|
787
794
|
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
788
795
|
if (found) result = map->slots[index].value;
|
|
@@ -827,7 +834,7 @@ weak_map_fetch(int argc, VALUE *argv, VALUE self)
|
|
|
827
834
|
st_index_t hash = weak_map_key_hash(map, key);
|
|
828
835
|
bool found;
|
|
829
836
|
VALUE result = Qnil;
|
|
830
|
-
weak_map_execution_context_t execution;
|
|
837
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
831
838
|
weak_map_lock_state(map, &execution);
|
|
832
839
|
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
833
840
|
if (found) result = map->slots[index].value;
|
|
@@ -849,7 +856,7 @@ weak_map_set(VALUE self, VALUE key, VALUE value)
|
|
|
849
856
|
containers_check_shareable(value);
|
|
850
857
|
st_index_t hash = weak_map_key_hash(map, key);
|
|
851
858
|
rb_check_frozen(self);
|
|
852
|
-
weak_map_execution_context_t execution;
|
|
859
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
853
860
|
weak_map_lock_for_key(map, key, hash, &execution);
|
|
854
861
|
weak_map_store_locked(self, map, key, value, hash, &execution);
|
|
855
862
|
pthread_mutex_unlock(&map->lock);
|
|
@@ -863,7 +870,7 @@ weak_map_key_p(VALUE self, VALUE key)
|
|
|
863
870
|
key = weak_map_prepare_key(map, key);
|
|
864
871
|
st_index_t hash = weak_map_key_hash(map, key);
|
|
865
872
|
bool found;
|
|
866
|
-
weak_map_execution_context_t execution;
|
|
873
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
867
874
|
weak_map_lock_state(map, &execution);
|
|
868
875
|
(void)weak_map_find_slot(map, key, hash, &found, &execution);
|
|
869
876
|
pthread_mutex_unlock(&map->lock);
|
|
@@ -878,7 +885,7 @@ weak_map_getkey(VALUE self, VALUE key)
|
|
|
878
885
|
st_index_t hash = weak_map_key_hash(map, key);
|
|
879
886
|
bool found;
|
|
880
887
|
VALUE result = Qnil;
|
|
881
|
-
weak_map_execution_context_t execution;
|
|
888
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
882
889
|
weak_map_lock_state(map, &execution);
|
|
883
890
|
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
884
891
|
if (found) result = map->compare_keys_by_identity ? key : map->slots[index].key;
|
|
@@ -896,7 +903,7 @@ weak_map_delete(VALUE self, VALUE key)
|
|
|
896
903
|
rb_check_frozen(self);
|
|
897
904
|
bool found;
|
|
898
905
|
VALUE result = Qnil;
|
|
899
|
-
weak_map_execution_context_t execution;
|
|
906
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
900
907
|
weak_map_lock_for_key(map, key, hash, &execution);
|
|
901
908
|
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
902
909
|
if (RB_OBJ_FROZEN(self)) {
|
|
@@ -922,7 +929,7 @@ weak_map_clear(VALUE self)
|
|
|
922
929
|
{
|
|
923
930
|
rb_check_frozen(self);
|
|
924
931
|
weak_map_t *map = get_weak_map(self);
|
|
925
|
-
weak_map_execution_context_t execution;
|
|
932
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
926
933
|
weak_map_lock_state(map, &execution);
|
|
927
934
|
for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
|
|
928
935
|
entry->invalidated = true;
|
|
@@ -961,7 +968,7 @@ weak_map_operation_cleanup(VALUE opaque)
|
|
|
961
968
|
{
|
|
962
969
|
weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
|
|
963
970
|
if (!operation->complete) {
|
|
964
|
-
weak_map_execution_context_t execution;
|
|
971
|
+
weak_map_execution_context_t execution = {.self = operation->self};
|
|
965
972
|
weak_map_lock_state(operation->map, &execution);
|
|
966
973
|
weak_map_finish_reservation_locked(operation->map, &operation->reservation);
|
|
967
974
|
weak_map_notify_waiters_locked(operation->map);
|
|
@@ -977,7 +984,7 @@ weak_map_store_body(VALUE opaque)
|
|
|
977
984
|
VALUE result = rb_yield_values(0);
|
|
978
985
|
containers_check_shareable(result);
|
|
979
986
|
rb_check_frozen(operation->self);
|
|
980
|
-
weak_map_execution_context_t execution;
|
|
987
|
+
weak_map_execution_context_t execution = {.self = operation->self};
|
|
981
988
|
weak_map_lock_state(operation->map, &execution);
|
|
982
989
|
bool stored = !operation->reservation.invalidated;
|
|
983
990
|
if (stored) {
|
|
@@ -1022,7 +1029,7 @@ weak_map_store_if_absent(int argc, VALUE *argv, VALUE self)
|
|
|
1022
1029
|
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1023
1030
|
);
|
|
1024
1031
|
|
|
1025
|
-
weak_map_execution_context_t execution;
|
|
1032
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1026
1033
|
if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
|
|
1027
1034
|
bool found;
|
|
1028
1035
|
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
@@ -1051,7 +1058,7 @@ weak_map_cas_body(VALUE opaque)
|
|
|
1051
1058
|
: RTEST(rb_equal(operation->current, operation->argument));
|
|
1052
1059
|
rb_check_frozen(operation->self);
|
|
1053
1060
|
|
|
1054
|
-
weak_map_execution_context_t execution;
|
|
1061
|
+
weak_map_execution_context_t execution = {.self = operation->self};
|
|
1055
1062
|
weak_map_lock_state(operation->map, &execution);
|
|
1056
1063
|
matches = matches && !operation->reservation.invalidated;
|
|
1057
1064
|
if (matches) {
|
|
@@ -1103,7 +1110,7 @@ weak_map_compare_and_set(int argc, VALUE *argv, VALUE self)
|
|
|
1103
1110
|
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1104
1111
|
);
|
|
1105
1112
|
|
|
1106
|
-
weak_map_execution_context_t execution;
|
|
1113
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1107
1114
|
if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qfalse;
|
|
1108
1115
|
bool found;
|
|
1109
1116
|
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
@@ -1133,7 +1140,7 @@ weak_map_modify_body(VALUE opaque)
|
|
|
1133
1140
|
bool store = !remove && value != weak_map_keep;
|
|
1134
1141
|
if (store) containers_check_shareable(value);
|
|
1135
1142
|
rb_check_frozen(operation->self);
|
|
1136
|
-
weak_map_execution_context_t execution;
|
|
1143
|
+
weak_map_execution_context_t execution = {.self = operation->self};
|
|
1137
1144
|
weak_map_lock_state(operation->map, &execution);
|
|
1138
1145
|
bool changed = false;
|
|
1139
1146
|
if (store && !operation->reservation.invalidated) {
|
|
@@ -1180,7 +1187,7 @@ weak_map_modify(VALUE self, VALUE key)
|
|
|
1180
1187
|
};
|
|
1181
1188
|
operation.hash = weak_map_key_hash(map, key);
|
|
1182
1189
|
rb_check_frozen(self);
|
|
1183
|
-
weak_map_execution_context_t execution;
|
|
1190
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1184
1191
|
weak_map_lock_for_key(map, key, operation.hash, &execution);
|
|
1185
1192
|
bool found;
|
|
1186
1193
|
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
@@ -1204,7 +1211,7 @@ weak_map_upsert_body(VALUE opaque)
|
|
|
1204
1211
|
VALUE result = rb_yield(operation->current);
|
|
1205
1212
|
containers_check_shareable(result);
|
|
1206
1213
|
rb_check_frozen(operation->self);
|
|
1207
|
-
weak_map_execution_context_t execution;
|
|
1214
|
+
weak_map_execution_context_t execution = {.self = operation->self};
|
|
1208
1215
|
weak_map_lock_state(operation->map, &execution);
|
|
1209
1216
|
bool stored = !operation->reservation.invalidated;
|
|
1210
1217
|
if (stored) {
|
|
@@ -1251,7 +1258,7 @@ weak_map_upsert(int argc, VALUE *argv, VALUE self)
|
|
|
1251
1258
|
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1252
1259
|
);
|
|
1253
1260
|
|
|
1254
|
-
weak_map_execution_context_t execution;
|
|
1261
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1255
1262
|
if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
|
|
1256
1263
|
bool found;
|
|
1257
1264
|
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
|
@@ -1301,7 +1308,7 @@ weak_map_size(VALUE self)
|
|
|
1301
1308
|
{
|
|
1302
1309
|
weak_map_t *map = get_weak_map(self);
|
|
1303
1310
|
size_t size;
|
|
1304
|
-
weak_map_execution_context_t execution;
|
|
1311
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1305
1312
|
weak_map_lock_state(map, &execution);
|
|
1306
1313
|
size = map->size;
|
|
1307
1314
|
pthread_mutex_unlock(&map->lock);
|
|
@@ -1317,32 +1324,37 @@ weak_map_enumerator_size(VALUE self, VALUE arguments, VALUE enumerator)
|
|
|
1317
1324
|
}
|
|
1318
1325
|
|
|
1319
1326
|
static VALUE
|
|
1320
|
-
weak_map_entries_snapshot(weak_map_t *map)
|
|
1327
|
+
weak_map_entries_snapshot(VALUE self, weak_map_t *map)
|
|
1321
1328
|
{
|
|
1322
1329
|
for (;;) {
|
|
1323
1330
|
size_t capacity;
|
|
1324
|
-
weak_map_execution_context_t execution;
|
|
1331
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1325
1332
|
weak_map_lock_state(map, &execution);
|
|
1326
1333
|
capacity = map->size;
|
|
1327
1334
|
pthread_mutex_unlock(&map->lock);
|
|
1328
1335
|
|
|
1329
1336
|
VALUE entries = rb_ary_new_capa((long)(capacity * 2));
|
|
1330
|
-
rb_ary_resize(entries, (long)(capacity * 2));
|
|
1331
1337
|
weak_map_lock_state(map, &execution);
|
|
1332
1338
|
if (map->size > capacity) {
|
|
1333
1339
|
pthread_mutex_unlock(&map->lock);
|
|
1334
1340
|
continue;
|
|
1335
1341
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1342
|
+
/* The private array has capacity for every pair. Bulk appends cannot
|
|
1343
|
+
* grow it or trigger GC while the state mutex is held. */
|
|
1344
|
+
VALUE batch[64];
|
|
1345
|
+
long length = 0;
|
|
1338
1346
|
for (size_t index = 0; index < map->capacity; index++) {
|
|
1339
1347
|
weak_map_slot_t *slot = &map->slots[index];
|
|
1340
1348
|
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
1341
|
-
|
|
1342
|
-
|
|
1349
|
+
batch[length++] = slot->key;
|
|
1350
|
+
batch[length++] = slot->value;
|
|
1351
|
+
if (length == 64) {
|
|
1352
|
+
rb_ary_cat(entries, batch, length);
|
|
1353
|
+
length = 0;
|
|
1354
|
+
}
|
|
1343
1355
|
}
|
|
1356
|
+
if (length) rb_ary_cat(entries, batch, length);
|
|
1344
1357
|
pthread_mutex_unlock(&map->lock);
|
|
1345
|
-
rb_ary_resize(entries, entry_count);
|
|
1346
1358
|
return entries;
|
|
1347
1359
|
}
|
|
1348
1360
|
}
|
|
@@ -1350,11 +1362,11 @@ weak_map_entries_snapshot(weak_map_t *map)
|
|
|
1350
1362
|
static VALUE
|
|
1351
1363
|
weak_map_keys(VALUE self)
|
|
1352
1364
|
{
|
|
1353
|
-
VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
|
|
1365
|
+
VALUE entries = weak_map_entries_snapshot(self, get_weak_map(self));
|
|
1354
1366
|
long entry_count = RARRAY_LEN(entries);
|
|
1355
1367
|
VALUE keys = rb_ary_new_capa(entry_count / 2);
|
|
1356
1368
|
for (long index = 0; index < entry_count; index += 2) {
|
|
1357
|
-
rb_ary_push(keys,
|
|
1369
|
+
rb_ary_push(keys, rb_ary_entry(entries, index));
|
|
1358
1370
|
}
|
|
1359
1371
|
RB_GC_GUARD(entries);
|
|
1360
1372
|
return keys;
|
|
@@ -1364,10 +1376,10 @@ static VALUE
|
|
|
1364
1376
|
weak_map_each(VALUE self)
|
|
1365
1377
|
{
|
|
1366
1378
|
RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
|
|
1367
|
-
VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
|
|
1379
|
+
VALUE entries = weak_map_entries_snapshot(self, get_weak_map(self));
|
|
1368
1380
|
long entry_count = RARRAY_LEN(entries);
|
|
1369
1381
|
for (long index = 0; index < entry_count; index += 2) {
|
|
1370
|
-
rb_yield(rb_assoc_new(
|
|
1382
|
+
rb_yield(rb_assoc_new(rb_ary_entry(entries, index), rb_ary_entry(entries, index + 1)));
|
|
1371
1383
|
}
|
|
1372
1384
|
RB_GC_GUARD(entries);
|
|
1373
1385
|
return self;
|
|
@@ -1381,18 +1393,17 @@ weak_map_each_live(VALUE self)
|
|
|
1381
1393
|
{
|
|
1382
1394
|
RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
|
|
1383
1395
|
weak_map_t *map = get_weak_map(self);
|
|
1384
|
-
weak_map_execution_context_t execution;
|
|
1396
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1385
1397
|
size_t cursor = 0;
|
|
1386
1398
|
uint64_t epoch = 0;
|
|
1387
1399
|
bool started = false;
|
|
1388
1400
|
const long batch_slots = 64;
|
|
1389
|
-
|
|
1390
|
-
|
|
1401
|
+
/* C-stack roots keep this bounded batch alive across Ruby yields. */
|
|
1402
|
+
VALUE batch[64];
|
|
1391
1403
|
|
|
1392
1404
|
for (;;) {
|
|
1393
|
-
VALUE *storage = RARRAY_PTR(batch);
|
|
1394
1405
|
for (long index = 0; index < batch_slots; index++) {
|
|
1395
|
-
|
|
1406
|
+
batch[index] = Qnil;
|
|
1396
1407
|
}
|
|
1397
1408
|
weak_map_lock_state(map, &execution);
|
|
1398
1409
|
if (started && epoch != map->iteration_epoch) {
|
|
@@ -1405,16 +1416,17 @@ weak_map_each_live(VALUE self)
|
|
|
1405
1416
|
while (cursor < map->capacity && length < batch_slots) {
|
|
1406
1417
|
weak_map_slot_t *slot = &map->slots[cursor++];
|
|
1407
1418
|
if (slot->state != WEAK_MAP_OCCUPIED) continue;
|
|
1408
|
-
|
|
1409
|
-
|
|
1419
|
+
batch[length++] = slot->key;
|
|
1420
|
+
batch[length++] = slot->value;
|
|
1410
1421
|
}
|
|
1411
1422
|
pthread_mutex_unlock(&map->lock);
|
|
1412
1423
|
if (length == 0) break;
|
|
1413
1424
|
for (long index = 0; index < length; index += 2) {
|
|
1414
|
-
rb_yield(rb_assoc_new(
|
|
1425
|
+
rb_yield(rb_assoc_new(batch[index], batch[index + 1]));
|
|
1415
1426
|
}
|
|
1416
1427
|
}
|
|
1417
|
-
RB_GC_GUARD(batch);
|
|
1428
|
+
RB_GC_GUARD(batch[0]);
|
|
1429
|
+
RB_GC_GUARD(batch[63]);
|
|
1418
1430
|
return self;
|
|
1419
1431
|
}
|
|
1420
1432
|
|
|
@@ -1422,10 +1434,10 @@ static VALUE
|
|
|
1422
1434
|
weak_map_each_key(VALUE self)
|
|
1423
1435
|
{
|
|
1424
1436
|
RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
|
|
1425
|
-
VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
|
|
1437
|
+
VALUE entries = weak_map_entries_snapshot(self, get_weak_map(self));
|
|
1426
1438
|
long entry_count = RARRAY_LEN(entries);
|
|
1427
1439
|
for (long index = 0; index < entry_count; index += 2) {
|
|
1428
|
-
rb_yield(
|
|
1440
|
+
rb_yield(rb_ary_entry(entries, index));
|
|
1429
1441
|
}
|
|
1430
1442
|
RB_GC_GUARD(entries);
|
|
1431
1443
|
return self;
|
|
@@ -1435,10 +1447,10 @@ static VALUE
|
|
|
1435
1447
|
weak_map_each_value(VALUE self)
|
|
1436
1448
|
{
|
|
1437
1449
|
RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
|
|
1438
|
-
VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
|
|
1450
|
+
VALUE entries = weak_map_entries_snapshot(self, get_weak_map(self));
|
|
1439
1451
|
long entry_count = RARRAY_LEN(entries);
|
|
1440
1452
|
for (long index = 1; index < entry_count; index += 2) {
|
|
1441
|
-
rb_yield(
|
|
1453
|
+
rb_yield(rb_ary_entry(entries, index));
|
|
1442
1454
|
}
|
|
1443
1455
|
RB_GC_GUARD(entries);
|
|
1444
1456
|
return self;
|
|
@@ -1460,7 +1472,7 @@ weak_map_get_with_timeout(int argc, VALUE *argv, VALUE self)
|
|
|
1460
1472
|
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1461
1473
|
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1462
1474
|
);
|
|
1463
|
-
weak_map_execution_context_t execution;
|
|
1475
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1464
1476
|
if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
|
|
1465
1477
|
return weak_map_timeout_result();
|
|
1466
1478
|
}
|
|
@@ -1492,7 +1504,7 @@ weak_map_store_with_timeout(int argc, VALUE *argv, VALUE self)
|
|
|
1492
1504
|
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1493
1505
|
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1494
1506
|
);
|
|
1495
|
-
weak_map_execution_context_t execution;
|
|
1507
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1496
1508
|
if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
|
|
1497
1509
|
return weak_map_timeout_result();
|
|
1498
1510
|
}
|
|
@@ -1521,7 +1533,7 @@ weak_map_swap(int argc, VALUE *argv, VALUE self)
|
|
|
1521
1533
|
weak_map_timeout_t timeout = weak_map_parse_timeout(
|
|
1522
1534
|
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1523
1535
|
);
|
|
1524
|
-
weak_map_execution_context_t execution;
|
|
1536
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1525
1537
|
if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
|
|
1526
1538
|
return weak_map_timeout_result();
|
|
1527
1539
|
}
|
|
@@ -1568,7 +1580,7 @@ weak_map_wait_for_value(int argc, VALUE *argv, VALUE self, bool non_nil)
|
|
|
1568
1580
|
bool found;
|
|
1569
1581
|
VALUE current = Qnil;
|
|
1570
1582
|
uint64_t generation;
|
|
1571
|
-
weak_map_execution_context_t execution;
|
|
1583
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1572
1584
|
weak_map_lock_state(map, &execution);
|
|
1573
1585
|
weak_map_reservation_t *entry = weak_map_find_reservation_locked(map, key, hash, &execution);
|
|
1574
1586
|
size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
|
|
@@ -1628,7 +1640,7 @@ weak_map_update(int argc, VALUE *argv, VALUE self)
|
|
|
1628
1640
|
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1629
1641
|
);
|
|
1630
1642
|
|
|
1631
|
-
weak_map_execution_context_t execution;
|
|
1643
|
+
weak_map_execution_context_t execution = {.self = self};
|
|
1632
1644
|
if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
|
|
1633
1645
|
bool found;
|
|
1634
1646
|
size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
|
data/lib/farce/_yard/macros.rb
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
1
5
|
# @!macro unsafe
|
|
2
6
|
# @note
|
|
3
7
|
# Instances of this class are **not thread-safe** and should not be shared concurrently.
|
|
@@ -11,12 +15,22 @@
|
|
|
11
15
|
|
|
12
16
|
# @!macro modes
|
|
13
17
|
# Valid modes are:
|
|
14
|
-
# * `:
|
|
18
|
+
# * `:smart` - Applies smart rules or a dedicated proxy. Raises for unsupported values.
|
|
19
|
+
# This is the default for most mode-backed classes.
|
|
20
|
+
# * `:smart_raise` - Applies smart rules or a dedicated proxy, with a raising fallback.
|
|
21
|
+
# * `:smart_copy` - Applies smart rules or a dedicated proxy, then falls back to copying or Marshal.
|
|
22
|
+
# This is the default for {Farce::Port}.
|
|
23
|
+
# * `:smart_move` - Applies smart rules or a dedicated proxy, then falls back to moving.
|
|
24
|
+
# * `:copy` - The value will be copied between Ractors.
|
|
25
|
+
# * `:marshal` - The value will be serialized immediately and reconstructed once per Ractor
|
|
26
|
+
# with Ruby's Marshal protocol.
|
|
27
|
+
# Contents must support Marshal. Nested shareable values may be reconstructed too.
|
|
15
28
|
# * `:make_shareable` - The value will be made Ractor-shareable using {Ractor.make_shareable}.
|
|
16
29
|
# * `:move` - The value will be moved between Ractors. This saves memory compared to copying, and supports values
|
|
17
30
|
# that can't be copied but moved (like IO objects). However, the value will no longer be accessible on the Ractor
|
|
18
31
|
# that pushed it.
|
|
19
|
-
# * `:mutable` - A {Farce::Mutable} instance will be created for the value.
|
|
32
|
+
# * `:mutable` - A {Farce::Mutable} instance will be created for the value.
|
|
33
|
+
# This isn't done recursively and thus will fail for nested unshareable values.
|
|
20
34
|
# * `:local` - The value will be kept local to the Ractor that pushed it. Another ractor trying to receive it will
|
|
21
35
|
# get an error. Useful for usage contained within a single Ractor.
|
|
22
36
|
# * `:proxy` - The value will be wrapped in a `Farce::Proxy` that executes calls in the original Ractor.
|
|
@@ -40,7 +54,8 @@
|
|
|
40
54
|
# Converts incoming keys to their canonical stored form:
|
|
41
55
|
# * If a `Symbol` is provided, it will be used as a method name to call on each key.
|
|
42
56
|
# * If a `Proc` is provided, it will be called with each key and should return the normalized key.
|
|
43
|
-
# * If a `Hash` or {Farce::Abstract::Map Map} is provided, it will be used to look up
|
|
57
|
+
# * If a `Hash` or {Farce::Abstract::Map Map} is provided, it will be used to look up
|
|
58
|
+
# the normalized key for each incoming key.
|
|
44
59
|
|
|
45
60
|
# @!macro map_initialization
|
|
46
61
|
# Create a map with optional initial entries.
|
data/lib/farce/abstract/flag.rb
CHANGED
|
@@ -41,6 +41,14 @@ module Farce
|
|
|
41
41
|
# @return [Boolean] whether the replacement succeeded
|
|
42
42
|
# @raise [ArgumentError] if either argument is not true or false
|
|
43
43
|
#
|
|
44
|
+
# @!method wait_until_changed(expected, timeout: nil)
|
|
45
|
+
# Wait until the current value differs from the expected value.
|
|
46
|
+
# @param expected [Boolean] the value to wait to change from
|
|
47
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
48
|
+
# @yield called when the timeout expires
|
|
49
|
+
# @return [Boolean, BasicObject, nil] the changed value or timeout fallback
|
|
50
|
+
# @raise [ArgumentError] if expected is not true or false
|
|
51
|
+
#
|
|
44
52
|
# @!method toggle
|
|
45
53
|
# Atomically invert the value.
|
|
46
54
|
# @return [Boolean] the new value
|
|
@@ -51,6 +59,71 @@ module Farce
|
|
|
51
59
|
include Internal::ValueSerialization
|
|
52
60
|
include Internal::Inspect
|
|
53
61
|
|
|
62
|
+
# Wait until the flag becomes true.
|
|
63
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
64
|
+
# @yield called when the timeout expires
|
|
65
|
+
# @return [Boolean, BasicObject, nil] the changed value or timeout fallback
|
|
66
|
+
def wait_until_set(timeout: nil, &) = wait_until_changed(false, timeout:, &)
|
|
67
|
+
alias wait_until_true wait_until_set
|
|
68
|
+
alias wait_while_unset wait_until_set
|
|
69
|
+
alias wait_while_false wait_until_set
|
|
70
|
+
|
|
71
|
+
# Wait until the flag becomes false.
|
|
72
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
73
|
+
# @yield called when the timeout expires
|
|
74
|
+
# @return [Boolean, BasicObject, nil] the changed value or timeout fallback
|
|
75
|
+
def wait_until_unset(timeout: nil, &) = wait_until_changed(true, timeout:, &)
|
|
76
|
+
alias wait_until_false wait_until_unset
|
|
77
|
+
alias wait_while_set wait_until_unset
|
|
78
|
+
alias wait_while_true wait_until_unset
|
|
79
|
+
|
|
80
|
+
# Wait until a block condition matches the current value.
|
|
81
|
+
# One timeout budget covers all checks and waits. The block is not interrupted.
|
|
82
|
+
# @yieldparam value [Boolean] the current value
|
|
83
|
+
# @yieldreturn [Boolean] whether the value matches
|
|
84
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
85
|
+
# @return [Boolean, nil] the matching value, or nil on timeout
|
|
86
|
+
# @raise [LocalJumpError] if no block is given
|
|
87
|
+
def wait_until(timeout: nil, &) = Internal.wait_until(self, timeout:, &)
|
|
88
|
+
|
|
89
|
+
# Wait while the block returns a truthy value.
|
|
90
|
+
# @yieldparam value [Boolean] the current value
|
|
91
|
+
# @yieldreturn [BasicObject] a truthy value to keep waiting, or nil or false to stop
|
|
92
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
93
|
+
# @return [Boolean, nil] the value when the condition becomes false, or nil on timeout
|
|
94
|
+
# @raise [LocalJumpError] if no block is given
|
|
95
|
+
def wait_while(timeout: nil)
|
|
96
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
97
|
+
wait_until(timeout:) { |value| !yield(value) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Wait while `object === value` is true.
|
|
101
|
+
# @param object [#===] the pattern to stop matching
|
|
102
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
103
|
+
# @return [Boolean, nil] the first nonmatching value, or nil on timeout
|
|
104
|
+
def wait_while_match(object, timeout: nil)
|
|
105
|
+
wait_while(timeout:) { |value| object === value } # rubocop:disable Style/CaseEquality
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# (see #wait_until_changed)
|
|
109
|
+
def wait_while_value(...) = wait_until_changed(...)
|
|
110
|
+
|
|
111
|
+
# Wait until `object == value` is true.
|
|
112
|
+
# @param object [BasicObject] the value to compare with the current value
|
|
113
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
114
|
+
# @return [Boolean, nil] the matching value, or nil on timeout
|
|
115
|
+
def wait_until_value(object, timeout: nil)
|
|
116
|
+
wait_until(timeout:) { |value| object == value }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Wait until `object === value` is true.
|
|
120
|
+
# @param object [#===] the pattern to match
|
|
121
|
+
# @param timeout [Numeric, nil] the total seconds available
|
|
122
|
+
# @return [Boolean, nil] the matching value, or nil on timeout
|
|
123
|
+
def wait_until_match(object, timeout: nil)
|
|
124
|
+
wait_until(timeout:) { |value| object === value } # rubocop:disable Style/CaseEquality
|
|
125
|
+
end
|
|
126
|
+
|
|
54
127
|
# @api private
|
|
55
128
|
def inspect_with(inspector)
|
|
56
129
|
begin
|
data/lib/farce/abstract/queue.rb
CHANGED
|
@@ -98,7 +98,7 @@ module Farce
|
|
|
98
98
|
# @raise [Farce::ClosedQueueError] when the queue is closed
|
|
99
99
|
# @raise [Farce::SealedQueueError] when the queue is sealed but not yet closed
|
|
100
100
|
# @raise [ThreadError] when the queue is full and non_block is true
|
|
101
|
-
# @return [
|
|
101
|
+
# @return [BasicObject, false] a truthy object if the item was added, or `false` if the timeout expired
|
|
102
102
|
#
|
|
103
103
|
# @!method try_pop
|
|
104
104
|
# @abstract Subclasses may add additional parameters to this method.
|
|
@@ -116,8 +116,9 @@ module Farce
|
|
|
116
116
|
# @yield Block called if the queue is at capacity.
|
|
117
117
|
# @raise [Farce::ClosedQueueError] when the queue is closed
|
|
118
118
|
# @raise [Farce::SealedQueueError] when the queue is sealed but not yet closed
|
|
119
|
-
# @return [
|
|
120
|
-
#
|
|
119
|
+
# @return [BasicObject]
|
|
120
|
+
# A truthy object if the item was added, or the fallback result if the queue was at capacity.
|
|
121
|
+
# Returns `false` if the queue was at capacity and no block was given.
|
|
121
122
|
#
|
|
122
123
|
# @!method wait_pop(timeout: nil)
|
|
123
124
|
# @abstract Subclasses may add additional parameters to this method.
|
|
@@ -6,7 +6,7 @@ module Farce
|
|
|
6
6
|
module Abstract
|
|
7
7
|
# @abstract A shared super class for all scheduler implementations.
|
|
8
8
|
#
|
|
9
|
-
# @!method schedule(*args, mode: :
|
|
9
|
+
# @!method schedule(*args, mode: :smart, auto_local: true)
|
|
10
10
|
# @abstract This method must be implemented by concrete scheduler subclasses.
|
|
11
11
|
#
|
|
12
12
|
# Schedules a block for execution.
|
|
@@ -23,7 +23,7 @@ module Farce
|
|
|
23
23
|
# May be handled according to `mode`.
|
|
24
24
|
#
|
|
25
25
|
# @param mode [Symbol]
|
|
26
|
-
# The mode in which to schedule the task. Defaults to `:
|
|
26
|
+
# The mode in which to schedule the task. Defaults to `:smart`.
|
|
27
27
|
# May be ignored by some schedulers.
|
|
28
28
|
#
|
|
29
29
|
# @param auto_local [Boolean]
|
|
@@ -82,7 +82,7 @@ module Farce
|
|
|
82
82
|
#
|
|
83
83
|
# @param (see #schedule)
|
|
84
84
|
# @return [self]
|
|
85
|
-
def execute(*, mode: :
|
|
85
|
+
def execute(*, mode: :smart, auto_local: true, &callback)
|
|
86
86
|
raise LocalJumpError, "Cannot yield without a block" unless block_given?
|
|
87
87
|
raise SchedulerClosedError, "cannot execute task on a closed scheduler" if closed?
|
|
88
88
|
|
data/lib/farce/abstract/set.rb
CHANGED
|
@@ -61,7 +61,7 @@ module Farce
|
|
|
61
61
|
# @param normalize [Symbol, Proc, Hash, Farce::Abstract::Map, nil] The element normalizer.
|
|
62
62
|
# @param compare_by_identity [Boolean] Whether membership uses object identity.
|
|
63
63
|
# @param mode [Symbol] The default transfer mode for Farce::Set, Farce::SortedSet,
|
|
64
|
-
# and Farce::WeakSet. Defaults to :
|
|
64
|
+
# and Farce::WeakSet. Defaults to :smart for strong sets and :raise for WeakSet.
|
|
65
65
|
# @param options [Hash] Additional options for the selected variant.
|
|
66
66
|
# @option options [Symbol] scope (:ractor) The scope used by Farce::Local variants.
|
|
67
67
|
# @yield [element] Optionally transform each initial element before storage.
|
|
@@ -753,11 +753,22 @@ module Farce
|
|
|
753
753
|
def prepare_mode_entry(element, requested_mode)
|
|
754
754
|
if identity_membership?(element) && !Ractor.shareable?(element)
|
|
755
755
|
case requested_mode
|
|
756
|
-
when :copy
|
|
756
|
+
when :copy, :marshal
|
|
757
757
|
raise ArgumentError, "identity comparison cannot copy a non-shareable element"
|
|
758
758
|
when :shareable_copy, :make_shareable
|
|
759
759
|
payload = @manager.wrap(element, mode: requested_mode)
|
|
760
760
|
return [insertion_key(payload), payload]
|
|
761
|
+
when :smart, :smart_raise, :smart_copy, :smart_move
|
|
762
|
+
token = IDENTITY_TOKENS[element]
|
|
763
|
+
payload = @manager.wrap(element, mode: requested_mode)
|
|
764
|
+
case payload
|
|
765
|
+
when Envelope::Copy, Envelope::Marshal
|
|
766
|
+
raise ArgumentError, "identity comparison cannot copy a non-shareable element"
|
|
767
|
+
when Envelope::Move
|
|
768
|
+
return [MembershipKey.new(token || Object.new.freeze, identity: true), payload]
|
|
769
|
+
end
|
|
770
|
+
key = @manager.managed_envelope?(payload) ? insertion_key(element) : insertion_key(payload)
|
|
771
|
+
return [key, payload]
|
|
761
772
|
end
|
|
762
773
|
end
|
|
763
774
|
|
|
@@ -812,9 +823,10 @@ module Farce
|
|
|
812
823
|
|
|
813
824
|
def stored_mode(entry)
|
|
814
825
|
case entry.payload
|
|
815
|
-
when Envelope::Copy
|
|
816
|
-
when Envelope::
|
|
817
|
-
when Envelope::
|
|
826
|
+
when Envelope::Copy then :copy
|
|
827
|
+
when Envelope::Marshal then :marshal
|
|
828
|
+
when Envelope::Local then :local
|
|
829
|
+
when Envelope::Move then :move
|
|
818
830
|
else @manager.mode
|
|
819
831
|
end
|
|
820
832
|
end
|
|
@@ -11,12 +11,12 @@ module Farce
|
|
|
11
11
|
# elements raise during insertion or lookup.
|
|
12
12
|
class SortedSet < Set
|
|
13
13
|
# Construct a set in ascending comparator order.
|
|
14
|
-
# @overload initialize(enumerable = nil, normalize: nil, compare_by_identity: false, mode: :
|
|
14
|
+
# @overload initialize(enumerable = nil, normalize: nil, compare_by_identity: false, mode: :smart, **options)
|
|
15
15
|
# @param enumerable [#each, nil] The initial elements, or nil for an empty set.
|
|
16
16
|
# @param normalize [Symbol, Proc, Hash, Farce::Abstract::Map, nil] The element normalizer.
|
|
17
17
|
# @param compare_by_identity [Boolean] Must be false. Membership uses <=>.
|
|
18
18
|
# @param mode [Symbol] The default transfer mode. Only Farce::SortedSet accepts
|
|
19
|
-
# this option, which defaults to :
|
|
19
|
+
# this option, which defaults to :smart.
|
|
20
20
|
# @param options [Hash] Additional options for the selected variant.
|
|
21
21
|
# @option options [Symbol] scope (:ractor) The scope used by Farce::Local::SortedSet.
|
|
22
22
|
# @yield [element] Optionally transform each initial element before normalization.
|