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/priority_queue.c
CHANGED
|
@@ -52,7 +52,7 @@ priority_queue_snapshot_string(VALUE string)
|
|
|
52
52
|
rb_str_replace(copy, string);
|
|
53
53
|
instance_variables = rb_obj_instance_variables(string);
|
|
54
54
|
for (index = 0; index < RARRAY_LEN(instance_variables); index++) {
|
|
55
|
-
ID id = SYM2ID(
|
|
55
|
+
ID id = SYM2ID(rb_ary_entry(instance_variables, index));
|
|
56
56
|
rb_ivar_set(copy, id, rb_ivar_get(string, id));
|
|
57
57
|
}
|
|
58
58
|
rb_obj_freeze(copy);
|
|
@@ -436,7 +436,7 @@ priority_queue_track_remove(priority_queue_t *queue, priority_queue_value_t *val
|
|
|
436
436
|
queue->generation++;
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
static void priority_queue_wait_for_publication(priority_queue_t *queue);
|
|
439
|
+
static void priority_queue_wait_for_publication(VALUE self, priority_queue_t *queue);
|
|
440
440
|
|
|
441
441
|
typedef struct {
|
|
442
442
|
VALUE receiver;
|
|
@@ -581,25 +581,27 @@ priority_queue_compact(void *pointer)
|
|
|
581
581
|
dnode_t *node;
|
|
582
582
|
|
|
583
583
|
if (!queue) return;
|
|
584
|
-
|
|
585
|
-
|
|
584
|
+
containers_gc_update(&queue->owner_fiber);
|
|
585
|
+
containers_gc_update(&queue->owner_ruby_thread);
|
|
586
586
|
/* The VM already roots the running publisher. dcompact only refreshes the
|
|
587
587
|
* unmarked identity handles if that Fiber or Thread moved. */
|
|
588
|
-
queue->publication_owner_fiber
|
|
589
|
-
|
|
590
|
-
queue->
|
|
591
|
-
|
|
592
|
-
queue->signal = rb_gc_location(queue->signal);
|
|
593
|
-
queue->unshared_self = rb_gc_location(queue->unshared_self);
|
|
588
|
+
containers_gc_update(&queue->publication_owner_fiber);
|
|
589
|
+
containers_gc_update(&queue->publication_owner_ruby_thread);
|
|
590
|
+
containers_gc_update(&queue->signal);
|
|
591
|
+
containers_gc_update(&queue->unshared_self);
|
|
594
592
|
for (node = dict_first(&queue->tree); node; node = dict_next(&queue->tree, node)) {
|
|
595
593
|
priority_queue_bucket_t *bucket = (priority_queue_bucket_t *)node;
|
|
596
594
|
priority_queue_value_t *entry;
|
|
597
|
-
|
|
598
|
-
|
|
595
|
+
VALUE previous_priority = bucket->priority;
|
|
596
|
+
containers_gc_update(&bucket->priority);
|
|
597
|
+
if (previous_priority != bucket->priority) node->dict_key = (const void *)bucket->priority;
|
|
598
|
+
bool rebuild = false;
|
|
599
599
|
for (entry = bucket->head; entry; entry = entry->next) {
|
|
600
|
-
|
|
600
|
+
VALUE previous_value = entry->value;
|
|
601
|
+
containers_gc_update(&entry->value);
|
|
602
|
+
if (previous_value != entry->value) rebuild = true;
|
|
601
603
|
}
|
|
602
|
-
priority_queue_identity_index_rebuild(bucket);
|
|
604
|
+
if (rebuild) priority_queue_identity_index_rebuild(bucket);
|
|
603
605
|
}
|
|
604
606
|
}
|
|
605
607
|
|
|
@@ -645,7 +647,11 @@ static const rb_data_type_t priority_queue_type = {
|
|
|
645
647
|
.dsize = priority_queue_memsize,
|
|
646
648
|
.dcompact = priority_queue_compact,
|
|
647
649
|
},
|
|
648
|
-
.flags =
|
|
650
|
+
.flags =
|
|
651
|
+
RUBY_TYPED_WB_PROTECTED |
|
|
652
|
+
RUBY_TYPED_FROZEN_SHAREABLE |
|
|
653
|
+
RUBY_TYPED_FREE_IMMEDIATELY |
|
|
654
|
+
FARCE_TYPED_THREAD_SAFE_FREE,
|
|
649
655
|
};
|
|
650
656
|
|
|
651
657
|
static const rb_data_type_t unshared_priority_queue_type = {
|
|
@@ -657,6 +663,7 @@ static const rb_data_type_t unshared_priority_queue_type = {
|
|
|
657
663
|
.dcompact = priority_queue_compact,
|
|
658
664
|
},
|
|
659
665
|
.parent = &priority_queue_type,
|
|
666
|
+
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE,
|
|
660
667
|
};
|
|
661
668
|
|
|
662
669
|
/* The GVL protects the native fields within one Ractor. Keep the logical
|
|
@@ -737,7 +744,7 @@ priority_queue_get(VALUE self)
|
|
|
737
744
|
* traversal cannot leak a usable half-published queue. The steady-state
|
|
738
745
|
* path is one atomic load and does not touch the physical mutex. */
|
|
739
746
|
if (priority_queue_publication_busy(state)) {
|
|
740
|
-
priority_queue_wait_for_publication(queue);
|
|
747
|
+
priority_queue_wait_for_publication(self, queue);
|
|
741
748
|
state = RUBY_ATOMIC_LOAD(queue->publication_state);
|
|
742
749
|
}
|
|
743
750
|
if (state != PRIORITY_QUEUE_INITIALIZED) {
|
|
@@ -884,7 +891,7 @@ priority_queue_lock_wait_once(priority_queue_t *queue)
|
|
|
884
891
|
}
|
|
885
892
|
|
|
886
893
|
static void
|
|
887
|
-
priority_queue_lock(priority_queue_t *queue, bool *acquired)
|
|
894
|
+
priority_queue_lock(VALUE self, priority_queue_t *queue, bool *acquired)
|
|
888
895
|
{
|
|
889
896
|
VALUE current = rb_fiber_current();
|
|
890
897
|
VALUE current_thread = rb_thread_current();
|
|
@@ -903,8 +910,8 @@ priority_queue_lock(priority_queue_t *queue, bool *acquired)
|
|
|
903
910
|
rb_raise(rb_eThreadError, "deadlock; recursive priority queue access");
|
|
904
911
|
}
|
|
905
912
|
if (NIL_P(queue->owner_fiber) && !publication_busy) {
|
|
906
|
-
queue->owner_fiber
|
|
907
|
-
queue->owner_ruby_thread
|
|
913
|
+
RB_OBJ_WRITE(self, &queue->owner_fiber, current);
|
|
914
|
+
RB_OBJ_WRITE(self, &queue->owner_ruby_thread, current_thread);
|
|
908
915
|
*acquired = true;
|
|
909
916
|
priority_queue_mutex_unlock(queue);
|
|
910
917
|
return;
|
|
@@ -945,6 +952,7 @@ priority_queue_unlock(priority_queue_t *queue)
|
|
|
945
952
|
}
|
|
946
953
|
|
|
947
954
|
typedef struct {
|
|
955
|
+
VALUE self;
|
|
948
956
|
priority_queue_t *queue;
|
|
949
957
|
bool acquired;
|
|
950
958
|
} priority_queue_publication_wait_t;
|
|
@@ -955,7 +963,7 @@ priority_queue_publication_wait_body(VALUE opaque)
|
|
|
955
963
|
priority_queue_publication_wait_t *wait =
|
|
956
964
|
(priority_queue_publication_wait_t *)opaque;
|
|
957
965
|
|
|
958
|
-
priority_queue_lock(wait->queue, &wait->acquired);
|
|
966
|
+
priority_queue_lock(wait->self, wait->queue, &wait->acquired);
|
|
959
967
|
return Qnil;
|
|
960
968
|
}
|
|
961
969
|
|
|
@@ -970,9 +978,10 @@ priority_queue_publication_wait_ensure(VALUE opaque)
|
|
|
970
978
|
}
|
|
971
979
|
|
|
972
980
|
static void
|
|
973
|
-
priority_queue_wait_for_publication(priority_queue_t *queue)
|
|
981
|
+
priority_queue_wait_for_publication(VALUE self, priority_queue_t *queue)
|
|
974
982
|
{
|
|
975
983
|
priority_queue_publication_wait_t wait = {
|
|
984
|
+
.self = self,
|
|
976
985
|
.queue = queue,
|
|
977
986
|
.acquired = false,
|
|
978
987
|
};
|
|
@@ -991,6 +1000,7 @@ priority_queue_wait_for_publication(priority_queue_t *queue)
|
|
|
991
1000
|
typedef VALUE (*priority_queue_operation_t)(VALUE opaque);
|
|
992
1001
|
|
|
993
1002
|
typedef struct {
|
|
1003
|
+
VALUE self;
|
|
994
1004
|
priority_queue_t *queue;
|
|
995
1005
|
priority_queue_operation_t operation;
|
|
996
1006
|
VALUE argument;
|
|
@@ -1001,7 +1011,7 @@ static VALUE
|
|
|
1001
1011
|
priority_queue_call_body(VALUE opaque)
|
|
1002
1012
|
{
|
|
1003
1013
|
priority_queue_call_t *call = (priority_queue_call_t *)opaque;
|
|
1004
|
-
priority_queue_lock(call->queue, &call->acquired);
|
|
1014
|
+
priority_queue_lock(call->self, call->queue, &call->acquired);
|
|
1005
1015
|
return call->operation(call->argument);
|
|
1006
1016
|
}
|
|
1007
1017
|
|
|
@@ -1016,12 +1026,14 @@ priority_queue_unlock_ensure(VALUE opaque)
|
|
|
1016
1026
|
|
|
1017
1027
|
static VALUE
|
|
1018
1028
|
priority_queue_call(
|
|
1029
|
+
VALUE self,
|
|
1019
1030
|
priority_queue_t *queue,
|
|
1020
1031
|
priority_queue_operation_t operation,
|
|
1021
1032
|
VALUE argument
|
|
1022
1033
|
)
|
|
1023
1034
|
{
|
|
1024
1035
|
priority_queue_call_t call = {
|
|
1036
|
+
.self = self,
|
|
1025
1037
|
.queue = queue,
|
|
1026
1038
|
.operation = operation,
|
|
1027
1039
|
.argument = argument,
|
|
@@ -1217,7 +1229,7 @@ priority_queue_initialize_commit(VALUE opaque)
|
|
|
1217
1229
|
queue->bounded = initialization->bounded;
|
|
1218
1230
|
queue->capacity = initialization->capacity;
|
|
1219
1231
|
queue->track_age = initialization->track_age;
|
|
1220
|
-
queue->signal
|
|
1232
|
+
RB_OBJ_WRITE(initialization->self, &queue->signal, initialization->signal);
|
|
1221
1233
|
/* Publication installs its logical gate before marking the object shareable
|
|
1222
1234
|
* and keeps that whole transition under nested ensure cleanup. */
|
|
1223
1235
|
if (queue->shared) return priority_queue_publish_shareable(initialization);
|
|
@@ -1278,7 +1290,7 @@ priority_queue_initialize(int argc, VALUE *argv, VALUE self)
|
|
|
1278
1290
|
priority_queue_check_initializable(self, queue);
|
|
1279
1291
|
|
|
1280
1292
|
return priority_queue_call(
|
|
1281
|
-
queue,
|
|
1293
|
+
self, queue,
|
|
1282
1294
|
priority_queue_initialize_commit,
|
|
1283
1295
|
(VALUE)&initialization
|
|
1284
1296
|
);
|
|
@@ -1363,7 +1375,7 @@ priority_queue_push_body(VALUE opaque)
|
|
|
1363
1375
|
priority_queue_identity_index_prepare_insert(bucket->identity_index, push->value);
|
|
1364
1376
|
entry = calloc(1, queue->track_age ? sizeof(priority_queue_tracked_value_t) : sizeof(priority_queue_value_t));
|
|
1365
1377
|
if (!entry) rb_memerror();
|
|
1366
|
-
entry->value
|
|
1378
|
+
RB_OBJ_WRITE(push->self, &entry->value, push->value);
|
|
1367
1379
|
entry->previous = bucket->tail;
|
|
1368
1380
|
entry->next = NULL;
|
|
1369
1381
|
entry->identity_next = NULL;
|
|
@@ -1377,7 +1389,7 @@ priority_queue_push_body(VALUE opaque)
|
|
|
1377
1389
|
free(entry);
|
|
1378
1390
|
rb_memerror();
|
|
1379
1391
|
}
|
|
1380
|
-
entry->value
|
|
1392
|
+
RB_OBJ_WRITE(push->self, &entry->value, push->value);
|
|
1381
1393
|
entry->previous = NULL;
|
|
1382
1394
|
entry->next = NULL;
|
|
1383
1395
|
entry->identity_next = NULL;
|
|
@@ -1393,7 +1405,7 @@ priority_queue_push_body(VALUE opaque)
|
|
|
1393
1405
|
else {
|
|
1394
1406
|
push->stored_priority = push->priority;
|
|
1395
1407
|
}
|
|
1396
|
-
bucket->priority
|
|
1408
|
+
RB_OBJ_WRITE(push->self, &bucket->priority, push->stored_priority);
|
|
1397
1409
|
if (CLASS_OF(push->priority) == rb_cFloat) {
|
|
1398
1410
|
bucket->float_priority = RFLOAT_VALUE(push->priority);
|
|
1399
1411
|
}
|
|
@@ -1475,7 +1487,7 @@ priority_queue_push(VALUE self, VALUE priority, VALUE value)
|
|
|
1475
1487
|
return priority_queue_push_locked((VALUE)&push);
|
|
1476
1488
|
}
|
|
1477
1489
|
return priority_queue_call(
|
|
1478
|
-
queue,
|
|
1490
|
+
self, queue,
|
|
1479
1491
|
priority_queue_push_locked,
|
|
1480
1492
|
(VALUE)&push
|
|
1481
1493
|
);
|
|
@@ -1559,7 +1571,7 @@ priority_queue_read(VALUE self, priority_queue_read_kind_t kind, bool last)
|
|
|
1559
1571
|
priority_queue_read_body((VALUE)&read);
|
|
1560
1572
|
}
|
|
1561
1573
|
else {
|
|
1562
|
-
priority_queue_call(queue, priority_queue_read_body, (VALUE)&read);
|
|
1574
|
+
priority_queue_call(self, queue, priority_queue_read_body, (VALUE)&read);
|
|
1563
1575
|
}
|
|
1564
1576
|
if (read.empty) return rb_block_given_p() ? rb_yield_values(0) : Qnil;
|
|
1565
1577
|
return read.result;
|
|
@@ -1596,7 +1608,7 @@ priority_queue_pop_before(VALUE self, VALUE latest_priority)
|
|
|
1596
1608
|
priority_queue_read_body((VALUE)&read);
|
|
1597
1609
|
}
|
|
1598
1610
|
else {
|
|
1599
|
-
priority_queue_call(queue, priority_queue_read_body, (VALUE)&read);
|
|
1611
|
+
priority_queue_call(self, queue, priority_queue_read_body, (VALUE)&read);
|
|
1600
1612
|
}
|
|
1601
1613
|
if (read.empty) return rb_block_given_p() ? rb_yield_values(0) : Qnil;
|
|
1602
1614
|
return read.result;
|
|
@@ -1692,7 +1704,7 @@ priority_queue_delete(VALUE self, VALUE priority, VALUE value)
|
|
|
1692
1704
|
if (queue->shared) priority_queue_check_shareable(priority);
|
|
1693
1705
|
if (queue->shared) priority_queue_check_shareable(value);
|
|
1694
1706
|
return priority_queue_call(
|
|
1695
|
-
queue,
|
|
1707
|
+
self, queue,
|
|
1696
1708
|
priority_queue_delete_body,
|
|
1697
1709
|
(VALUE)&deletion
|
|
1698
1710
|
);
|
|
@@ -1713,7 +1725,7 @@ priority_queue_delete_match(VALUE self, VALUE priority, VALUE pattern)
|
|
|
1713
1725
|
};
|
|
1714
1726
|
if (queue->shared) priority_queue_check_shareable(priority);
|
|
1715
1727
|
if (queue->shared) priority_queue_check_shareable(pattern);
|
|
1716
|
-
return priority_queue_call(queue, priority_queue_delete_body, (VALUE)&deletion);
|
|
1728
|
+
return priority_queue_call(self, queue, priority_queue_delete_body, (VALUE)&deletion);
|
|
1717
1729
|
}
|
|
1718
1730
|
|
|
1719
1731
|
static VALUE
|
|
@@ -1774,7 +1786,7 @@ priority_queue_delete_identity(VALUE self, VALUE priority, VALUE value)
|
|
|
1774
1786
|
if (queue->shared) priority_queue_check_shareable(priority);
|
|
1775
1787
|
if (queue->shared) priority_queue_check_shareable(value);
|
|
1776
1788
|
return priority_queue_call(
|
|
1777
|
-
queue,
|
|
1789
|
+
self, queue,
|
|
1778
1790
|
priority_queue_delete_identity_body,
|
|
1779
1791
|
(VALUE)&deletion
|
|
1780
1792
|
);
|
|
@@ -1799,7 +1811,7 @@ priority_queue_size(VALUE self)
|
|
|
1799
1811
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1800
1812
|
if (priority_queue_can_run_direct(queue)) return priority_queue_size_body((VALUE)&operation);
|
|
1801
1813
|
return priority_queue_call(
|
|
1802
|
-
queue,
|
|
1814
|
+
self, queue,
|
|
1803
1815
|
priority_queue_size_body,
|
|
1804
1816
|
(VALUE)&operation
|
|
1805
1817
|
);
|
|
@@ -1819,7 +1831,7 @@ priority_queue_empty(VALUE self)
|
|
|
1819
1831
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1820
1832
|
if (priority_queue_can_run_direct(queue)) return priority_queue_empty_body((VALUE)&operation);
|
|
1821
1833
|
return priority_queue_call(
|
|
1822
|
-
queue,
|
|
1834
|
+
self, queue,
|
|
1823
1835
|
priority_queue_empty_body,
|
|
1824
1836
|
(VALUE)&operation
|
|
1825
1837
|
);
|
|
@@ -1859,7 +1871,7 @@ priority_queue_seal(VALUE self)
|
|
|
1859
1871
|
priority_queue_t *queue = priority_queue_get(self);
|
|
1860
1872
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1861
1873
|
if (priority_queue_can_run_direct(queue)) return priority_queue_seal_body((VALUE)&operation);
|
|
1862
|
-
return priority_queue_call(queue, priority_queue_seal_body, (VALUE)&operation);
|
|
1874
|
+
return priority_queue_call(self, queue, priority_queue_seal_body, (VALUE)&operation);
|
|
1863
1875
|
}
|
|
1864
1876
|
|
|
1865
1877
|
static VALUE
|
|
@@ -1869,7 +1881,7 @@ priority_queue_clear(VALUE self)
|
|
|
1869
1881
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1870
1882
|
if (priority_queue_can_run_direct(queue)) return priority_queue_clear_body((VALUE)&operation);
|
|
1871
1883
|
return priority_queue_call(
|
|
1872
|
-
queue,
|
|
1884
|
+
self, queue,
|
|
1873
1885
|
priority_queue_clear_body,
|
|
1874
1886
|
(VALUE)&operation
|
|
1875
1887
|
);
|
|
@@ -1893,7 +1905,7 @@ priority_queue_close(VALUE self)
|
|
|
1893
1905
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1894
1906
|
if (priority_queue_can_run_direct(queue)) return priority_queue_close_body((VALUE)&operation);
|
|
1895
1907
|
return priority_queue_call(
|
|
1896
|
-
queue,
|
|
1908
|
+
self, queue,
|
|
1897
1909
|
priority_queue_close_body,
|
|
1898
1910
|
(VALUE)&operation
|
|
1899
1911
|
);
|
|
@@ -1913,7 +1925,7 @@ priority_queue_closed(VALUE self)
|
|
|
1913
1925
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1914
1926
|
if (priority_queue_can_run_direct(queue)) return priority_queue_closed_body((VALUE)&operation);
|
|
1915
1927
|
return priority_queue_call(
|
|
1916
|
-
queue,
|
|
1928
|
+
self, queue,
|
|
1917
1929
|
priority_queue_closed_body,
|
|
1918
1930
|
(VALUE)&operation
|
|
1919
1931
|
);
|
|
@@ -1932,7 +1944,7 @@ priority_queue_sealed(VALUE self)
|
|
|
1932
1944
|
priority_queue_t *queue = priority_queue_get(self);
|
|
1933
1945
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1934
1946
|
if (priority_queue_can_run_direct(queue)) return priority_queue_sealed_body((VALUE)&operation);
|
|
1935
|
-
return priority_queue_call(queue, priority_queue_sealed_body, (VALUE)&operation);
|
|
1947
|
+
return priority_queue_call(self, queue, priority_queue_sealed_body, (VALUE)&operation);
|
|
1936
1948
|
}
|
|
1937
1949
|
|
|
1938
1950
|
static VALUE
|
|
@@ -1955,7 +1967,7 @@ priority_queue_generation(VALUE self)
|
|
|
1955
1967
|
if (!queue->track_age) return Qnil;
|
|
1956
1968
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1957
1969
|
if (priority_queue_can_run_direct(queue)) return priority_queue_generation_body((VALUE)&operation);
|
|
1958
|
-
return priority_queue_call(queue, priority_queue_generation_body, (VALUE)&operation);
|
|
1970
|
+
return priority_queue_call(self, queue, priority_queue_generation_body, (VALUE)&operation);
|
|
1959
1971
|
}
|
|
1960
1972
|
|
|
1961
1973
|
static VALUE
|
|
@@ -1973,7 +1985,7 @@ priority_queue_oldest_enqueued_at(VALUE self)
|
|
|
1973
1985
|
if (!queue->track_age) return Qnil;
|
|
1974
1986
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1975
1987
|
if (priority_queue_can_run_direct(queue)) return priority_queue_oldest_body((VALUE)&operation);
|
|
1976
|
-
return priority_queue_call(queue, priority_queue_oldest_body, (VALUE)&operation);
|
|
1988
|
+
return priority_queue_call(self, queue, priority_queue_oldest_body, (VALUE)&operation);
|
|
1977
1989
|
}
|
|
1978
1990
|
|
|
1979
1991
|
static VALUE
|
|
@@ -1991,7 +2003,7 @@ priority_queue_oldest_age(VALUE self)
|
|
|
1991
2003
|
if (!queue->track_age) return Qnil;
|
|
1992
2004
|
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1993
2005
|
if (priority_queue_can_run_direct(queue)) return priority_queue_oldest_age_body((VALUE)&operation);
|
|
1994
|
-
return priority_queue_call(queue, priority_queue_oldest_age_body, (VALUE)&operation);
|
|
2006
|
+
return priority_queue_call(self, queue, priority_queue_oldest_age_body, (VALUE)&operation);
|
|
1995
2007
|
}
|
|
1996
2008
|
|
|
1997
2009
|
static VALUE
|
data/ext/farce/queue.c
CHANGED
|
@@ -198,7 +198,7 @@ queue_compact(void *pointer)
|
|
|
198
198
|
if (!queue->values) return;
|
|
199
199
|
size_t index = queue->head;
|
|
200
200
|
for (size_t remaining = queue->size; remaining > 0; remaining--) {
|
|
201
|
-
|
|
201
|
+
containers_gc_update(&queue->values[index]);
|
|
202
202
|
if (++index == queue->storage_capacity) index = 0;
|
|
203
203
|
}
|
|
204
204
|
}
|
|
@@ -231,7 +231,11 @@ static const rb_data_type_t queue_type = {
|
|
|
231
231
|
.dsize = queue_memsize,
|
|
232
232
|
.dcompact = queue_compact,
|
|
233
233
|
},
|
|
234
|
-
.flags =
|
|
234
|
+
.flags =
|
|
235
|
+
RUBY_TYPED_WB_PROTECTED |
|
|
236
|
+
RUBY_TYPED_FROZEN_SHAREABLE |
|
|
237
|
+
RUBY_TYPED_FREE_IMMEDIATELY |
|
|
238
|
+
FARCE_TYPED_THREAD_SAFE_FREE,
|
|
235
239
|
};
|
|
236
240
|
|
|
237
241
|
static const rb_data_type_t unshared_queue_type = {
|
|
@@ -243,6 +247,7 @@ static const rb_data_type_t unshared_queue_type = {
|
|
|
243
247
|
.dcompact = queue_compact,
|
|
244
248
|
},
|
|
245
249
|
.parent = &queue_type,
|
|
250
|
+
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE,
|
|
246
251
|
};
|
|
247
252
|
|
|
248
253
|
/* Unshared queues are confined to one Ractor. These critical sections call no
|
|
@@ -662,7 +667,8 @@ queue_capacity(VALUE self)
|
|
|
662
667
|
return queue->bounded ? SIZET2NUM(queue->limit) : Qnil;
|
|
663
668
|
}
|
|
664
669
|
|
|
665
|
-
/* Called with queue->lock held. The queue is unchanged if allocation fails.
|
|
670
|
+
/* Called with queue->lock held. The queue is unchanged if allocation fails.
|
|
671
|
+
* Ruby allocators could trigger GC while a peer is blocked on this mutex. */
|
|
666
672
|
static bool
|
|
667
673
|
queue_grow(queue_t *queue)
|
|
668
674
|
{
|
|
@@ -921,7 +927,7 @@ queue_push_with_timeout(VALUE self, queue_t *queue, VALUE value, queue_timeout_t
|
|
|
921
927
|
queue_unlock(queue);
|
|
922
928
|
rb_memerror();
|
|
923
929
|
}
|
|
924
|
-
queue->values[queue->tail]
|
|
930
|
+
RB_OBJ_WRITE(self, &queue->values[queue->tail], value);
|
|
925
931
|
if (queue->track_age) queue->enqueued_at[queue->tail] = monotonic_now();
|
|
926
932
|
if (++queue->tail == queue->storage_capacity) queue->tail = 0;
|
|
927
933
|
queue->size++;
|
data/ext/farce/reactor.c
CHANGED
|
@@ -9,21 +9,46 @@
|
|
|
9
9
|
// Tell Ruby's garbage collector which Ruby objects one fiber's wait still needs.
|
|
10
10
|
static int mark_wait(st_data_t key, st_data_t value, st_data_t arg) {
|
|
11
11
|
fs_wait *w = (fs_wait *)value;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
rb_gc_mark_movable(w->fiber); rb_gc_mark_movable(w->token); rb_gc_mark_movable(w->value);
|
|
13
|
+
rb_gc_mark_movable(w->error); rb_gc_mark_movable(w->groups); rb_gc_mark_movable(w->ios);
|
|
14
14
|
return ST_CONTINUE;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// Keep the scheduler's Ruby objects alive, including fibers that are starting or waiting.
|
|
18
18
|
static void mark(void *data) {
|
|
19
19
|
fs_state *s = data;
|
|
20
|
-
|
|
21
|
-
for (fs_admission *a = s->admission; a; a = a->previous) {
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
rb_gc_mark_movable(s->root); rb_gc_mark_movable(s->active);
|
|
21
|
+
for (fs_admission *a = s->admission; a; a = a->previous) { rb_gc_mark_movable(a->parent); rb_gc_mark_movable(a->child); }
|
|
22
|
+
rb_gc_mark_movable(s->owner);
|
|
23
|
+
rb_gc_mark_movable(s->backend); rb_gc_mark_movable(s->fallback);
|
|
24
24
|
if (s->fibers) st_foreach(s->fibers, mark_wait, 0);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Update wait references and the table's fiber identity keys together.
|
|
28
|
+
static int compact_wait(st_data_t key, st_data_t value, st_data_t arg, int error) {
|
|
29
|
+
fs_wait *w = (fs_wait *)value;
|
|
30
|
+
containers_gc_update(&w->fiber); containers_gc_update(&w->token);
|
|
31
|
+
containers_gc_update(&w->value); containers_gc_update(&w->error);
|
|
32
|
+
containers_gc_update(&w->groups); containers_gc_update(&w->ios);
|
|
33
|
+
return key == (st_data_t)w->fiber ? ST_CONTINUE : ST_REPLACE;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static int compact_wait_key(st_data_t *key, st_data_t *value, st_data_t arg, int existing) {
|
|
37
|
+
*key = (st_data_t)((fs_wait *)*value)->fiber;
|
|
38
|
+
return ST_CONTINUE;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static void compact(void *data) {
|
|
42
|
+
fs_state *s = data;
|
|
43
|
+
containers_gc_update(&s->root); containers_gc_update(&s->active);
|
|
44
|
+
containers_gc_update(&s->owner); containers_gc_update(&s->backend);
|
|
45
|
+
containers_gc_update(&s->fallback);
|
|
46
|
+
for (fs_admission *a = s->admission; a; a = a->previous) {
|
|
47
|
+
containers_gc_update(&a->parent); containers_gc_update(&a->child);
|
|
48
|
+
}
|
|
49
|
+
if (s->fibers) st_foreach_with_replace(s->fibers, compact_wait, compact_wait_key, 0);
|
|
50
|
+
}
|
|
51
|
+
|
|
27
52
|
// Free a fiber's wait record and any extra IO watches it owns.
|
|
28
53
|
// The first watch is stored inside the record and needs no separate free.
|
|
29
54
|
static int free_wait(st_data_t key, st_data_t value, st_data_t arg) {
|
|
@@ -97,8 +122,8 @@ static size_t memsize(const void *data) {
|
|
|
97
122
|
// Describe how Ruby should mark, free, and measure the native scheduler object.
|
|
98
123
|
const rb_data_type_t fs_type = {
|
|
99
124
|
.wrap_struct_name = "Farce::Internal::FiberScheduler",
|
|
100
|
-
.function = {.dmark = mark, .dfree = free_state, .dsize = memsize},
|
|
101
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
125
|
+
.function = {.dmark = mark, .dfree = free_state, .dsize = memsize, .dcompact = compact},
|
|
126
|
+
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE
|
|
102
127
|
};
|
|
103
128
|
|
|
104
129
|
// Create an empty scheduler in a closed state.
|
|
@@ -128,8 +153,8 @@ static VALUE initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
128
153
|
}
|
|
129
154
|
fs_state *s = fs_get(self);
|
|
130
155
|
if (!NIL_P(s->owner)) rb_raise(rb_eRuntimeError, "scheduler already initialized");
|
|
131
|
-
s->owner
|
|
132
|
-
s->root
|
|
156
|
+
RB_OBJ_WRITE(self, &s->owner, rb_thread_current());
|
|
157
|
+
RB_OBJ_WRITE(self, &s->root, rb_fiber_current());
|
|
133
158
|
s->fibers = st_init_numtable();
|
|
134
159
|
int driver = 0;
|
|
135
160
|
if (backend == ID2SYM(rb_intern("auto"))) {
|
|
@@ -144,7 +169,7 @@ static VALUE initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
144
169
|
else rb_raise(rb_eArgError, "unknown native scheduler backend");
|
|
145
170
|
int error = fs_driver_init(s, driver);
|
|
146
171
|
if (error) rb_syserr_fail(error, "requested fiber scheduler backend is unavailable");
|
|
147
|
-
s->backend
|
|
172
|
+
RB_OBJ_WRITE(self, &s->backend, backend);
|
|
148
173
|
if (s->descriptor >= 0) fcntl(s->descriptor, F_SETFD, FD_CLOEXEC);
|
|
149
174
|
if (pipe(s->wake) < 0) rb_sys_fail("scheduler wake pipe");
|
|
150
175
|
for (int i = 0; i < 2; i++) {
|
|
@@ -212,7 +237,7 @@ VALUE fs_arm_checked(VALUE self) {
|
|
|
212
237
|
fs_slot *slot = &s->slots[w->slot];
|
|
213
238
|
if (slot->generation == (UINT64_MAX >> (FS_SLOT_BITS + 2))) rb_raise(rb_eRangeError, "scheduler generation exhausted");
|
|
214
239
|
VALUE token = ULL2NUM((++slot->generation << FS_SLOT_BITS) | w->slot);
|
|
215
|
-
w->token
|
|
240
|
+
RB_OBJ_WRITE(self, &w->token, token); w->status = w->events = w->selected = w->native_error = 0;
|
|
216
241
|
w->value = w->error = w->groups = w->ios = Qnil;
|
|
217
242
|
s->pending++;
|
|
218
243
|
return token;
|
|
@@ -257,7 +282,7 @@ static fs_wait *ready_pop(fs_state *s) {
|
|
|
257
282
|
VALUE fs_resume(VALUE self, VALUE token, VALUE value, VALUE error) {
|
|
258
283
|
fs_state *s = fs_get(self); fs_wait *w = fs_lookup(s, token);
|
|
259
284
|
if (!w || w->status != 0) return Qfalse;
|
|
260
|
-
w->status = 1; w->value
|
|
285
|
+
w->status = 1; RB_OBJ_WRITE(self, &w->value, value); RB_OBJ_WRITE(self, &w->error, error);
|
|
261
286
|
ready_push(s, w); return Qtrue;
|
|
262
287
|
}
|
|
263
288
|
|
|
@@ -273,7 +298,7 @@ static VALUE interrupt(VALUE self, VALUE token, VALUE error) {
|
|
|
273
298
|
fs_state *s = fs_get(self); fs_wait *w = fs_lookup(s, token);
|
|
274
299
|
if (!w || w->status == 2) return Qfalse;
|
|
275
300
|
if (!w->status) ready_push(s, w);
|
|
276
|
-
w->status = 1; w->error
|
|
301
|
+
w->status = 1; RB_OBJ_WRITE(self, &w->error, error); return Qtrue;
|
|
277
302
|
}
|
|
278
303
|
|
|
279
304
|
// Declare the closed-IO check used below to detect descriptor reuse.
|
|
@@ -359,14 +384,14 @@ VALUE fs_retire(VALUE self, VALUE token) {
|
|
|
359
384
|
// Switch from the root to a ready fiber.
|
|
360
385
|
// If a newly started child finishes, return control to its waiting parent
|
|
361
386
|
// before returning to the root dispatcher.
|
|
362
|
-
static VALUE transfer_to(fs_state *s, VALUE fiber) {
|
|
363
|
-
s->active
|
|
387
|
+
static VALUE transfer_to(VALUE self, fs_state *s, VALUE fiber) {
|
|
388
|
+
RB_OBJ_WRITE(self, &s->active, fiber);
|
|
364
389
|
VALUE result = rb_fiber_transfer(fiber, 0, NULL);
|
|
365
390
|
s->active = Qnil;
|
|
366
391
|
// A transferred fiber returns to the thread root when its block finishes.
|
|
367
392
|
// Complete nested immediate admissions before returning to the driver.
|
|
368
393
|
while (s->admission && s->admission->parent != s->root) {
|
|
369
|
-
s->active
|
|
394
|
+
RB_OBJ_WRITE(self, &s->active, s->admission->parent);
|
|
370
395
|
result = rb_fiber_transfer(s->admission->parent, 0, NULL);
|
|
371
396
|
s->active = Qnil;
|
|
372
397
|
}
|
|
@@ -377,8 +402,8 @@ static VALUE transfer_to(fs_state *s, VALUE fiber) {
|
|
|
377
402
|
static VALUE start_body(VALUE data) {
|
|
378
403
|
VALUE *args = (VALUE *)data;
|
|
379
404
|
fs_state *s = fs_get(args[0]);
|
|
380
|
-
if (rb_fiber_current() == s->root) return transfer_to(s, args[1]);
|
|
381
|
-
s->active
|
|
405
|
+
if (rb_fiber_current() == s->root) return transfer_to(args[0], s, args[1]);
|
|
406
|
+
RB_OBJ_WRITE(args[0], &s->active, args[1]);
|
|
382
407
|
return rb_fiber_transfer(args[1], 0, NULL);
|
|
383
408
|
}
|
|
384
409
|
|
|
@@ -387,7 +412,7 @@ static VALUE start_body(VALUE data) {
|
|
|
387
412
|
static VALUE start_cleanup(VALUE data) {
|
|
388
413
|
VALUE *args = (VALUE *)data;
|
|
389
414
|
fs_state *s = fs_get(args[0]);
|
|
390
|
-
s->active
|
|
415
|
+
RB_OBJ_WRITE(args[0], &s->active, s->admission->parent == s->root ? Qnil : s->admission->parent);
|
|
391
416
|
s->admission = s->admission->previous;
|
|
392
417
|
return Qnil;
|
|
393
418
|
}
|
|
@@ -410,9 +435,11 @@ static VALUE start_fiber(VALUE self, VALUE fiber) {
|
|
|
410
435
|
index = s->slot_count++;
|
|
411
436
|
}
|
|
412
437
|
fs_wait *w = ZALLOC(fs_wait); w->slot = index; s->slots[index].wait = w;
|
|
413
|
-
w->fiber
|
|
438
|
+
RB_OBJ_WRITE(self, &w->fiber, fiber); w->token = w->value = w->error = w->groups = w->ios = Qnil;
|
|
414
439
|
st_insert(s->fibers, (st_data_t)fiber, (st_data_t)w);
|
|
415
440
|
fs_admission admission = {rb_fiber_current(), fiber, s->admission};
|
|
441
|
+
RB_OBJ_WRITTEN(self, Qundef, admission.parent);
|
|
442
|
+
RB_OBJ_WRITTEN(self, Qundef, admission.child);
|
|
416
443
|
s->admission = &admission;
|
|
417
444
|
VALUE args[] = {self, fiber};
|
|
418
445
|
return rb_ensure(start_body, (VALUE)args, start_cleanup, (VALUE)args);
|
|
@@ -441,9 +468,9 @@ static VALUE park_body(VALUE data) {
|
|
|
441
468
|
break;
|
|
442
469
|
}
|
|
443
470
|
}
|
|
444
|
-
s->active
|
|
471
|
+
RB_OBJ_WRITE(args[0], &s->active, next == s->root ? Qnil : next);
|
|
445
472
|
rb_fiber_transfer(next, 0, NULL);
|
|
446
|
-
s->active
|
|
473
|
+
RB_OBJ_WRITE(args[0], &s->active, current);
|
|
447
474
|
fs_wait *w = fs_lookup(fs_get(args[0]), args[1]);
|
|
448
475
|
if (!w) rb_raise(rb_eRuntimeError, "retired scheduler suspension");
|
|
449
476
|
if (!NIL_P(w->error)) rb_exc_raise(w->error);
|
|
@@ -486,7 +513,7 @@ VALUE fs_checkpoint(VALUE self, VALUE value) {
|
|
|
486
513
|
// Keep the original groups so the result can return the caller's objects.
|
|
487
514
|
static VALUE register_select(VALUE self, VALUE token, VALUE groups, VALUE ios) {
|
|
488
515
|
fs_state *s = fs_get(self); fs_wait *w = fs_lookup(s, token);
|
|
489
|
-
w->groups
|
|
516
|
+
RB_OBJ_WRITE(self, &w->groups, groups); RB_OBJ_WRITE(self, &w->ios, ios);
|
|
490
517
|
for (int i = 0; i < 3; i++) {
|
|
491
518
|
VALUE list = rb_ary_entry(ios, i);
|
|
492
519
|
for (long j = 0; j < RARRAY_LEN(list); j++)
|
|
@@ -728,7 +755,7 @@ drain_ready:;
|
|
|
728
755
|
if (w->status != 1) continue;
|
|
729
756
|
w->status = 2;
|
|
730
757
|
s->dispatch_left--;
|
|
731
|
-
transfer_to(s, w->fiber);
|
|
758
|
+
transfer_to(self, s, w->fiber);
|
|
732
759
|
}
|
|
733
760
|
int count = limit - s->dispatch_left;
|
|
734
761
|
s->dispatch_left = 0;
|
data/ext/farce/reactor.h
CHANGED
data/ext/farce/signal.c
CHANGED
|
@@ -119,7 +119,11 @@ static const rb_data_type_t containers_signal_type = {
|
|
|
119
119
|
.dsize = containers_signal_memsize,
|
|
120
120
|
.dcompact = NULL,
|
|
121
121
|
},
|
|
122
|
-
.flags =
|
|
122
|
+
.flags =
|
|
123
|
+
RUBY_TYPED_WB_PROTECTED |
|
|
124
|
+
RUBY_TYPED_FROZEN_SHAREABLE |
|
|
125
|
+
RUBY_TYPED_FREE_IMMEDIATELY |
|
|
126
|
+
FARCE_TYPED_THREAD_SAFE_FREE,
|
|
123
127
|
};
|
|
124
128
|
|
|
125
129
|
static VALUE
|