Skip to content

Commit e8451e5

Browse files
committed
Modernize the concurrent-ruby-ext C extension
Replace the per-platform CAS and memory-barrier code in `CAtomicReference`, `CAtomicBoolean`, and `CAtomicFixnum` with the helpers in `ruby/atomic.h`, and switch the three classes from the old `DATA_PTR` / `RUBY_NEVER_FREE` pattern to `TypedData_Make_Struct` with proper `dmark`, `dfree`, `dsize`, and (on Ruby >= 2.7) `dcompact`. All atomic primitives (load, set, CAS, exchange) are `static inline` in `atomic_reference.h` and shared across the three classes via a `CR_DEFINE_ATOMIC_DATA_TYPE` macro that keeps the three `rb_data_type_t` definitions in lock-step. Stores publish via `RUBY_ATOMIC_VALUE_SET` (or `__atomic_store_n` on Ruby < 4.0) so the "java volatile" semantics the docstring advertises are preserved across the modernization, and the corresponding `RB_OBJ_WRITTEN` announces the write barrier to the GC. `AtomicReference#get_and_set` now uses a single atomic exchange instead of a CAS retry loop. Each primitive prefers the `ruby/atomic.h` helper when its specific macro is defined and falls back to GCC/Clang `__atomic_*` builtins otherwise, since `RUBY_ATOMIC_PTR_LOAD` only landed in Ruby 3.3 and `RUBY_ATOMIC_VALUE_SET` in 4.0. GC compaction support is gated on `RUBY_API_VERSION_CODE >= 20700` via the `CR_GC_COMPACTION` macro; older Rubies fall back to `rb_gc_mark`. `extconf.rb` drops the `libkern/OSAtomic.h` probe and the `-march` flags, which no longer matter once we're on `ruby/atomic.h` or the compiler atomic builtins. No behavioral change to any public API; the spec suite passes unchanged, including the atomic specs under `GC.stress`.
1 parent df8706d commit e8451e5

7 files changed

Lines changed: 261 additions & 212 deletions

File tree

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,67 @@
1-
#include <ruby.h>
2-
31
#include "atomic_boolean.h"
42
#include "atomic_reference.h"
53

6-
void atomic_boolean_mark(void *value) {
7-
rb_gc_mark_maybe((VALUE) value);
8-
}
9-
10-
const rb_data_type_t atomic_boolean_type = {
11-
"Concurrent::CAtomicBoolean",
12-
{
13-
atomic_boolean_mark,
14-
RUBY_NEVER_FREE,
15-
},
16-
};
4+
CR_DEFINE_ATOMIC_DATA_TYPE(atomic_boolean_type, "Concurrent::CAtomicBoolean");
175

186
VALUE atomic_boolean_allocate(VALUE klass) {
19-
return rb_data_typed_object_wrap(klass, (void *) Qfalse, &atomic_boolean_type);
7+
cr_atomic_t *atomic;
8+
VALUE obj = TypedData_Make_Struct(klass, cr_atomic_t, &atomic_boolean_type, atomic);
9+
RB_OBJ_WRITE(obj, &atomic->value, Qfalse);
10+
return obj;
2011
}
2112

22-
VALUE method_atomic_boolean_initialize(int argc, VALUE* argv, VALUE self) {
13+
VALUE method_atomic_boolean_initialize(int argc, VALUE *argv, VALUE self) {
14+
cr_atomic_t *atomic;
2315
VALUE value = Qfalse;
16+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_boolean_type, atomic);
17+
2418
rb_check_arity(argc, 0, 1);
2519
if (argc == 1) value = TRUTHY(argv[0]);
26-
DATA_PTR(self) = (void *) value;
27-
return(self);
20+
RB_OBJ_WRITE(self, &atomic->value, value);
21+
return self;
2822
}
2923

3024
VALUE method_atomic_boolean_value(VALUE self) {
31-
return(ir_get(self));
25+
cr_atomic_t *atomic;
26+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_boolean_type, atomic);
27+
return cr_atomic_value_load(atomic);
3228
}
3329

3430
VALUE method_atomic_boolean_value_set(VALUE self, VALUE value) {
31+
cr_atomic_t *atomic;
3532
VALUE new_value = TRUTHY(value);
36-
return(ir_set(self, new_value));
33+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_boolean_type, atomic);
34+
cr_atomic_value_set(atomic, new_value);
35+
RB_OBJ_WRITTEN(self, Qundef, new_value);
36+
return new_value;
3737
}
3838

3939
VALUE method_atomic_boolean_true_question(VALUE self) {
40-
return(method_atomic_boolean_value(self));
40+
return method_atomic_boolean_value(self);
4141
}
4242

4343
VALUE method_atomic_boolean_false_question(VALUE self) {
44-
VALUE current = method_atomic_boolean_value(self);
45-
return(current == Qfalse ? Qtrue : Qfalse);
44+
return method_atomic_boolean_value(self) == Qfalse ? Qtrue : Qfalse;
4645
}
4746

4847
VALUE method_atomic_boolean_make_true(VALUE self) {
49-
return(ir_compare_and_set(self, Qfalse, Qtrue));
48+
cr_atomic_t *atomic;
49+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_boolean_type, atomic);
50+
51+
if (cr_atomic_value_cas(atomic, Qfalse, Qtrue) == Qfalse) {
52+
RB_OBJ_WRITTEN(self, Qfalse, Qtrue);
53+
return Qtrue;
54+
}
55+
return Qfalse;
5056
}
5157

5258
VALUE method_atomic_boolean_make_false(VALUE self) {
53-
return(ir_compare_and_set(self, Qtrue, Qfalse));
59+
cr_atomic_t *atomic;
60+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_boolean_type, atomic);
61+
62+
if (cr_atomic_value_cas(atomic, Qtrue, Qfalse) == Qtrue) {
63+
RB_OBJ_WRITTEN(self, Qtrue, Qfalse);
64+
return Qtrue;
65+
}
66+
return Qfalse;
5467
}
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
#ifndef __ATOMIC_BOOLEAN_H__
2-
#define __ATOMIC_BOOLEAN_H__
1+
#ifndef CONCURRENT_RUBY_ATOMIC_BOOLEAN_H
2+
#define CONCURRENT_RUBY_ATOMIC_BOOLEAN_H 1
33

4-
#define TRUTHY(value)(value == Qfalse || value == Qnil ? Qfalse : Qtrue)
4+
#include <ruby.h>
55

6-
void atomic_boolean_mark(void*);
7-
VALUE atomic_boolean_allocate(VALUE);
8-
VALUE method_atomic_boolean_initialize(int, VALUE*, VALUE);
9-
VALUE method_atomic_boolean_value(VALUE);
10-
VALUE method_atomic_boolean_value_set(VALUE, VALUE);
11-
VALUE method_atomic_boolean_true_question(VALUE);
12-
VALUE method_atomic_boolean_false_question(VALUE);
13-
VALUE method_atomic_boolean_make_true(VALUE);
14-
VALUE method_atomic_boolean_make_false(VALUE);
6+
#define TRUTHY(value) (((value) == Qfalse || (value) == Qnil) ? Qfalse : Qtrue)
7+
8+
VALUE atomic_boolean_allocate(VALUE klass);
9+
VALUE method_atomic_boolean_initialize(int argc, VALUE *argv, VALUE self);
10+
VALUE method_atomic_boolean_value(VALUE self);
11+
VALUE method_atomic_boolean_value_set(VALUE self, VALUE value);
12+
VALUE method_atomic_boolean_true_question(VALUE self);
13+
VALUE method_atomic_boolean_false_question(VALUE self);
14+
VALUE method_atomic_boolean_make_true(VALUE self);
15+
VALUE method_atomic_boolean_make_false(VALUE self);
1516

1617
#endif

ext/concurrent-ruby-ext/atomic_fixnum.c

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,101 @@
1-
#include <ruby.h>
2-
31
#include "atomic_fixnum.h"
42
#include "atomic_reference.h"
53

6-
void atomic_fixnum_mark(void *value) {
7-
rb_gc_mark_maybe((VALUE) value);
8-
}
9-
10-
const rb_data_type_t atomic_fixnum_type = {
11-
"Concurrent::CAtomicFixnum",
12-
{
13-
atomic_fixnum_mark,
14-
RUBY_NEVER_FREE,
15-
},
16-
};
4+
CR_DEFINE_ATOMIC_DATA_TYPE(atomic_fixnum_type, "Concurrent::CAtomicFixnum");
175

186
VALUE atomic_fixnum_allocate(VALUE klass) {
19-
return rb_data_typed_object_wrap(klass, (void *) Qnil, &atomic_fixnum_type);
7+
cr_atomic_t *atomic;
8+
VALUE obj = TypedData_Make_Struct(klass, cr_atomic_t, &atomic_fixnum_type, atomic);
9+
RB_OBJ_WRITE(obj, &atomic->value, Qnil);
10+
return obj;
2011
}
2112

22-
VALUE method_atomic_fixnum_initialize(int argc, VALUE* argv, VALUE self) {
13+
VALUE method_atomic_fixnum_initialize(int argc, VALUE *argv, VALUE self) {
14+
cr_atomic_t *atomic;
2315
VALUE value = LL2NUM(0);
16+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_fixnum_type, atomic);
17+
2418
rb_check_arity(argc, 0, 1);
2519
if (argc == 1) {
2620
Check_Type(argv[0], T_FIXNUM);
2721
value = argv[0];
2822
}
29-
DATA_PTR(self) = (void *) value;
30-
return(self);
23+
RB_OBJ_WRITE(self, &atomic->value, value);
24+
return self;
3125
}
3226

3327
VALUE method_atomic_fixnum_value(VALUE self) {
34-
return (VALUE) DATA_PTR(self);
28+
cr_atomic_t *atomic;
29+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_fixnum_type, atomic);
30+
return cr_atomic_value_load(atomic);
3531
}
3632

3733
VALUE method_atomic_fixnum_value_set(VALUE self, VALUE value) {
34+
cr_atomic_t *atomic;
3835
Check_Type(value, T_FIXNUM);
39-
DATA_PTR(self) = (void *) value;
40-
return(value);
36+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_fixnum_type, atomic);
37+
cr_atomic_value_set(atomic, value);
38+
RB_OBJ_WRITTEN(self, Qundef, value);
39+
return value;
4140
}
4241

43-
VALUE method_atomic_fixnum_increment(int argc, VALUE* argv, VALUE self) {
44-
long long value = NUM2LL((VALUE) DATA_PTR(self));
42+
VALUE method_atomic_fixnum_increment(int argc, VALUE *argv, VALUE self) {
43+
cr_atomic_t *atomic;
4544
long long delta = 1;
45+
VALUE new_value;
46+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_fixnum_type, atomic);
47+
4648
rb_check_arity(argc, 0, 1);
4749
if (argc == 1) {
4850
Check_Type(argv[0], T_FIXNUM);
4951
delta = NUM2LL(argv[0]);
5052
}
51-
return method_atomic_fixnum_value_set(self, LL2NUM(value + delta));
53+
new_value = LL2NUM(NUM2LL(cr_atomic_value_load(atomic)) + delta);
54+
cr_atomic_value_set(atomic, new_value);
55+
RB_OBJ_WRITTEN(self, Qundef, new_value);
56+
return new_value;
5257
}
5358

54-
VALUE method_atomic_fixnum_decrement(int argc, VALUE* argv, VALUE self) {
55-
long long value = NUM2LL((VALUE) DATA_PTR(self));
59+
VALUE method_atomic_fixnum_decrement(int argc, VALUE *argv, VALUE self) {
60+
cr_atomic_t *atomic;
5661
long long delta = 1;
62+
VALUE new_value;
63+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_fixnum_type, atomic);
64+
5765
rb_check_arity(argc, 0, 1);
5866
if (argc == 1) {
5967
Check_Type(argv[0], T_FIXNUM);
6068
delta = NUM2LL(argv[0]);
6169
}
62-
return method_atomic_fixnum_value_set(self, LL2NUM(value - delta));
70+
new_value = LL2NUM(NUM2LL(cr_atomic_value_load(atomic)) - delta);
71+
cr_atomic_value_set(atomic, new_value);
72+
RB_OBJ_WRITTEN(self, Qundef, new_value);
73+
return new_value;
6374
}
6475

65-
VALUE method_atomic_fixnum_compare_and_set(VALUE self, VALUE rb_expect, VALUE rb_update) {
66-
Check_Type(rb_expect, T_FIXNUM);
67-
Check_Type(rb_update, T_FIXNUM);
68-
return ir_compare_and_set(self, rb_expect, rb_update);
76+
VALUE method_atomic_fixnum_compare_and_set(VALUE self, VALUE expect, VALUE update) {
77+
cr_atomic_t *atomic;
78+
Check_Type(expect, T_FIXNUM);
79+
Check_Type(update, T_FIXNUM);
80+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_fixnum_type, atomic);
81+
82+
if (cr_atomic_value_cas(atomic, expect, update) == expect) {
83+
RB_OBJ_WRITTEN(self, expect, update);
84+
return Qtrue;
85+
}
86+
return Qfalse;
6987
}
7088

7189
VALUE method_atomic_fixnum_update(VALUE self) {
90+
cr_atomic_t *atomic;
7291
VALUE old_value, new_value;
92+
TypedData_Get_Struct(self, cr_atomic_t, &atomic_fixnum_type, atomic);
93+
7394
for (;;) {
74-
old_value = method_atomic_fixnum_value(self);
95+
old_value = cr_atomic_value_load(atomic);
7596
new_value = rb_yield(old_value);
76-
if (ir_compare_and_set(self, old_value, new_value) == Qtrue) {
97+
if (cr_atomic_value_cas(atomic, old_value, new_value) == old_value) {
98+
RB_OBJ_WRITTEN(self, old_value, new_value);
7799
return new_value;
78100
}
79101
}
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
#ifndef __ATOMIC_FIXNUM_H__
2-
#define __ATOMIC_FIXNUM_H__
1+
#ifndef CONCURRENT_RUBY_ATOMIC_FIXNUM_H
2+
#define CONCURRENT_RUBY_ATOMIC_FIXNUM_H 1
33

4-
void atomic_fixnum_mark(void*);
5-
VALUE atomic_fixnum_allocate(VALUE);
6-
VALUE method_atomic_fixnum_initialize(int, VALUE*, VALUE);
7-
VALUE method_atomic_fixnum_value(VALUE);
8-
VALUE method_atomic_fixnum_value_set(VALUE, VALUE);
9-
VALUE method_atomic_fixnum_increment(int, VALUE*, VALUE);
10-
VALUE method_atomic_fixnum_decrement(int, VALUE*, VALUE);
11-
VALUE method_atomic_fixnum_compare_and_set(VALUE, VALUE, VALUE);
12-
VALUE method_atomic_fixnum_update(VALUE);
4+
#include <ruby.h>
5+
6+
VALUE atomic_fixnum_allocate(VALUE klass);
7+
VALUE method_atomic_fixnum_initialize(int argc, VALUE *argv, VALUE self);
8+
VALUE method_atomic_fixnum_value(VALUE self);
9+
VALUE method_atomic_fixnum_value_set(VALUE self, VALUE value);
10+
VALUE method_atomic_fixnum_increment(int argc, VALUE *argv, VALUE self);
11+
VALUE method_atomic_fixnum_decrement(int argc, VALUE *argv, VALUE self);
12+
VALUE method_atomic_fixnum_compare_and_set(VALUE self, VALUE expect, VALUE update);
13+
VALUE method_atomic_fixnum_update(VALUE self);
1314

1415
#endif

0 commit comments

Comments
 (0)