From 4137271188589ab7cd7a4d8e17c2669e6955b97e Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 22 Feb 2023 13:11:00 -0700 Subject: [PATCH] int62_t: use explicit memory order for all loads and stores Also, improve implementation of CAS loops for operator =. Ideally, we ought to test this on ARM, both before and after. --- libs/pbd/pbd/int62.h | 132 ++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 76 deletions(-) diff --git a/libs/pbd/pbd/int62.h b/libs/pbd/pbd/int62.h index 0cffee7aba..74f50f18b8 100644 --- a/libs/pbd/pbd/int62.h +++ b/libs/pbd/pbd/int62.h @@ -59,7 +59,7 @@ class alignas(16) int62_t { int62_t () : v (0) {} int62_t (bool bc, int64_t vc) : v (build (bc, vc)) {} - int62_t (int62_t const & other) { v.store (other.v.load()); } + int62_t (int62_t const & other) { v.store (other.v.load(std::memory_order_acquire), std::memory_order_release); } static const int64_t max = 4611686018427387903; /* 2^62 - 1 */ static const int64_t min = -2305843009213693952; @@ -67,8 +67,8 @@ class alignas(16) int62_t { bool flagged() const { return flagged (v); } int64_t val() const { return int62(v); } - int62_t& operator= (int64_t n) { v.store (build (flagged (v.load()), n)); return *this; } - int62_t& operator= (int62_t const & other) { v.store (other.v.load()); return *this; } + int62_t& operator= (int64_t n) { v.store (build (flagged (v.load(std::memory_order_acquire)), n), std::memory_order_release); return *this; } + int62_t& operator= (int62_t const & other) { v.store (other.v.load(std::memory_order_acquire), std::memory_order_release); return *this; } /* there's a pattern to many of these operators: @@ -85,19 +85,19 @@ class alignas(16) int62_t { rather than two separate loads for each "part". */ - int62_t operator- () const { const int64_t vv = v.load(); return int62_t (flagged (vv), -int62(vv)); } + int62_t operator- () const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), -int62(vv)); } - int62_t operator+ (int64_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) + n); } - int62_t operator- (int64_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) - n); } - int62_t operator* (int64_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) * n); } - int62_t operator/ (int64_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) / n); } - int62_t operator% (int64_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) % n); } + int62_t operator+ (int64_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) + n); } + int62_t operator- (int64_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) - n); } + int62_t operator* (int64_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) * n); } + int62_t operator/ (int64_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) / n); } + int62_t operator% (int64_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) % n); } - int62_t operator+ (int62_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) + n.val()); } - int62_t operator- (int62_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) - n.val()); } - int62_t operator* (int62_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) * n.val()); } - int62_t operator/ (int62_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) / n.val()); } - int62_t operator% (int62_t n) const { const int64_t vv = v.load(); return int62_t (flagged (vv), int62 (vv) % n.val()); } + int62_t operator+ (int62_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) + n.val()); } + int62_t operator- (int62_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) - n.val()); } + int62_t operator* (int62_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) * n.val()); } + int62_t operator/ (int62_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) / n.val()); } + int62_t operator% (int62_t n) const { const int64_t vv = v.load(std::memory_order_acquire); return int62_t (flagged (vv), int62 (vv) % n.val()); } /* comparison operators .. will throw if the two objects have different * flag settings (which is assumed to indicate that they differ in some @@ -118,8 +118,8 @@ class alignas(16) int62_t { * the semantics are well defined and the computation cost is trivial */ - bool operator!= (int62_t const & other) const { const int64_t vv = v.load(); if (flagged (vv) != other.flagged()) return true; return int62 (vv) != other.val(); } - bool operator== (int62_t const & other) const { const int64_t vv = v.load(); if (flagged (vv) != other.flagged()) return false; return int62 (vv) == other.val(); } + bool operator!= (int62_t const & other) const { const int64_t vv = v.load(std::memory_order_acquire); if (flagged (vv) != other.flagged()) return true; return int62 (vv) != other.val(); } + bool operator== (int62_t const & other) const { const int64_t vv = v.load(std::memory_order_acquire); if (flagged (vv) != other.flagged()) return false; return int62 (vv) == other.val(); } explicit operator int64_t() const { return int62(v); } @@ -133,103 +133,83 @@ class alignas(16) int62_t { int62_t abs() const { const int64_t tmp = v; return int62_t (flagged(tmp), ::llabs(int62(tmp))); } int62_t& operator+= (int64_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) + n); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) + n); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) + n); } return *this; } int62_t& operator-= (int64_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) - n); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) - n); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) - n); } return *this; } int62_t& operator*= (int64_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) * n); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) * n); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) * n); } return *this; } int62_t& operator/= (int64_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) / n); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) / n); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) / n); } return *this; } int62_t& operator%= (int64_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) % n); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) % n); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) % n); } return *this; } int62_t& operator+= (int62_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) + n.val()); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) + n.val()); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) + n.val()); } return *this; } int62_t& operator-= (int62_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) - n.val()); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) - n.val()); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) - n.val()); } return *this; } int62_t& operator*= (int62_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) * n.val()); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) * n.val()); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) * n.val()); } return *this; } int62_t& operator/= (int62_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) / n.val()); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) / n.val()); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) / n.val()); } return *this; } int62_t& operator%= (int62_t n) { - while (1) { - int64_t oldval = v.load (std::memory_order_relaxed); - int64_t newval = build (flagged (oldval), int62 (oldval) % n.val()); - if (v.compare_exchange_weak (oldval, newval)) { - break; - } + int64_t oldval = v.load (std::memory_order_acquire); + int64_t newval = build (flagged (oldval), int62 (oldval) % n.val()); + while (!v.compare_exchange_weak (oldval, newval, std::memory_order_release, std::memory_order_relaxed)) { + newval = build (flagged (oldval), int62 (oldval) % n.val()); } return *this; }