farce 0.5.0-aarch64-linux-gnu → 0.6.0-aarch64-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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/docs/modes.md +1 -1
  4. data/docs/utilities.md +1 -0
  5. data/ext/farce/atom.c +23 -7
  6. data/ext/farce/containers.h +3 -0
  7. data/ext/farce/counter.c +4 -6
  8. data/ext/farce/depend +3 -1
  9. data/ext/farce/extconf.rb +19 -14
  10. data/ext/farce/farce.c +101 -3
  11. data/ext/farce/map.c +1 -4
  12. data/ext/farce/trie.c +140 -72
  13. data/ext/farce/trie_search.h +114 -0
  14. data/ext/farce/vector.c +50 -18
  15. data/ext/farce/weak_map.c +4 -8
  16. data/ext/farce/weak_references.h +23 -0
  17. data/ext/java/org/farce/FiberScheduler.java +8 -6
  18. data/lib/farce/abstract/atom.rb +5 -1
  19. data/lib/farce/abstract/counter.rb +5 -4
  20. data/lib/farce/abstract/molecule.rb +14 -14
  21. data/lib/farce/abstract/vector.rb +1 -1
  22. data/lib/farce/atom.rb +6 -0
  23. data/lib/farce/clock.rb +8 -7
  24. data/lib/farce/counter.rb +6 -0
  25. data/lib/farce/deduper.rb +12 -4
  26. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  27. data/lib/farce/engine/jvm/counter.rb +0 -2
  28. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  29. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  30. data/lib/farce/engine/ruby/shared/weak_cache_mirror.rb +56 -0
  31. data/lib/farce/engine/ruby.rb +5 -1
  32. data/lib/farce/engine/shared/trie/ignore_case.rb +136 -0
  33. data/lib/farce/engine/shared/trie.rb +14 -1
  34. data/lib/farce/engine/shared/trie_builder.rb +19 -8
  35. data/lib/farce/engine/shared/unshared_vector.rb +3 -1
  36. data/lib/farce/engine/shared/vector.rb +2 -0
  37. data/lib/farce/engine/shared.rb +2 -0
  38. data/lib/farce/engine/truffleruby/native/counter.rb +0 -2
  39. data/lib/farce/engine/truffleruby.rb +9 -0
  40. data/lib/farce/extension.rb +5 -3
  41. data/lib/farce/integrations/sorted_set.rb +1 -0
  42. data/lib/farce/internal/fake_ractor.rb +1 -1
  43. data/lib/farce/internal/freeze.rb +1 -5
  44. data/lib/farce/internal/map_value_modes.rb +8 -7
  45. data/lib/farce/internal.rb +11 -0
  46. data/lib/farce/local/counter.rb +4 -10
  47. data/lib/farce/map.rb +8 -1
  48. data/lib/farce/memoize.rb +10 -5
  49. data/lib/farce/shareable.rb +3 -0
  50. data/lib/farce/strict/weak_cache.rb +199 -0
  51. data/lib/farce/vector.rb +8 -7
  52. data/lib/farce/version.rb +1 -1
  53. data/lib/farce/walker/definitions.rb +18 -0
  54. data/lib/farce/walker/modification.rb +28 -14
  55. data/lib/farce/walker.rb +9 -1
  56. data/lib/farce/weak_key_map.rb +1 -1
  57. data/lib/farce/weak_map.rb +1 -1
  58. data/lib/farce/weak_value_map.rb +1 -1
  59. metadata +6 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29c08153d7bd4d5a305e7e70f260fef0cba2e5dfa7f1e8dc99af163dcfd5baf3
4
- data.tar.gz: 107b066e56e91ec2f7964c2df67674e8828bdf48cceede77ba5a3685c58f4251
3
+ metadata.gz: 5e2c2a98a1e2d92757f55523e856de7f0d8e0cb0c2387101a2f020d1aacba7e6
4
+ data.tar.gz: 1e1f813f06c4d852affb7d2651405905adcb9446252609efb39a6debfc33f7f3
5
5
  SHA512:
6
- metadata.gz: b142d0d80c4767b6806333dd4d77bd7c278fbb85e43a8ffb6a6cbf55e6806ab6b175662a9e51f65ad85aa004c9419d05d0cf102adb0ca1ad49772c5efa4f7812
7
- data.tar.gz: 5d1b89f719415cf79788306f5ec62fdf493a88f474d4e43a5fe7e5ec936998dcf89ce982b4846e2c6b056994206ef6b8475419ffa4d380c76bbcc1478549e478
6
+ metadata.gz: a66f1a280334d4cb989df1bbeb8ec2e5823a4783b3ba88c47cdd9f576576d466231aa9c1d13faa8c5f758c358ec15874b6761e80e67e0bf850b36cd3e1af696e
7
+ data.tar.gz: e16ea65e445755b5849350870c4e995d5cec0ab2ce0943da6bd9343f12e5b36c27a0357c895ebd249e5c8138f9d3ad9e71a459163ed23131701555e7057bbbdb
data/CHANGELOG.md CHANGED
@@ -6,6 +6,7 @@ Farce follows [Semantic Versioning 2.0](http://semver.org/). Any public method o
6
6
 
7
7
  Version | Date | Important Changes
8
8
  --------------------------------|------------|--------------------------------------
9
+ [0.6.0](#farce-060-2026-11-04) | 2026-11-04 | Performance improvements, better counters, and `WeakCache`
9
10
  [0.5.0](#farce-050-2026-10-21) | 2026-10-21 | Publishers, memoization, pool and queue resizing, and better concurrent-ruby compatibility
10
11
  [0.4.1](#farce-041-2026-10-18) | 2026-10-18 | Include improved io_uring support in prebuilt Linux binaries
11
12
  [0.4.0](#farce-040-2026-10-16) | 2026-10-16 | Introduce `Farce::Backports`
@@ -15,6 +16,23 @@ Farce follows [Semantic Versioning 2.0](http://semver.org/). Any public method o
15
16
 
16
17
  ## Release History
17
18
 
19
+ ### Farce 0.6.0 (2026-11-04)
20
+
21
+ #### Breaking Changes
22
+
23
+ * `Counter` and `Local::Counter` arithmetic methods (`increment`, `add`, `decrement`, `subtract`, and `remove`) now return the integer produced by the atomic update instead of the counter. Use `number = counter.increment` to allocate sequence numbers without a separate read. Calls can no longer be chained.
24
+
25
+ #### New Features
26
+
27
+ * `Farce.dedup` can now deduplicate `Regexp`, `Range`, `Data`, and `SortedSet` instances.
28
+ * `Farce::Walker` (and thus `Farce.freeze_graph`) correctly walk `Struct` and `Range` instances now.
29
+ * Add `Farce::Strict::WeakCache`. It offers a subset of the API of `Farce::Strict::WeakValueMap`, but without its performance penalty on CRuby 3.x and 4.0 (on other Rubies, including CRuby 4.1, it's just a tiny wrapper around the well-performing `WeakValueMap`).
30
+
31
+ #### Performance improvements
32
+
33
+ * Faster construction of Map and Vector instances.
34
+ * Speed up atoms and memoization.
35
+
18
36
  ### Farce 0.5.0 (2026-10-21)
19
37
 
20
38
  #### New Features
data/docs/modes.md CHANGED
@@ -390,7 +390,7 @@ The same choices appear in several Farce APIs. Containers normally default to `:
390
390
  | `Farce::Exchanger` | `new`, `exchange` | The value offered to a partner. |
391
391
  | `Farce::WeakAtom` | `new`, `store`, `update`, and other replacement operations | Weakly held values. Supports only `:raise`, `:make_shareable`, and `:dedup`. Defaults to `:raise`. |
392
392
  | `Farce::Atom` | `new`, `store`, `swap`, `update`, and other replacement operations | The stored value. |
393
- | `Farce::Memoize` | `memoize`, `[]` | Memoized results with `scope: :global` (the default). With `weak: true`, supports only `:raise`, `:make_shareable`, and `:dedup`, and defaults to `:raise`. Other scopes ignore the mode. |
393
+ | `Farce::Memoize` | `memoize`, `[]` | Memoized results with `scope: :global` (the default). `:strict` caches shareable results directly, including with `weak: true`. Weak memoization also accepts `:raise` (the default), `:make_shareable`, and `:dedup`. Other scopes ignore the mode. |
394
394
  | `Farce::Lazy`, `Farce::LazyRef` | `new` | The result computed once by the factory. |
395
395
  | `Farce::Task` | `new`, `schedule`, `try_schedule`, `in_parallel`, `on_main`, `add_argument` | Arguments and the successful result. `add_argument` overrides the mode for one argument. Scheduling can select local mode automatically. |
396
396
  | `Farce::Publisher`, `Farce::Unshared::Publisher`, `Farce::Local::Publisher` | `subscribe` with a block | Callback arguments and queued payloads. Defaults to `:smart` for `Publisher` and `:local` for its Unshared and Local variants. |
data/docs/utilities.md CHANGED
@@ -59,6 +59,7 @@ Other classes Farce provides include:
59
59
  * `ThreadExecutor`: A [task executor](scheduling.md#scheduling-code) creating a new thread for each unit of work. Used on TruffleRuby.
60
60
  * `Walker`: A tool for walking a Ruby object tree.
61
61
  * `WeakValue`: A value object version of [`WeakRef`](shims.md#weakref). Allows handling references more explicitly, without automatic method delegation.
62
+ * `Strict::WeakCache`: Ractor-shared weak maps are slow on CRuby before 4.1. `WeakCache` provides a faster alternative if entries aren't frequently garbage collected, but it does not allow deleting or replacing entries manually.
62
63
 
63
64
  In addition, Farce includes a range of error classes not listed here. Check the [API documentation](https://rkh.github.io/farce/) or [code base](https://github.com/rkh/farce/blob/main/lib/farce/error.rb) for these.
64
65
 
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(void *pointer)
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 (rb_gc_handle_weak_references_alive_p(atom->value)) return;
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,
@@ -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
- /* The chainable operations are inherited directly by Farce::Counter. Keep
342
- * add/subtract as strict, value-returning primitives for internal callers. */
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
- (void)counter_change(counter, delta, false);
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
- (void)counter_change(counter, delta, true);
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
- weak_callback = try_compile(<<~SOURCE)
107
- #include "ruby.h"
108
-
109
- static void handle_weak_references(void *pointer) { (void)pointer; }
110
- static const rb_data_type_t probe_type = {
111
- .wrap_struct_name = "weak-reference-probe",
112
- .function = {
113
- .handle_weak_references = handle_weak_references,
114
- },
115
- };
116
-
117
- int main(void) { return probe_type.function.handle_weak_references == 0; }
118
- SOURCE
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
- $defs << "-DRC_HAVE_NATIVE_WEAK_REFERENCES=1" if weak_callback && weak_declare && weak_alive
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
- VALUE instance_variables = rb_obj_instance_variables(self);
57
- if (RARRAY_LEN(instance_variables) != 0) {
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
@@ -327,6 +361,63 @@ farce_load_native_fiber_scheduler(VALUE namespace)
327
361
  return Qnil;
328
362
  }
329
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
+
330
421
  #if RUBY_API_VERSION_CODE == 30400
331
422
  static VALUE
332
423
  farce_lock_native_thread(VALUE namespace)
@@ -345,6 +436,13 @@ Init_farce(void)
345
436
  VALUE singleton = rb_singleton_class(mInternal);
346
437
  eIsolationError = rb_const_get(rb_cRactor, rb_intern("IsolationError"));
347
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);
348
446
  #if RUBY_API_VERSION_CODE == 30400
349
447
  rb_define_singleton_method(mInternal, "lock_native_thread", farce_lock_native_thread, 0);
350
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
- rb_scan_args(argc, argv, "01:", &mapping, &keywords);
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