farce 0.1.0-x86_64-linux-gnu → 0.3.0-x86_64-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 +127 -1
- data/README.md +111 -17
- data/docs/gems/dry-types.md +8 -8
- data/docs/modes.md +75 -15
- data/docs/scopes.md +3 -1
- data/docs/variants.md +18 -0
- 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/executor.rb +130 -0
- data/lib/farce/abstract/flag.rb +73 -0
- data/lib/farce/abstract/pool.rb +334 -0
- data/lib/farce/abstract/queue.rb +4 -3
- data/lib/farce/abstract/scheduler.rb +539 -93
- 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/jruby/signal.rb +7 -4
- 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/main_scheduler.rb +2 -2
- data/lib/farce/engine/shared/parallel_scheduler.rb +1 -1
- data/lib/farce/engine/shared/proxy_owner.rb +1 -1
- data/lib/farce/engine/shared/unshared_atom.rb +9 -8
- 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/shared.rb +9 -0
- 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/lease_initialization.rb +1 -1
- data/lib/farce/internal/local_executor_methods.rb +33 -0
- data/lib/farce/internal/local_scheduled_task.rb +27 -0
- data/lib/farce/internal/map_value_modes.rb +1 -1
- data/lib/farce/internal/scheduled_task.rb +1 -1
- data/lib/farce/internal/scheduler_lifecycle.rb +1 -1
- data/lib/farce/internal/thread_pool_supervisor.rb +45 -0
- data/lib/farce/internal/unshared_pool_worker.rb +14 -0
- data/lib/farce/internal.rb +14 -0
- 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/pool.rb +111 -0
- data/lib/farce/local/scheduler.rb +219 -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 +2 -290
- 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 +9 -4
- data/lib/farce/queue.rb +1 -1
- data/lib/farce/scheduler.rb +1 -502
- data/lib/farce/set.rb +1 -1
- data/lib/farce/sorted_set.rb +1 -1
- data/lib/farce/task.rb +285 -0
- data/lib/farce/{thread_scheduler.rb → thread_executor.rb} +7 -7
- 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/pool.rb +44 -0
- data/lib/farce/unshared/scheduler.rb +89 -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 +20 -25
- metadata +17 -2
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.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# shareable_constant_value: literal
|
|
3
|
+
# warn_indent: true
|
|
4
|
+
|
|
5
|
+
module Farce
|
|
6
|
+
module Abstract
|
|
7
|
+
# A task executor that can submit work or wait for it to finish.
|
|
8
|
+
#
|
|
9
|
+
# {#schedule} submits a task. {#execute} waits for the task to finish and may
|
|
10
|
+
# run it directly when execution is local. Concrete executors decide where
|
|
11
|
+
# tasks run and how their arguments are transferred.
|
|
12
|
+
#
|
|
13
|
+
# @abstract Use a scheduler or pool class.
|
|
14
|
+
#
|
|
15
|
+
# @!method schedule(*args, mode: :smart, auto_local: true)
|
|
16
|
+
# @abstract This method must be implemented by concrete executor subclasses.
|
|
17
|
+
#
|
|
18
|
+
# Schedules a block for execution.
|
|
19
|
+
# What actually happens depends on the concrete executor implementation.
|
|
20
|
+
# Some executors may ignore the `auto_local` option. Some might treat every mode as `:local`.
|
|
21
|
+
#
|
|
22
|
+
# The executor may choose to block until the task has been executed, but most will add it to a queue of pending
|
|
23
|
+
# tasks, only blocking when that queue's capacity has been reached.
|
|
24
|
+
#
|
|
25
|
+
# @!macro modes
|
|
26
|
+
#
|
|
27
|
+
# @param args [Array]
|
|
28
|
+
# Arguments to pass to the scheduled block.
|
|
29
|
+
# May be handled according to `mode`.
|
|
30
|
+
#
|
|
31
|
+
# @param mode [Symbol]
|
|
32
|
+
# The mode in which to schedule the task. Defaults to `:smart`.
|
|
33
|
+
# May be ignored by some executors.
|
|
34
|
+
#
|
|
35
|
+
# @param auto_local [Boolean]
|
|
36
|
+
# Whether to automatically use a local mode when possible. Defaults to `true`.
|
|
37
|
+
# May be ignored by some executors.
|
|
38
|
+
#
|
|
39
|
+
# @yield [*args] The block to be executed by the scheduler.
|
|
40
|
+
# @yieldparam args [Array] The arguments passed to the block.
|
|
41
|
+
# @yieldreceiver [BasicObject, nil]
|
|
42
|
+
# Local mode will not touch the block's binding (self stays the same), but other modes may change it to `nil`.
|
|
43
|
+
#
|
|
44
|
+
# @return [self]
|
|
45
|
+
#
|
|
46
|
+
# @!method close
|
|
47
|
+
# @abstract This method must be implemented by concrete executor subclasses.
|
|
48
|
+
# Closes the executor, releasing any resources it holds.
|
|
49
|
+
# Once closed, the executor cannot be used to schedule new tasks.
|
|
50
|
+
# Will drain any pending tasks before fully closing. If tasks are running indefinitely, it may block.
|
|
51
|
+
# @return [self]
|
|
52
|
+
#
|
|
53
|
+
# @!method closed?
|
|
54
|
+
# @abstract This method must be implemented by concrete executor subclasses.
|
|
55
|
+
# @return [Boolean] Whether the executor is closed.
|
|
56
|
+
#
|
|
57
|
+
# @!method state
|
|
58
|
+
# @abstract This method must be implemented by concrete executor subclasses.
|
|
59
|
+
# @return [Symbol]
|
|
60
|
+
# The current state of the executor.
|
|
61
|
+
#
|
|
62
|
+
# Common states include:
|
|
63
|
+
# * `:initialized`, `:launching` and `:setup` for the setup phase.
|
|
64
|
+
# * `:running` for the execution phase.
|
|
65
|
+
# * `:closing`, `:closed`, and `:error` for the termination phase.
|
|
66
|
+
#
|
|
67
|
+
# Not all executors will implement every state.
|
|
68
|
+
#
|
|
69
|
+
# @!method ractor_shareable?
|
|
70
|
+
# @abstract Executors should include {Shareable} or {Unshareable}, which implement this method.
|
|
71
|
+
# @return [Boolean] Whether the executor is safe to use across Ractors.
|
|
72
|
+
class Executor
|
|
73
|
+
include Internal::Noncopyable
|
|
74
|
+
|
|
75
|
+
# An error that occurred during the executor's operation, if any.
|
|
76
|
+
# This is primarily for scheduling errors, not execution errors.
|
|
77
|
+
# You can check this if the {#state} is `:error`.
|
|
78
|
+
def error = nil
|
|
79
|
+
|
|
80
|
+
# @param wait [Boolean] Whether to wait for the owner to be non-nil before checking if the executor is local.
|
|
81
|
+
# @return [Boolean] Whether the executor is local for the current Ractor.
|
|
82
|
+
def local?(wait: true) = false # rubocop:disable Lint/UnusedMethodArgument
|
|
83
|
+
|
|
84
|
+
# Attempts to submit a task. Queue-based executors return `nil` when full
|
|
85
|
+
# instead of waiting for space. Other executors behave like {#schedule}.
|
|
86
|
+
#
|
|
87
|
+
# @param (see #schedule)
|
|
88
|
+
# @return [self, nil] self when submitted, or nil when the queue is full.
|
|
89
|
+
def try_schedule(...) = schedule(...)
|
|
90
|
+
|
|
91
|
+
# Closes the executor in every scope.
|
|
92
|
+
# For executors without scopes, this behaves like {#close}.
|
|
93
|
+
#
|
|
94
|
+
# @return [self]
|
|
95
|
+
def close_all = close
|
|
96
|
+
|
|
97
|
+
# Like {#schedule}, with two differences:
|
|
98
|
+
# 1. If {#local?} returns true, and it would run in local mode (either due to `auto_local` or explicitly set
|
|
99
|
+
# `mode: :local`), it executes the block directly without enqueuing it.
|
|
100
|
+
# 2. It blocks until the block has been executed, either immediately in local mode or after being scheduled.
|
|
101
|
+
#
|
|
102
|
+
# @param (see #schedule)
|
|
103
|
+
# @return [self]
|
|
104
|
+
def execute(*, mode: :smart, auto_local: true, &callback)
|
|
105
|
+
raise LocalJumpError, "Cannot yield without a block" unless block_given?
|
|
106
|
+
raise SchedulerClosedError, "cannot execute task on a closed scheduler" if closed?
|
|
107
|
+
|
|
108
|
+
if (auto_local || mode == :local) && local?
|
|
109
|
+
raise SchedulerClosedError, "cannot execute task on a closed scheduler" if closed?
|
|
110
|
+
yield(*)
|
|
111
|
+
return self
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
ran = Farce::Flag.new(false)
|
|
115
|
+
signal = Signal.new
|
|
116
|
+
callback = Ractor.shareable_proc(&callback) unless Ractor.shareable?(callback)
|
|
117
|
+
|
|
118
|
+
schedule(callback, ran, signal, *, mode:, auto_local:) do |callback, ran, signal, *args|
|
|
119
|
+
callback.call(*args)
|
|
120
|
+
ensure
|
|
121
|
+
ran.set
|
|
122
|
+
signal.broadcast
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
signal.wait_until { ran.value }
|
|
126
|
+
self
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|