farce 0.4.1-x86_64-linux-gnu → 0.6.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 +98 -2
- data/README.md +3 -1
- data/docs/benchmarks.md +37 -21
- data/docs/compatibility.md +13 -0
- data/docs/data-structures.md +37 -0
- data/docs/integrations.md +1 -0
- data/docs/introduction.md +19 -0
- data/docs/modes.md +2 -0
- data/docs/scheduling.md +35 -3
- data/docs/scopes.md +3 -1
- data/docs/shims.md +53 -0
- data/docs/utilities.md +32 -0
- data/docs/variants.md +9 -0
- data/ext/farce/atom.c +23 -7
- data/ext/farce/containers.h +3 -0
- data/ext/farce/counter.c +4 -6
- data/ext/farce/depend +3 -1
- data/ext/farce/extconf.rb +19 -14
- data/ext/farce/farce.c +110 -3
- data/ext/farce/map.c +15 -7
- data/ext/farce/priority_queue.c +46 -1
- data/ext/farce/queue.c +141 -7
- data/ext/farce/trie.c +140 -72
- data/ext/farce/trie_search.h +114 -0
- data/ext/farce/vector.c +53 -21
- data/ext/farce/weak_map.c +16 -11
- data/ext/farce/weak_references.h +23 -0
- data/ext/java/org/farce/FiberScheduler.java +8 -6
- data/ext/java/org/farce/PriorityQueue.java +18 -2
- data/lib/farce/abstract/atom.rb +5 -1
- data/lib/farce/abstract/concurrent_map.rb +122 -0
- data/lib/farce/abstract/counter.rb +36 -1
- data/lib/farce/abstract/executor.rb +30 -7
- data/lib/farce/abstract/flag.rb +12 -0
- data/lib/farce/abstract/molecule.rb +14 -14
- data/lib/farce/abstract/pool.rb +158 -47
- data/lib/farce/abstract/priority_queue.rb +1 -1
- data/lib/farce/abstract/publisher.rb +189 -0
- data/lib/farce/abstract/queue.rb +29 -3
- data/lib/farce/abstract/scheduler.rb +41 -14
- data/lib/farce/abstract/timer_queue.rb +3 -3
- data/lib/farce/abstract/vector.rb +3 -1
- data/lib/farce/atom.rb +15 -2
- data/lib/farce/clock.rb +20 -7
- data/lib/farce/concurrent/count_down_latch.rb +37 -0
- data/lib/farce/concurrent/cyclic_barrier.rb +109 -0
- data/lib/farce/concurrent/event.rb +60 -0
- data/lib/farce/concurrent/fiber_local_var.rb +31 -0
- data/lib/farce/concurrent/local_var.rb +65 -0
- data/lib/farce/concurrent/thread_local_var.rb +28 -0
- data/lib/farce/concurrent/tuple.rb +74 -0
- data/lib/farce/concurrent.rb +50 -0
- data/lib/farce/config.rb +112 -2
- data/lib/farce/counter.rb +5 -15
- data/lib/farce/deduper.rb +12 -4
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/map.rb +8 -6
- data/lib/farce/engine/jruby/signal.rb +2 -2
- data/lib/farce/engine/jvm/counter.rb +0 -2
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/priority_queue.rb +14 -4
- 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/parallel_scheduler.rb +9 -1
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +1 -4
- data/lib/farce/engine/ruby/shared/weak_cache_mirror.rb +56 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +9 -12
- data/lib/farce/engine/ruby.rb +5 -1
- data/lib/farce/engine/shared/exchanger.rb +2 -2
- data/lib/farce/engine/shared/queue.rb +43 -5
- data/lib/farce/engine/shared/ractor_selector.rb +1 -1
- data/lib/farce/engine/shared/strict_map.rb +5 -0
- data/lib/farce/engine/shared/trie/ignore_case.rb +136 -0
- data/lib/farce/engine/shared/trie.rb +14 -1
- data/lib/farce/engine/shared/trie_builder.rb +19 -8
- data/lib/farce/engine/shared/unshared_atom.rb +1 -1
- data/lib/farce/engine/shared/unshared_vector.rb +11 -8
- data/lib/farce/engine/shared/unshared_weak_map.rb +8 -6
- data/lib/farce/engine/shared/vector.rb +2 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +5 -10
- data/lib/farce/engine/shared/weak_map/cell.rb +1 -1
- data/lib/farce/engine/shared.rb +2 -0
- data/lib/farce/engine/truffleruby/map.rb +8 -6
- data/lib/farce/engine/truffleruby/native/counter.rb +0 -2
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +18 -4
- data/lib/farce/engine/truffleruby/signal.rb +1 -1
- data/lib/farce/engine/truffleruby.rb +9 -0
- data/lib/farce/extension.rb +5 -3
- data/lib/farce/integrations/concurrent.rb +16 -3
- data/lib/farce/integrations/sorted_set.rb +1 -0
- data/lib/farce/internal/blocking_priority_queue.rb +4 -9
- data/lib/farce/internal/fake_ractor.rb +1 -1
- data/lib/farce/internal/flag_waiting.rb +1 -1
- data/lib/farce/internal/freeze.rb +1 -5
- data/lib/farce/internal/lease_initialization.rb +1 -1
- data/lib/farce/internal/lease_pool_state.rb +1 -1
- data/lib/farce/internal/lease_state.rb +2 -2
- data/lib/farce/internal/local_executor_methods.rb +3 -3
- data/lib/farce/internal/map_value_modes.rb +17 -9
- data/lib/farce/internal/pool_settings.rb +9 -0
- data/lib/farce/internal/pool_worker.rb +34 -21
- data/lib/farce/internal/reservation_waiting.rb +1 -1
- data/lib/farce/internal/scheduler_lifecycle.rb +2 -2
- data/lib/farce/internal/weak_map_value_modes.rb +5 -0
- data/lib/farce/internal.rb +19 -8
- data/lib/farce/lazy.rb +3 -0
- data/lib/farce/lazy_ref.rb +3 -0
- data/lib/farce/local/counter.rb +4 -10
- data/lib/farce/local/lease.rb +2 -9
- data/lib/farce/local/lease_map.rb +2 -9
- data/lib/farce/local/pool.rb +38 -5
- data/lib/farce/local/publisher.rb +30 -0
- data/lib/farce/local/scheduler.rb +18 -5
- data/lib/farce/local/vector.rb +3 -2
- data/lib/farce/map.rb +8 -1
- data/lib/farce/memoize.rb +319 -0
- data/lib/farce/pool.rb +5 -0
- data/lib/farce/priority_queue.rb +1 -1
- data/lib/farce/publisher.rb +57 -0
- data/lib/farce/shareable.rb +3 -0
- data/lib/farce/signal.rb +1 -1
- data/lib/farce/strict/publisher.rb +31 -0
- data/lib/farce/strict/vector.rb +3 -2
- data/lib/farce/strict/weak_cache.rb +199 -0
- data/lib/farce/subscription/definitions.rb +27 -0
- data/lib/farce/subscription.rb +121 -0
- data/lib/farce/transaction/map.rb +35 -1
- data/lib/farce/unshared/pool.rb +1 -1
- data/lib/farce/unshared/publisher.rb +25 -0
- data/lib/farce/unshared/scheduler.rb +3 -3
- data/lib/farce/unshared/vector.rb +3 -2
- data/lib/farce/vector.rb +20 -16
- data/lib/farce/version.rb +1 -1
- data/lib/farce/walker/definitions.rb +31 -2
- data/lib/farce/walker/modification.rb +28 -14
- data/lib/farce/walker.rb +9 -1
- data/lib/farce/weak_atom.rb +5 -2
- data/lib/farce/weak_key_map.rb +1 -1
- data/lib/farce/weak_map.rb +1 -1
- data/lib/farce/weak_value_map.rb +1 -1
- data/lib/farce.rb +3 -1
- metadata +23 -1
data/ext/farce/atom.c
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "containers.h"
|
|
2
2
|
#include "native_wait.h"
|
|
3
3
|
#include "transaction.h"
|
|
4
|
+
#include "weak_references.h"
|
|
4
5
|
#include "ruby/fiber/scheduler.h"
|
|
5
6
|
|
|
6
7
|
#include <errno.h>
|
|
@@ -12,11 +13,6 @@ static VALUE cAtom;
|
|
|
12
13
|
#ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
|
|
13
14
|
static VALUE cWeakAtom;
|
|
14
15
|
static VALUE cUnsharedWeakAtom;
|
|
15
|
-
|
|
16
|
-
/* These functions are exported by Rubies with the probed typed-data weak
|
|
17
|
-
* reference callback, but their declarations intentionally remain internal. */
|
|
18
|
-
void rb_gc_declare_weak_references(VALUE object);
|
|
19
|
-
bool rb_gc_handle_weak_references_alive_p(VALUE object);
|
|
20
16
|
#endif
|
|
21
17
|
|
|
22
18
|
typedef struct atom_waiter atom_waiter_t;
|
|
@@ -149,11 +145,11 @@ atom_compact(void *pointer)
|
|
|
149
145
|
/* The VM invokes this after marking while mutators are stopped. Taking the atom
|
|
150
146
|
* mutex here could deadlock if GC stopped a mutator while it owned the mutex. */
|
|
151
147
|
static void
|
|
152
|
-
atom_handle_weak_references
|
|
148
|
+
FARCE_WEAK_REFERENCES_CALLBACK(atom_handle_weak_references)
|
|
153
149
|
{
|
|
154
150
|
atom_t *atom = pointer;
|
|
155
151
|
if (!atom->weak || NIL_P(atom->value)) return;
|
|
156
|
-
if (
|
|
152
|
+
if (FARCE_WEAK_REFERENCE_ALIVE_P(atom->value)) return;
|
|
157
153
|
|
|
158
154
|
atom->value = Qnil;
|
|
159
155
|
atom_changed(atom);
|
|
@@ -500,6 +496,15 @@ static VALUE
|
|
|
500
496
|
atom_get(int argc, VALUE *argv, VALUE self)
|
|
501
497
|
{
|
|
502
498
|
atom_t *atom = get_atom(self);
|
|
499
|
+
if (argc == 0) {
|
|
500
|
+
pthread_mutex_lock(&atom->lock);
|
|
501
|
+
if (!atom->updating) {
|
|
502
|
+
VALUE value = atom->value;
|
|
503
|
+
pthread_mutex_unlock(&atom->lock);
|
|
504
|
+
return value;
|
|
505
|
+
}
|
|
506
|
+
pthread_mutex_unlock(&atom->lock);
|
|
507
|
+
}
|
|
503
508
|
atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
|
|
504
509
|
atom_execution_context_t execution = atom_current_execution_context();
|
|
505
510
|
|
|
@@ -607,6 +612,17 @@ static VALUE
|
|
|
607
612
|
atom_store_if_absent(int argc, VALUE *argv, VALUE self)
|
|
608
613
|
{
|
|
609
614
|
atom_t *atom = get_atom(self);
|
|
615
|
+
if (argc == 0) {
|
|
616
|
+
rb_check_frozen(self);
|
|
617
|
+
rb_need_block();
|
|
618
|
+
pthread_mutex_lock(&atom->lock);
|
|
619
|
+
if (!atom->updating && !NIL_P(atom->value)) {
|
|
620
|
+
VALUE value = atom->value;
|
|
621
|
+
pthread_mutex_unlock(&atom->lock);
|
|
622
|
+
return value;
|
|
623
|
+
}
|
|
624
|
+
pthread_mutex_unlock(&atom->lock);
|
|
625
|
+
}
|
|
610
626
|
atom_operation_t operation = {
|
|
611
627
|
.self = self,
|
|
612
628
|
.atom = atom,
|
data/ext/farce/containers.h
CHANGED
|
@@ -36,6 +36,9 @@ containers_check_typed_frozen(VALUE self)
|
|
|
36
36
|
|
|
37
37
|
void containers_check_shareable(VALUE value);
|
|
38
38
|
bool containers_strict_bool(VALUE value, const char *name);
|
|
39
|
+
VALUE containers_parse_constructor(
|
|
40
|
+
int argc, const VALUE *argv, const ID *keyword_ids, int keyword_count, VALUE *keyword_values
|
|
41
|
+
);
|
|
39
42
|
typedef void (*containers_native_reference_validator_t)(VALUE object);
|
|
40
43
|
VALUE containers_publish_native_reference_free(VALUE self);
|
|
41
44
|
VALUE containers_publish_native_with_references(
|
data/ext/farce/counter.c
CHANGED
|
@@ -338,8 +338,8 @@ counter_compare_and_set(VALUE self, VALUE expected_input, VALUE replacement_inpu
|
|
|
338
338
|
return exchanged ? Qtrue : Qfalse;
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
-
/*
|
|
342
|
-
* add/subtract
|
|
341
|
+
/* Farce::Counter inherits these coercing operations directly. Internal
|
|
342
|
+
* add/subtract callers require strict Integer arguments. */
|
|
343
343
|
static VALUE
|
|
344
344
|
counter_increment(int argc, VALUE *argv, VALUE self)
|
|
345
345
|
{
|
|
@@ -352,8 +352,7 @@ counter_increment(int argc, VALUE *argv, VALUE self)
|
|
|
352
352
|
rb_check_frozen(self);
|
|
353
353
|
}
|
|
354
354
|
long long delta = NUM2LL(input);
|
|
355
|
-
|
|
356
|
-
return self;
|
|
355
|
+
return counter_change(counter, delta, false);
|
|
357
356
|
}
|
|
358
357
|
|
|
359
358
|
static VALUE
|
|
@@ -368,8 +367,7 @@ counter_decrement(int argc, VALUE *argv, VALUE self)
|
|
|
368
367
|
rb_check_frozen(self);
|
|
369
368
|
}
|
|
370
369
|
long long delta = NUM2LL(input);
|
|
371
|
-
|
|
372
|
-
return self;
|
|
370
|
+
return counter_change(counter, delta, true);
|
|
373
371
|
}
|
|
374
372
|
|
|
375
373
|
void
|
data/ext/farce/depend
CHANGED
|
@@ -7,11 +7,13 @@ reactor.o: reactor.c reactor.h futex.h
|
|
|
7
7
|
drivers.o: drivers.c reactor.h futex.h
|
|
8
8
|
io.o: io.c reactor.h futex.h
|
|
9
9
|
tree_map.o: tree_map.c containers.h native_wait.h futex.h
|
|
10
|
+
trie.o: trie.c containers.h trie_search.h
|
|
10
11
|
|
|
11
12
|
queue.o: queue.c containers.h unshared_wait.h
|
|
12
13
|
unshared_signal.o: unshared_signal.c containers.h unshared_wait.h unshared_io_pool.h
|
|
13
14
|
|
|
14
|
-
atom.o: atom.c containers.h transaction.h native_wait.h futex.h
|
|
15
|
+
atom.o: atom.c containers.h transaction.h native_wait.h futex.h weak_references.h
|
|
16
|
+
weak_map.o: weak_map.c containers.h weak_references.h
|
|
15
17
|
map.o: map.c containers.h transaction.h native_wait.h futex.h
|
|
16
18
|
vector.o: vector.c containers.h transaction.h native_wait.h futex.h
|
|
17
19
|
signal.o: signal.c containers.h native_wait.h futex.h
|
data/ext/farce/extconf.rb
CHANGED
|
@@ -103,19 +103,21 @@ if epoll || kqueue
|
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
106
|
+
# The internal callback gained an object-space argument during Ruby 4.1
|
|
107
|
+
# development. Check its exact type, since incompatible assignments may only warn.
|
|
108
|
+
weak_callback_arguments = ["void *, void *", "void *"].find do |arguments|
|
|
109
|
+
try_compile(<<~SOURCE)
|
|
110
|
+
#include "ruby.h"
|
|
111
|
+
|
|
112
|
+
_Static_assert(
|
|
113
|
+
_Generic(((rb_data_type_t *)0)->function.handle_weak_references,
|
|
114
|
+
void (*)(#{arguments}): 1, default: 0),
|
|
115
|
+
"unsupported weak-reference callback"
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
int main(void) { return 0; }
|
|
119
|
+
SOURCE
|
|
120
|
+
end
|
|
119
121
|
|
|
120
122
|
weak_declare = have_func("rb_gc_declare_weak_references")
|
|
121
123
|
weak_alive = have_func("rb_gc_handle_weak_references_alive_p")
|
|
@@ -123,7 +125,10 @@ weak_alive = have_func("rb_gc_handle_weak_references_alive_p")
|
|
|
123
125
|
# Kazlib deliberately puts full-tree integrity walks behind assert(3).
|
|
124
126
|
# Enable them only while debugging the priority queue.
|
|
125
127
|
$defs << "-DNDEBUG" unless ENV["FARCE_PRIORITY_QUEUE_ASSERTIONS"] == "1"
|
|
126
|
-
|
|
128
|
+
if weak_callback_arguments && weak_declare && weak_alive
|
|
129
|
+
$defs << "-DRC_HAVE_NATIVE_WEAK_REFERENCES=1"
|
|
130
|
+
$defs << "-DFARCE_WEAK_REFERENCES_HAVE_OBJSPACE=1" if weak_callback_arguments == "void *, void *"
|
|
131
|
+
end
|
|
127
132
|
$defs << "-DFARCE_TRIE_FAILURE_INJECTION" if ENV["FARCE_TRIE_FAILURE_INJECTION"] == "1"
|
|
128
133
|
$CFLAGS = "#{$CFLAGS} -std=c11 -fvisibility=hidden -Wall -Wextra -Wno-unused-parameter -Wno-unused-function"
|
|
129
134
|
$LDFLAGS = "#{$LDFLAGS} -pthread" unless /mswin|mingw/ =~ RUBY_PLATFORM
|
data/ext/farce/farce.c
CHANGED
|
@@ -43,6 +43,40 @@ containers_strict_bool(VALUE value, const char *name)
|
|
|
43
43
|
rb_raise(rb_eArgError, "%s must be true or false", name);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
VALUE
|
|
47
|
+
containers_parse_constructor(
|
|
48
|
+
int argc, const VALUE *argv, const ID *keyword_ids, int keyword_count, VALUE *keyword_values
|
|
49
|
+
)
|
|
50
|
+
{
|
|
51
|
+
VALUE keywords = Qnil;
|
|
52
|
+
int positional_count = argc;
|
|
53
|
+
if (argc > 0 && rb_keyword_given_p()) keywords = argv[--positional_count];
|
|
54
|
+
rb_check_arity(positional_count, 0, 1);
|
|
55
|
+
|
|
56
|
+
int found = 0;
|
|
57
|
+
for (int index = 0; index < keyword_count; index++) {
|
|
58
|
+
VALUE value = NIL_P(keywords)
|
|
59
|
+
? Qundef
|
|
60
|
+
: rb_hash_lookup2(keywords, ID2SYM(keyword_ids[index]), Qundef);
|
|
61
|
+
keyword_values[index] = value;
|
|
62
|
+
if (value != Qundef) found++;
|
|
63
|
+
}
|
|
64
|
+
if (!NIL_P(keywords) && RHASH_SIZE(keywords) != (size_t)found) {
|
|
65
|
+
/* Ruby's unknown-keyword error path deletes recognized keys, even
|
|
66
|
+
* when no output values are requested. Copy only for that error. */
|
|
67
|
+
rb_get_kwargs(rb_hash_dup(keywords), keyword_ids, 0, keyword_count, keyword_values);
|
|
68
|
+
}
|
|
69
|
+
return positional_count ? argv[0] : Qnil;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static int
|
|
73
|
+
containers_find_public_ivar(ID name, VALUE value, st_data_t found)
|
|
74
|
+
{
|
|
75
|
+
if (!rb_is_instance_id(name)) return ST_CONTINUE;
|
|
76
|
+
*(bool *)found = true;
|
|
77
|
+
return ST_STOP;
|
|
78
|
+
}
|
|
79
|
+
|
|
46
80
|
static void
|
|
47
81
|
containers_check_native_publication_target(VALUE self)
|
|
48
82
|
{
|
|
@@ -53,15 +87,15 @@ containers_check_native_publication_target(VALUE self)
|
|
|
53
87
|
rb_raise(rb_eTypeError, "native publication requires a shareable typed-data descriptor");
|
|
54
88
|
}
|
|
55
89
|
|
|
56
|
-
|
|
57
|
-
|
|
90
|
+
bool has_public_ivar = false;
|
|
91
|
+
rb_ivar_foreach(self, containers_find_public_ivar, (st_data_t)&has_public_ivar);
|
|
92
|
+
if (has_public_ivar) {
|
|
58
93
|
rb_raise(
|
|
59
94
|
rb_eTypeError,
|
|
60
95
|
"%s cannot be published with Ruby instance variables",
|
|
61
96
|
rb_obj_classname(self)
|
|
62
97
|
);
|
|
63
98
|
}
|
|
64
|
-
RB_GC_GUARD(instance_variables);
|
|
65
99
|
}
|
|
66
100
|
|
|
67
101
|
VALUE
|
|
@@ -306,6 +340,14 @@ containers_raise_key_error(VALUE receiver, VALUE key)
|
|
|
306
340
|
rb_exc_raise(exception);
|
|
307
341
|
}
|
|
308
342
|
|
|
343
|
+
static VALUE
|
|
344
|
+
farce_value_changed(VALUE namespace, VALUE current, VALUE expected, VALUE identity)
|
|
345
|
+
{
|
|
346
|
+
if (current == expected) return Qfalse;
|
|
347
|
+
if (RTEST(identity)) return identity;
|
|
348
|
+
return RTEST(rb_equal(current, expected)) ? Qfalse : Qtrue;
|
|
349
|
+
}
|
|
350
|
+
|
|
309
351
|
static VALUE
|
|
310
352
|
farce_futex_supported(VALUE namespace)
|
|
311
353
|
{
|
|
@@ -319,6 +361,63 @@ farce_load_native_fiber_scheduler(VALUE namespace)
|
|
|
319
361
|
return Qnil;
|
|
320
362
|
}
|
|
321
363
|
|
|
364
|
+
#if RUBY_API_VERSION_CODE >= 40100
|
|
365
|
+
static rb_alloc_func_t farce_object_allocator;
|
|
366
|
+
static VALUE farce_shareable_inclusion;
|
|
367
|
+
|
|
368
|
+
static VALUE
|
|
369
|
+
farce_new_shareable(int argc, VALUE *argv, VALUE klass)
|
|
370
|
+
{
|
|
371
|
+
/* Ordinary ivar writes then include the hidden ID in the class's size
|
|
372
|
+
* hint, keeping later instances embedded instead of spilling at publication.
|
|
373
|
+
* Leave the allocator unchanged: Ruby's copy path requires a root shape. */
|
|
374
|
+
VALUE object = rb_obj_alloc(klass);
|
|
375
|
+
if (RB_TYPE_P(object, T_OBJECT)) rb_obj_id(object);
|
|
376
|
+
rb_obj_call_init_kw(object, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
|
377
|
+
return object;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
static VALUE
|
|
381
|
+
farce_prepare_shareable_class(VALUE namespace, VALUE base)
|
|
382
|
+
{
|
|
383
|
+
if (RB_TYPE_P(base, T_CLASS) && !RB_FL_TEST_RAW(base, RUBY_FL_SINGLETON)) {
|
|
384
|
+
ID id_new = rb_intern("new");
|
|
385
|
+
if (rb_get_alloc_func(base) == farce_object_allocator &&
|
|
386
|
+
rb_method_basic_definition_p(rb_singleton_class(base), id_new)) {
|
|
387
|
+
VALUE constructor = rb_obj_method(base, ID2SYM(id_new));
|
|
388
|
+
if (rb_funcall(constructor, rb_intern("owner"), 0) == rb_cClass) {
|
|
389
|
+
rb_define_singleton_method(base, "new", farce_new_shareable, -1);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else if (RB_TYPE_P(base, T_MODULE)) {
|
|
394
|
+
rb_prepend_module(rb_singleton_class(base), farce_shareable_inclusion);
|
|
395
|
+
}
|
|
396
|
+
return base;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
static VALUE
|
|
400
|
+
farce_shareable_included(VALUE self, VALUE base)
|
|
401
|
+
{
|
|
402
|
+
VALUE result = rb_call_super(1, &base);
|
|
403
|
+
farce_prepare_shareable_class(Qnil, base);
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
#endif
|
|
407
|
+
|
|
408
|
+
static VALUE
|
|
409
|
+
farce_publish_shareable(VALUE namespace, VALUE object)
|
|
410
|
+
{
|
|
411
|
+
#if RUBY_API_VERSION_CODE >= 40100
|
|
412
|
+
/* A frozen facade can bypass Ruby's object-ID preallocation during
|
|
413
|
+
* make_shareable. Reserve the ID before freezing so a later object_id
|
|
414
|
+
* cannot resize shared ivar storage while another Ractor reads it. */
|
|
415
|
+
rb_obj_id(object);
|
|
416
|
+
#endif
|
|
417
|
+
rb_obj_freeze(object);
|
|
418
|
+
return rb_ractor_make_shareable(object);
|
|
419
|
+
}
|
|
420
|
+
|
|
322
421
|
#if RUBY_API_VERSION_CODE == 30400
|
|
323
422
|
static VALUE
|
|
324
423
|
farce_lock_native_thread(VALUE namespace)
|
|
@@ -336,6 +435,14 @@ Init_farce(void)
|
|
|
336
435
|
VALUE mInternal = rb_const_get(mFarce, rb_intern("Internal"));
|
|
337
436
|
VALUE singleton = rb_singleton_class(mInternal);
|
|
338
437
|
eIsolationError = rb_const_get(rb_cRactor, rb_intern("IsolationError"));
|
|
438
|
+
rb_define_singleton_method(mInternal, "value_changed?", farce_value_changed, 3);
|
|
439
|
+
#if RUBY_API_VERSION_CODE >= 40100
|
|
440
|
+
farce_object_allocator = rb_get_alloc_func(rb_cObject);
|
|
441
|
+
farce_shareable_inclusion = rb_define_module_under(mInternal, "ShareableConstruction");
|
|
442
|
+
rb_define_method(farce_shareable_inclusion, "included", farce_shareable_included, 1);
|
|
443
|
+
rb_define_singleton_method(mInternal, "prepare_shareable_class", farce_prepare_shareable_class, 1);
|
|
444
|
+
#endif
|
|
445
|
+
rb_define_singleton_method(mInternal, "publish_shareable", farce_publish_shareable, 1);
|
|
339
446
|
#if RUBY_API_VERSION_CODE == 30400
|
|
340
447
|
rb_define_singleton_method(mInternal, "lock_native_thread", farce_lock_native_thread, 0);
|
|
341
448
|
#endif
|
data/ext/farce/map.c
CHANGED
|
@@ -850,8 +850,6 @@ map_initialize_entry(VALUE key, VALUE value, VALUE opaque)
|
|
|
850
850
|
static VALUE
|
|
851
851
|
map_initialize(int argc, VALUE *argv, VALUE self)
|
|
852
852
|
{
|
|
853
|
-
VALUE mapping = Qnil;
|
|
854
|
-
VALUE keywords = Qnil;
|
|
855
853
|
ID keyword_ids[] = {
|
|
856
854
|
rb_intern("compare_by_identity"),
|
|
857
855
|
rb_intern("compare_keys_by_identity"),
|
|
@@ -860,8 +858,7 @@ map_initialize(int argc, VALUE *argv, VALUE self)
|
|
|
860
858
|
VALUE keyword_values[3] = {Qundef, Qundef, Qundef};
|
|
861
859
|
map_t *map;
|
|
862
860
|
|
|
863
|
-
|
|
864
|
-
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 3, keyword_values);
|
|
861
|
+
VALUE mapping = containers_parse_constructor(argc, argv, keyword_ids, 3, keyword_values);
|
|
865
862
|
TypedData_Get_Struct(self, map_t, &map_type, map);
|
|
866
863
|
if (map->initialized) rb_raise(rb_eRuntimeError, "Map is already initialized");
|
|
867
864
|
|
|
@@ -1417,9 +1414,15 @@ map_modify_body(VALUE opaque)
|
|
|
1417
1414
|
}
|
|
1418
1415
|
|
|
1419
1416
|
static VALUE
|
|
1420
|
-
map_modify(VALUE
|
|
1417
|
+
map_modify(int argc, VALUE *argv, VALUE self)
|
|
1421
1418
|
{
|
|
1422
1419
|
rb_check_frozen(self);
|
|
1420
|
+
VALUE key;
|
|
1421
|
+
VALUE keywords = Qnil;
|
|
1422
|
+
VALUE keyword_values[1] = {Qundef};
|
|
1423
|
+
ID keyword_ids[] = {rb_intern("timeout")};
|
|
1424
|
+
rb_scan_args(argc, argv, "1:", &key, &keywords);
|
|
1425
|
+
if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
|
|
1423
1426
|
rb_need_block();
|
|
1424
1427
|
map_t *map = get_map(self);
|
|
1425
1428
|
key = map_prepare_key(map, key);
|
|
@@ -1432,7 +1435,12 @@ map_modify(VALUE self, VALUE key)
|
|
|
1432
1435
|
};
|
|
1433
1436
|
operation.hash = map_key_hash(map, key);
|
|
1434
1437
|
rb_check_frozen(self);
|
|
1435
|
-
|
|
1438
|
+
map_timeout_t timeout = map_parse_timeout(
|
|
1439
|
+
keyword_values[0] == Qundef ? Qnil : keyword_values[0]
|
|
1440
|
+
);
|
|
1441
|
+
if (!map_lock_for_key_with_timeout(
|
|
1442
|
+
map, key, operation.hash, &timeout, &operation.execution
|
|
1443
|
+
)) return Qfalse;
|
|
1436
1444
|
bool found;
|
|
1437
1445
|
size_t index = map_find_slot(map, key, operation.hash, &found, &operation.execution);
|
|
1438
1446
|
if (RB_OBJ_FROZEN(self)) {
|
|
@@ -2151,7 +2159,7 @@ map_define_methods(VALUE klass)
|
|
|
2151
2159
|
rb_define_method(klass, "store", map_store_with_timeout, -1);
|
|
2152
2160
|
rb_define_method(klass, "swap", map_swap, -1);
|
|
2153
2161
|
rb_define_method(klass, "update", map_update, -1);
|
|
2154
|
-
rb_define_method(klass, "modify", map_modify, 1);
|
|
2162
|
+
rb_define_method(klass, "modify", map_modify, -1);
|
|
2155
2163
|
rb_define_method(klass, "wait_until_changed", map_wait_until_changed, -1);
|
|
2156
2164
|
rb_define_method(klass, "wait_until_non_nil", map_wait_until_non_nil, -1);
|
|
2157
2165
|
rb_define_method(klass, "store_if_absent", map_store_if_absent, -1);
|
data/ext/farce/priority_queue.c
CHANGED
|
@@ -1964,11 +1964,55 @@ priority_queue_oldest_age(VALUE self)
|
|
|
1964
1964
|
return priority_queue_call(self, queue, priority_queue_oldest_age_body, (VALUE)&operation);
|
|
1965
1965
|
}
|
|
1966
1966
|
|
|
1967
|
+
static VALUE
|
|
1968
|
+
priority_queue_capacity_body(VALUE opaque)
|
|
1969
|
+
{
|
|
1970
|
+
priority_queue_self_operation_t *operation = (priority_queue_self_operation_t *)opaque;
|
|
1971
|
+
return operation->queue->bounded ? SIZET2NUM(operation->queue->capacity) : Qnil;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1967
1974
|
static VALUE
|
|
1968
1975
|
priority_queue_capacity(VALUE self)
|
|
1969
1976
|
{
|
|
1970
1977
|
priority_queue_t *queue = priority_queue_get(self);
|
|
1971
|
-
|
|
1978
|
+
priority_queue_self_operation_t operation = {.queue = queue, .self = self};
|
|
1979
|
+
if (priority_queue_can_run_direct(queue)) return priority_queue_capacity_body((VALUE)&operation);
|
|
1980
|
+
return priority_queue_call(self, queue, priority_queue_capacity_body, (VALUE)&operation);
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
typedef struct {
|
|
1984
|
+
priority_queue_t *queue;
|
|
1985
|
+
VALUE value;
|
|
1986
|
+
size_t capacity;
|
|
1987
|
+
bool bounded;
|
|
1988
|
+
} priority_queue_resize_t;
|
|
1989
|
+
|
|
1990
|
+
static VALUE
|
|
1991
|
+
priority_queue_set_capacity_body(VALUE opaque)
|
|
1992
|
+
{
|
|
1993
|
+
priority_queue_resize_t *resize = (priority_queue_resize_t *)opaque;
|
|
1994
|
+
priority_queue_notify_before_commit(resize->queue);
|
|
1995
|
+
resize->queue->capacity = resize->capacity;
|
|
1996
|
+
resize->queue->bounded = resize->bounded;
|
|
1997
|
+
return resize->value;
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
static VALUE
|
|
2001
|
+
priority_queue_set_capacity(VALUE self, VALUE value)
|
|
2002
|
+
{
|
|
2003
|
+
priority_queue_t *queue = priority_queue_get(self);
|
|
2004
|
+
priority_queue_resize_t resize = {.queue = queue, .value = value, .capacity = 0, .bounded = !NIL_P(value)};
|
|
2005
|
+
if (resize.bounded) {
|
|
2006
|
+
resize.value = rb_to_int(value);
|
|
2007
|
+
long long capacity = NUM2LL(resize.value);
|
|
2008
|
+
if (capacity <= 0) rb_raise(rb_eArgError, "capacity must be positive or nil");
|
|
2009
|
+
if ((unsigned long long)capacity > (unsigned long long)SIZE_MAX) {
|
|
2010
|
+
rb_raise(rb_eArgError, "capacity is too large");
|
|
2011
|
+
}
|
|
2012
|
+
resize.capacity = (size_t)capacity;
|
|
2013
|
+
}
|
|
2014
|
+
if (priority_queue_can_run_direct(queue)) return priority_queue_set_capacity_body((VALUE)&resize);
|
|
2015
|
+
return priority_queue_call(self, queue, priority_queue_set_capacity_body, (VALUE)&resize);
|
|
1972
2016
|
}
|
|
1973
2017
|
|
|
1974
2018
|
static void
|
|
@@ -1999,6 +2043,7 @@ priority_queue_define_methods(VALUE klass)
|
|
|
1999
2043
|
rb_define_method(klass, "oldest_enqueued_at", priority_queue_oldest_enqueued_at, 0);
|
|
2000
2044
|
rb_define_method(klass, "oldest_age", priority_queue_oldest_age, 0);
|
|
2001
2045
|
rb_define_method(klass, "capacity", priority_queue_capacity, 0);
|
|
2046
|
+
rb_define_method(klass, "capacity=", priority_queue_set_capacity, 1);
|
|
2002
2047
|
rb_define_method(klass, "freeze", priority_queue_freeze, 0);
|
|
2003
2048
|
rb_define_method(klass, "frozen?", priority_queue_frozen_p, 0);
|
|
2004
2049
|
}
|