Ruby 4.1.0dev (2026-10-24 revision 83c63ffc9e6699e61ccfbfc8e568d2b7b1319c42)
atomic.h
Go to the documentation of this file.
1#ifndef RUBY_ATOMIC_H /*-*-C++-*-vi:se ft=cpp:*/
2#define RUBY_ATOMIC_H
27#include "ruby/internal/config.h"
28
29#ifdef STDC_HEADERS
30# include <stddef.h> /* size_t */
31#endif
32
33#ifdef HAVE_SYS_TYPES_H
34# include <sys/types.h> /* ssize_t */
35#endif
36
37#if defined(_MSC_VER)
38# pragma intrinsic(_InterlockedOr)
39#elif defined(__sun) && defined(HAVE_ATOMIC_H)
40# include <atomic.h>
41#endif
42
43#include "ruby/assert.h"
44#include "ruby/backward/2/limits.h"
49#include "ruby/internal/cast.h"
50#include "ruby/internal/value.h"
53
54/*
55 * Asserts that your environment supports more than one atomic types. These
56 * days systems tend to have such property (C11 was a standard of decades ago,
57 * right?) but we still support older ones.
58 */
59#if defined(__DOXYGEN__) || defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
60# define RUBY_ATOMIC_GENERIC_MACRO 1
61#endif
62
68#if defined(__DOXYGEN__)
69using rb_atomic_t = std::atomic<unsigned>;
70#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
71typedef unsigned int rb_atomic_t;
72#elif defined(HAVE_GCC_SYNC_BUILTINS)
73typedef unsigned int rb_atomic_t;
74#elif defined(_WIN32)
75# include <winsock2.h> // to prevent macro redefinitions
76# include <windows.h> // for `LONG` and `Interlocked` functions
77typedef LONG rb_atomic_t;
78#elif defined(__sun) && defined(HAVE_ATOMIC_H)
79typedef unsigned int rb_atomic_t;
80#elif defined(HAVE_STDATOMIC_H)
81# include <stdatomic.h>
82typedef unsigned int rb_atomic_t;
83#else
84# error No atomic operation found
85#endif
86
87/* Memory ordering constants */
88#if defined(HAVE_GCC_ATOMIC_BUILTINS)
89# define RBIMPL_ATOMIC_RELAXED __ATOMIC_RELAXED
90# define RBIMPL_ATOMIC_ACQUIRE __ATOMIC_ACQUIRE
91# define RBIMPL_ATOMIC_RELEASE __ATOMIC_RELEASE
92# define RBIMPL_ATOMIC_ACQ_REL __ATOMIC_ACQ_REL
93# define RBIMPL_ATOMIC_SEQ_CST __ATOMIC_SEQ_CST
94#elif defined(HAVE_STDATOMIC_H)
95# define RBIMPL_ATOMIC_RELAXED memory_order_relaxed
96# define RBIMPL_ATOMIC_ACQUIRE memory_order_acquire
97# define RBIMPL_ATOMIC_RELEASE memory_order_release
98# define RBIMPL_ATOMIC_ACQ_REL memory_order_acq_rel
99# define RBIMPL_ATOMIC_SEQ_CST memory_order_seq_cst
100#else
101/* Dummy values for unsupported platforms */
102# define RBIMPL_ATOMIC_RELAXED 0
103# define RBIMPL_ATOMIC_ACQUIRE 1
104# define RBIMPL_ATOMIC_RELEASE 2
105# define RBIMPL_ATOMIC_ACQ_REL 3
106# define RBIMPL_ATOMIC_SEQ_CST 4
107#endif
108
118#define RUBY_ATOMIC_FETCH_ADD(var, val) rbimpl_atomic_fetch_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
119
129#define RUBY_ATOMIC_FETCH_SUB(var, val) rbimpl_atomic_fetch_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
130
141#define RUBY_ATOMIC_OR(var, val) rbimpl_atomic_or(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
142
152#define RUBY_ATOMIC_EXCHANGE(var, val) rbimpl_atomic_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
153
165#define RUBY_ATOMIC_CAS(var, oldval, newval) \
166 rbimpl_atomic_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
167
175#define RUBY_ATOMIC_LOAD(var) rbimpl_atomic_load(&(var), RBIMPL_ATOMIC_SEQ_CST)
176
185#define RUBY_ATOMIC_SET(var, val) rbimpl_atomic_store(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
186
195#define RUBY_ATOMIC_ADD(var, val) rbimpl_atomic_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
196
205#define RUBY_ATOMIC_SUB(var, val) rbimpl_atomic_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
206
214#define RUBY_ATOMIC_INC(var) rbimpl_atomic_inc(&(var), RBIMPL_ATOMIC_SEQ_CST)
215
223#define RUBY_ATOMIC_DEC(var) rbimpl_atomic_dec(&(var), RBIMPL_ATOMIC_SEQ_CST)
224
235#define RUBY_ATOMIC_SIZE_FETCH_ADD(var, val) rbimpl_atomic_size_fetch_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
236
246#define RUBY_ATOMIC_SIZE_INC(var) rbimpl_atomic_size_inc(&(var), RBIMPL_ATOMIC_SEQ_CST)
247
257#define RUBY_ATOMIC_SIZE_DEC(var) rbimpl_atomic_size_dec(&(var), RBIMPL_ATOMIC_SEQ_CST)
258
270#define RUBY_ATOMIC_SIZE_EXCHANGE(var, val) \
271 rbimpl_atomic_size_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
272
284#define RUBY_ATOMIC_SIZE_CAS(var, oldval, newval) \
285 rbimpl_atomic_size_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
286
295#define RUBY_ATOMIC_SIZE_LOAD(var) rbimpl_atomic_size_load(&(var), RBIMPL_ATOMIC_SEQ_CST)
296
307#define RUBY_ATOMIC_SIZE_ADD(var, val) rbimpl_atomic_size_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
308
319#define RUBY_ATOMIC_SIZE_SUB(var, val) rbimpl_atomic_size_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
320
337#define RUBY_ATOMIC_PTR_EXCHANGE(var, val) \
338 RBIMPL_CAST(rbimpl_atomic_ptr_exchange((void **)&(var), (void *)val, RBIMPL_ATOMIC_SEQ_CST))
339
348#define RUBY_ATOMIC_PTR_LOAD(var) \
349 RBIMPL_CAST(rbimpl_atomic_ptr_load((void **)&var, RBIMPL_ATOMIC_SEQ_CST))
350
361#define RUBY_ATOMIC_PTR_SET(var, val) \
362 rbimpl_atomic_ptr_store((volatile void **)&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
363
375#define RUBY_ATOMIC_PTR_CAS(var, oldval, newval) \
376 RBIMPL_CAST(rbimpl_atomic_ptr_cas((void **)&(var), (void *)(oldval), (void *)(newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST))
377
388#define RUBY_ATOMIC_VALUE_SET(var, val) \
389 rbimpl_atomic_value_store(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
390
402#define RUBY_ATOMIC_VALUE_EXCHANGE(var, val) \
403 rbimpl_atomic_value_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
404
416#define RUBY_ATOMIC_VALUE_CAS(var, oldval, newval) \
417 rbimpl_atomic_value_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
418
423static inline rb_atomic_t
424rbimpl_atomic_fetch_add(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
425{
426 (void)memory_order;
427#if 0
428
429#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
430 return __atomic_fetch_add(ptr, val, memory_order);
431
432#elif defined(HAVE_GCC_SYNC_BUILTINS)
433 return __sync_fetch_and_add(ptr, val);
434
435#elif defined(_WIN32)
436 return InterlockedExchangeAdd(ptr, val);
437
438#elif defined(__sun) && defined(HAVE_ATOMIC_H)
439 /*
440 * `atomic_add_int_nv` takes its second argument as `int`! Meanwhile our
441 * `rb_atomic_t` is unsigned. We cannot pass `val` as-is. We have to
442 * manually check integer overflow.
443 */
444 RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX);
445 return atomic_add_int_nv(ptr, val) - val;
446
447#elif defined(HAVE_STDATOMIC_H)
448 return atomic_fetch_add_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
449
450#else
451# error Unsupported platform.
452#endif
453}
454
459static inline size_t
460rbimpl_atomic_size_fetch_add(volatile size_t *ptr, size_t val, int memory_order)
461{
462 (void)memory_order;
463#if 0
464
465#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
466 return __atomic_fetch_add(ptr, val, memory_order);
467
468#elif defined(HAVE_GCC_SYNC_BUILTINS)
469 return __sync_fetch_and_add(ptr, val);
470
471#elif defined(_WIN64)
472 return InterlockedExchangeAdd64(RBIMPL_CAST((LONG64 *)ptr), val);
473
474#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
475 /* Ditto for `atomic_add_int_nv`. */
476 RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX);
477 atomic_add_long(ptr, val);
478
479#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
480 RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
481
482 volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
483 return rbimpl_atomic_fetch_add(tmp, val, memory_order);
484
485#elif defined(HAVE_STDATOMIC_H)
486 return atomic_fetch_add_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
487
488#else
489# error Unsupported platform.
490#endif
491}
492
496static inline void
497rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
498{
499 (void)memory_order;
500#if 0
501
502#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
503 /*
504 * GCC on amd64 is smart enough to detect this `__atomic_add_fetch`'s
505 * return value is not used, then compiles it into single `LOCK ADD`
506 * instruction.
507 */
508 __atomic_add_fetch(ptr, val, memory_order);
509
510#elif defined(HAVE_GCC_SYNC_BUILTINS)
511 __sync_add_and_fetch(ptr, val);
512
513#elif defined(_WIN32)
514 /*
515 * `InterlockedExchangeAdd` is `LOCK XADD`. It seems there also is
516 * `_InterlockedAdd` intrinsic in ARM Windows but not for x86? Sticking to
517 * `InterlockedExchangeAdd` for better portability.
518 */
519 InterlockedExchangeAdd(ptr, val);
520
521#elif defined(__sun) && defined(HAVE_ATOMIC_H)
522 /* Ditto for `atomic_add_int_nv`. */
523 RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX);
524 atomic_add_int(ptr, val);
525
526#elif defined(HAVE_STDATOMIC_H)
527 atomic_fetch_add_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
528
529#else
530# error Unsupported platform.
531#endif
532}
533
537static inline void
538rbimpl_atomic_size_add(volatile size_t *ptr, size_t val, int memory_order)
539{
540 (void)memory_order;
541#if 0
542
543#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
544 __atomic_add_fetch(ptr, val, memory_order);
545
546#elif defined(HAVE_GCC_SYNC_BUILTINS)
547 __sync_add_and_fetch(ptr, val);
548
549#elif defined(_WIN64)
550 /* Ditto for `InterlockeExchangedAdd`. */
551 InterlockedExchangeAdd64(RBIMPL_CAST((LONG64 *)ptr), val);
552
553#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
554 /* Ditto for `atomic_add_int_nv`. */
555 RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX);
556 atomic_add_long(ptr, val);
557
558#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
559 RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
560
561 volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
562 rbimpl_atomic_add(tmp, val, memory_order);
563
564#elif defined(HAVE_STDATOMIC_H)
565 atomic_fetch_add_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
566
567#else
568# error Unsupported platform.
569#endif
570}
571
575static inline void
576rbimpl_atomic_inc(volatile rb_atomic_t *ptr, int memory_order)
577{
578 (void)memory_order;
579#if 0
580
581#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
582 rbimpl_atomic_add(ptr, 1, memory_order);
583
584#elif defined(_WIN32)
585 InterlockedIncrement(ptr);
586
587#elif defined(__sun) && defined(HAVE_ATOMIC_H)
588 atomic_inc_uint(ptr);
589
590#elif defined(HAVE_STDATOMIC_H)
591 rbimpl_atomic_add(ptr, 1, memory_order);
592
593#else
594# error Unsupported platform.
595#endif
596}
597
601static inline void
602rbimpl_atomic_size_inc(volatile size_t *ptr, int memory_order)
603{
604 (void)memory_order;
605#if 0
606
607#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
608 rbimpl_atomic_size_add(ptr, 1, memory_order);
609
610#elif defined(_WIN64)
611 InterlockedIncrement64(RBIMPL_CAST((LONG64 *)ptr));
612
613#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
614 atomic_inc_ulong(ptr);
615
616#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
617 RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
618
619 rbimpl_atomic_size_add(ptr, 1, memory_order);
620
621#elif defined(HAVE_STDATOMIC_H)
622 rbimpl_atomic_size_add(ptr, 1, memory_order);
623
624#else
625# error Unsupported platform.
626#endif
627}
628
632static inline rb_atomic_t
633rbimpl_atomic_fetch_sub(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
634{
635 (void)memory_order;
636#if 0
637
638#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
639 return __atomic_fetch_sub(ptr, val, memory_order);
640
641#elif defined(HAVE_GCC_SYNC_BUILTINS)
642 return __sync_fetch_and_sub(ptr, val);
643
644#elif defined(_WIN32)
645 /* rb_atomic_t is signed here! Safe to do `-val`. */
646 return InterlockedExchangeAdd(ptr, -val);
647
648#elif defined(__sun) && defined(HAVE_ATOMIC_H)
649 /* Ditto for `rbimpl_atomic_fetch_add`. */
650 const signed neg = -1;
651 RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX);
652 return atomic_add_int_nv(ptr, neg * val) + val;
653
654#elif defined(HAVE_STDATOMIC_H)
655 return atomic_fetch_sub_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
656
657#else
658# error Unsupported platform.
659#endif
660}
661
665static inline void
666rbimpl_atomic_sub(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
667{
668 (void)memory_order;
669#if 0
670
671#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
672 __atomic_sub_fetch(ptr, val, memory_order);
673
674#elif defined(HAVE_GCC_SYNC_BUILTINS)
675 __sync_sub_and_fetch(ptr, val);
676
677#elif defined(_WIN32)
678 InterlockedExchangeAdd(ptr, -val);
679
680#elif defined(__sun) && defined(HAVE_ATOMIC_H)
681 const signed neg = -1;
682 RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX);
683 atomic_add_int(ptr, neg * val);
684
685#elif defined(HAVE_STDATOMIC_H)
686 atomic_fetch_sub_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
687
688#else
689# error Unsupported platform.
690#endif
691}
692
696static inline void
697rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val, int memory_order)
698{
699 (void)memory_order;
700#if 0
701
702#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
703 __atomic_sub_fetch(ptr, val, memory_order);
704
705#elif defined(HAVE_GCC_SYNC_BUILTINS)
706 __sync_sub_and_fetch(ptr, val);
707
708#elif defined(_WIN64)
709 const ssize_t neg = -1;
710 InterlockedExchangeAdd64(RBIMPL_CAST((LONG64 *)ptr), neg * val);
711
712#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
713 const signed neg = -1;
714 RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX);
715 atomic_add_long(ptr, neg * val);
716
717#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
718 RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
719
720 volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
721 rbimpl_atomic_sub(tmp, val, memory_order);
722
723#elif defined(HAVE_STDATOMIC_H)
724 atomic_fetch_sub_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
725
726#else
727# error Unsupported platform.
728#endif
729}
730
734static inline void
735rbimpl_atomic_dec(volatile rb_atomic_t *ptr, int memory_order)
736{
737 (void)memory_order;
738#if 0
739
740#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
741 rbimpl_atomic_sub(ptr, 1, memory_order);
742
743#elif defined(_WIN32)
744 InterlockedDecrement(ptr);
745
746#elif defined(__sun) && defined(HAVE_ATOMIC_H)
747 atomic_dec_uint(ptr);
748
749#elif defined(HAVE_STDATOMIC_H)
750 rbimpl_atomic_sub(ptr, 1, memory_order);
751
752#else
753# error Unsupported platform.
754#endif
755}
756
760static inline void
761rbimpl_atomic_size_dec(volatile size_t *ptr, int memory_order)
762{
763 (void)memory_order;
764#if 0
765
766#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
767 rbimpl_atomic_size_sub(ptr, 1, memory_order);
768
769#elif defined(_WIN64)
770 InterlockedDecrement64(RBIMPL_CAST((LONG64 *)ptr));
771
772#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
773 atomic_dec_ulong(ptr);
774
775#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
776 RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
777
778 rbimpl_atomic_size_sub(ptr, 1, memory_order);
779
780#elif defined(HAVE_STDATOMIC_H)
781 rbimpl_atomic_size_sub(ptr, 1, memory_order);
782
783#else
784# error Unsupported platform.
785#endif
786}
787
791static inline void
792rbimpl_atomic_or(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
793{
794 (void)memory_order;
795#if 0
796
797#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
798 __atomic_or_fetch(ptr, val, memory_order);
799
800#elif defined(HAVE_GCC_SYNC_BUILTINS)
801 __sync_or_and_fetch(ptr, val);
802
803#elif defined(_MSC_VER)
804 _InterlockedOr(ptr, val);
805
806#elif defined(__sun) && defined(HAVE_ATOMIC_H)
807 atomic_or_uint(ptr, val);
808
809#elif !defined(_WIN32) && defined(HAVE_STDATOMIC_H)
810 atomic_fetch_or_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
811
812#else
813# error Unsupported platform.
814#endif
815}
816
820static inline rb_atomic_t
821rbimpl_atomic_exchange(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
822{
823 (void)memory_order;
824#if 0
825
826#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
827 return __atomic_exchange_n(ptr, val, memory_order);
828
829#elif defined(HAVE_GCC_SYNC_BUILTINS)
830 return __sync_lock_test_and_set(ptr, val);
831
832#elif defined(_WIN32)
833 return InterlockedExchange(ptr, val);
834
835#elif defined(__sun) && defined(HAVE_ATOMIC_H)
836 return atomic_swap_uint(ptr, val);
837
838#elif defined(HAVE_STDATOMIC_H)
839 return atomic_exchange_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
840
841#else
842# error Unsupported platform.
843#endif
844}
845
849static inline size_t
850rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val, int memory_order)
851{
852 (void)memory_order;
853#if 0
854
855#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
856 return __atomic_exchange_n(ptr, val, memory_order);
857
858#elif defined(HAVE_GCC_SYNC_BUILTINS)
859 return __sync_lock_test_and_set(ptr, val);
860
861#elif defined(_WIN64)
862 return InterlockedExchange64(RBIMPL_CAST((LONG64 *)ptr), val);
863
864#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
865 return atomic_swap_ulong(ptr, val);
866
867#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
868 RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
869
870 volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
871 const rb_atomic_t ret = rbimpl_atomic_exchange(tmp, val, memory_order);
872 return RBIMPL_CAST((size_t)ret);
873
874#elif defined(HAVE_STDATOMIC_H)
875 return atomic_exchange_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
876
877#else
878# error Unsupported platform.
879#endif
880}
881
885static inline void
886rbimpl_atomic_size_store(volatile size_t *ptr, size_t val, int memory_order)
887{
888 (void)memory_order;
889#if 0
890
891#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
892 __atomic_store_n(ptr, val, memory_order);
893
894#else
895 rbimpl_atomic_size_exchange(ptr, val, memory_order);
896
897#endif
898}
899
903static inline void *
904rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val, int memory_order)
905{
906 (void)memory_order;
907#if 0
908
909#elif defined(InterlockedExchangePointer)
910 /* const_cast */
911 PVOID *pptr = RBIMPL_CAST((PVOID *)ptr);
912 PVOID pval = RBIMPL_CAST((PVOID)val);
913 return InterlockedExchangePointer(pptr, pval);
914
915#elif defined(__sun) && defined(HAVE_ATOMIC_H)
916 return atomic_swap_ptr(ptr, RBIMPL_CAST((void *)val));
917
918#else
919 RBIMPL_STATIC_ASSERT(sizeof_voidp, sizeof *ptr == sizeof(size_t));
920
921 const size_t sval = RBIMPL_CAST((size_t)val);
922 volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
923 const size_t sret = rbimpl_atomic_size_exchange(sptr, sval, memory_order);
924 return RBIMPL_CAST((void *)sret);
925
926#endif
927}
928
932static inline void
933rbimpl_atomic_ptr_store(volatile void **ptr, void *val, int memory_order)
934{
935 RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
936
937 const size_t sval = RBIMPL_CAST((size_t)val);
938 volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
939 rbimpl_atomic_size_store(sptr, sval, memory_order);
940}
941
945static inline VALUE
946rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val, int memory_order)
947{
948 RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
949
950 const size_t sval = RBIMPL_CAST((size_t)val);
951 volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
952 const size_t sret = rbimpl_atomic_size_exchange(sptr, sval, memory_order);
953 return RBIMPL_CAST((VALUE)sret);
954}
955
959static inline void
960rbimpl_atomic_value_store(volatile VALUE *ptr, VALUE val, int memory_order)
961{
962 RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
963
964 const size_t sval = RBIMPL_CAST((size_t)val);
965 volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
966 rbimpl_atomic_size_store(sptr, sval, memory_order);
967}
968
972static inline rb_atomic_t
973rbimpl_atomic_load(volatile rb_atomic_t *ptr, int memory_order)
974{
975 (void)memory_order;
976#if 0
977
978#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
979 return __atomic_load_n(ptr, memory_order);
980#elif defined(_M_X64) && !defined(_M_ARM64EC)
981 /* A plain load is seq_cst on x64 because the stores here use xchg. */
982 return *ptr;
983#else
984 return rbimpl_atomic_fetch_add(ptr, 0, memory_order);
985#endif
986}
987
991static inline void
992rbimpl_atomic_store(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
993{
994 (void)memory_order;
995#if 0
996
997#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
998 __atomic_store_n(ptr, val, memory_order);
999
1000#else
1001 /* Maybe std::atomic<rb_atomic_t>::store can be faster? */
1002 rbimpl_atomic_exchange(ptr, val, memory_order);
1003
1004#endif
1005}
1006
1010static inline rb_atomic_t
1011rbimpl_atomic_cas(volatile rb_atomic_t *ptr, rb_atomic_t oldval, rb_atomic_t newval, int success_memorder, int failure_memorder)
1012{
1013 (void)success_memorder;
1014 (void)failure_memorder;
1015#if 0
1016
1017#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
1018 __atomic_compare_exchange_n(
1019 ptr, &oldval, newval, 0, success_memorder, failure_memorder);
1020 return oldval;
1021
1022#elif defined(HAVE_GCC_SYNC_BUILTINS)
1023 return __sync_val_compare_and_swap(ptr, oldval, newval);
1024
1025#elif defined(_MSC_VER)
1026 return InterlockedCompareExchange(ptr, newval, oldval);
1027
1028#elif defined(__sun) && defined(HAVE_ATOMIC_H)
1029 return atomic_cas_uint(ptr, oldval, newval);
1030
1031#elif defined(HAVE_STDATOMIC_H)
1032 atomic_compare_exchange_strong_explicit(
1033 (_Atomic volatile rb_atomic_t *)ptr, &oldval, newval, success_memorder, failure_memorder);
1034 return oldval;
1035
1036#else
1037# error Unsupported platform.
1038#endif
1039}
1040
1044static inline size_t
1045rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval, int success_memorder, int failure_memorder)
1046{
1047 (void)success_memorder;
1048 (void)failure_memorder;
1049#if 0
1050
1051#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
1052 __atomic_compare_exchange_n(
1053 ptr, &oldval, newval, 0, success_memorder, failure_memorder);
1054 return oldval;
1055
1056#elif defined(HAVE_GCC_SYNC_BUILTINS)
1057 return __sync_val_compare_and_swap(ptr, oldval, newval);
1058
1059#elif defined(_WIN64)
1060 return InterlockedCompareExchange64(RBIMPL_CAST((LONG64 *)ptr), newval, oldval);
1061
1062#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
1063 return atomic_cas_ulong(ptr, oldval, newval);
1064
1065#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
1066 RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
1067
1068 volatile rb_atomic_t *tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
1069 return rbimpl_atomic_cas(tmp, oldval, newval, success_memorder, failure_memorder);
1070
1071#elif defined(HAVE_STDATOMIC_H)
1072 atomic_compare_exchange_strong_explicit(
1073 (_Atomic volatile size_t *)ptr, &oldval, newval, success_memorder, failure_memorder);
1074 return oldval;
1075
1076#else
1077# error Unsupported platform.
1078#endif
1079}
1080
1084static inline size_t
1085rbimpl_atomic_size_load(volatile size_t *ptr, int memory_order)
1086{
1087 (void)memory_order;
1088#if 0
1089
1090#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
1091 return __atomic_load_n(ptr, memory_order);
1092#elif defined(_M_X64) && !defined(_M_ARM64EC)
1093 /* A plain load is seq_cst on x64 because the stores here use xchg. */
1094 return *ptr;
1095#else
1096 size_t val = *ptr;
1097 return rbimpl_atomic_size_cas(ptr, val, val, memory_order, memory_order);
1098#endif
1099}
1100
1104static inline void *
1105rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval, int success_memorder, int failure_memorder)
1106{
1107 (void)success_memorder;
1108 (void)failure_memorder;
1109#if 0
1110
1111#elif defined(InterlockedExchangePointer)
1112 /* ... Can we say that InterlockedCompareExchangePtr surly exists when
1113 * InterlockedExchangePointer is defined? Seems so but...?*/
1114 PVOID *pptr = RBIMPL_CAST((PVOID *)ptr);
1115 PVOID pold = RBIMPL_CAST((PVOID)oldval);
1116 PVOID pnew = RBIMPL_CAST((PVOID)newval);
1117 return InterlockedCompareExchangePointer(pptr, pnew, pold);
1118
1119#elif defined(__sun) && defined(HAVE_ATOMIC_H)
1120 void *pold = RBIMPL_CAST((void *)oldval);
1121 void *pnew = RBIMPL_CAST((void *)newval);
1122 return atomic_cas_ptr(ptr, pold, pnew);
1123
1124
1125#else
1126 RBIMPL_STATIC_ASSERT(sizeof_voidp, sizeof *ptr == sizeof(size_t));
1127
1128 const size_t snew = RBIMPL_CAST((size_t)newval);
1129 const size_t sold = RBIMPL_CAST((size_t)oldval);
1130 volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
1131 const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew, success_memorder, failure_memorder);
1132 return RBIMPL_CAST((void *)sret);
1133
1134#endif
1135}
1136
1140static inline void *
1141rbimpl_atomic_ptr_load(void **ptr, int memory_order)
1142{
1143 (void)memory_order;
1144#if 0
1145
1146#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
1147 return __atomic_load_n(ptr, memory_order);
1148#elif defined(_M_X64) && !defined(_M_ARM64EC)
1149 return *RBIMPL_CAST((void *volatile *)ptr);
1150#else
1151 void *val = *ptr;
1152 return rbimpl_atomic_ptr_cas(ptr, val, val, memory_order, memory_order);
1153#endif
1154}
1155
1159static inline VALUE
1160rbimpl_atomic_value_load(volatile VALUE *ptr, int memory_order)
1161{
1162 return RBIMPL_CAST((VALUE)rbimpl_atomic_ptr_load((void **)ptr, memory_order));
1163}
1164
1168static inline VALUE
1169rbimpl_atomic_value_cas(volatile VALUE *ptr, VALUE oldval, VALUE newval, int success_memorder, int failure_memorder)
1170{
1171 RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
1172
1173 const size_t snew = RBIMPL_CAST((size_t)newval);
1174 const size_t sold = RBIMPL_CAST((size_t)oldval);
1175 volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
1176 const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew, success_memorder, failure_memorder);
1177 return RBIMPL_CAST((VALUE)sret);
1178}
1180#endif /* RUBY_ATOMIC_H */
Defines RBIMPL_ATTR_ARTIFICIAL.
#define RBIMPL_ATTR_ARTIFICIAL()
Wraps (or simulates) __attribute__((artificial))
Definition artificial.h:43
#define RBIMPL_ASSERT_OR_ASSUME(...)
This is either RUBY_ASSERT or RBIMPL_ASSUME, depending on RUBY_DEBUG.
Definition assert.h:311
Atomic operations.
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
Defines RBIMPL_COMPILER_SINCE.
Defines RBIMPL_ATTR_NONNULL.
#define RBIMPL_ATTR_NONNULL(list)
Wraps (or simulates) __attribute__((nonnull))
Definition nonnull.h:30
Defines RBIMPL_STATIC_ASSERT.
#define RBIMPL_STATIC_ASSERT
Wraps (or simulates) static_assert
Defines RBIMPL_ATTR_NOALIAS.
#define RBIMPL_ATTR_NOALIAS()
Wraps (or simulates) __declspec((noalias))
Definition noalias.h:66
C99 shim for <stdbool.h>
Defines VALUE and ID.
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40