diff --git a/internals/testing/smoke_test.cpp b/internals/testing/smoke_test.cpp index b70e92a..fc385b4 100644 --- a/internals/testing/smoke_test.cpp +++ b/internals/testing/smoke_test.cpp @@ -9,12 +9,14 @@ using namespace trade_v1; auto smoke_test = test([]() { atom xA = 1; - atom yA = 2.f; + atom yA = 1.f; atom zA = 3; atom> p(std::make_shared(32)); verify(atomically(assume_readonly, []() { return true; })); + yA.unsafe_store(2); + { verify(1 == xA.unsafe_load()); verify(2 == yA.unsafe_load()); diff --git a/provides/include/trade_v1/private/private-methods.hpp b/provides/include/trade_v1/private/private-methods.hpp index 2c4d333..197dd7a 100644 --- a/provides/include/trade_v1/private/private-methods.hpp +++ b/provides/include/trade_v1/private/private-methods.hpp @@ -109,6 +109,27 @@ Value &trade_v1::Private::store(atom_t &atom, Forwardable &&value) { return access->m_current; } +template +const Value &trade_v1::Private::unsafe_store(atom_t &atom, + const Value &value) { + auto &lock = s_locks[lock_ix_of(&atom)]; + molecular::backoff backoff; + while (true) { + auto u = lock.m_clock.load(); + if (0 <= static_cast(u)) { + if (lock.m_clock.compare_exchange_weak( + u, ~u, std::memory_order_acquire)) { + atom.m_value.store(value, std::memory_order_relaxed); + if (auto first = lock.m_first) + signal(first); + lock.m_clock.store(s_clock++, std::memory_order_release); + return value; + } + } + backoff(); + } +} + template Value &trade_v1::Private::ref(atom_t &atom) { auto transaction = s_transaction; auto access = insert(transaction, &atom); diff --git a/provides/include/trade_v1/private/private.hpp b/provides/include/trade_v1/private/private.hpp index d7cc02f..59e1f81 100644 --- a/provides/include/trade_v1/private/private.hpp +++ b/provides/include/trade_v1/private/private.hpp @@ -106,6 +106,9 @@ class Private { template static Value &store(atom_t &atom, Forwardable &&value); + template + static const Value &unsafe_store(atom_t &atom, const Value &value); + template static Value &ref(atom_t &atom); template diff --git a/provides/include/trade_v1/synopsis.hpp b/provides/include/trade_v1/synopsis.hpp index 245a9e8..c268256 100644 --- a/provides/include/trade_v1/synopsis.hpp +++ b/provides/include/trade_v1/synopsis.hpp @@ -41,6 +41,11 @@ template struct atom : Private::atom_t { /// value` is equivalent to `atom.store(value)`. template Value &store(Forwardable &&value); + /// Store the given value to the given atom as a single transaction. Note + /// that this cannot be used safely inside an `atomically` block, because it + /// can prevent the `atomically` block from ever completing. + const Value &unsafe_store(const Value &value); + /// Returns a mutable reference to the current value of the atom within a /// transaction. `atom.ref()` is roughly equivalent to /// `atom.store(atom.load())`, but accesses the transaction log only once. diff --git a/provides/include/trade_v1/trade.hpp b/provides/include/trade_v1/trade.hpp index b998ece..26d1ae6 100644 --- a/provides/include/trade_v1/trade.hpp +++ b/provides/include/trade_v1/trade.hpp @@ -39,6 +39,11 @@ Value &trade_v1::atom::store(Forwardable &&value) { return Private::store(*this, std::forward(value)); } +template +const Value &trade_v1::atom::unsafe_store(const Value &value) { + return Private::unsafe_store(*this, value); +} + template std::invoke_result_t trade_v1::atomically(Config config, Action &&action) {