1e36221adSTrond Norbye/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
266eb94d0SMike Wiederhold/*
366eb94d0SMike Wiederhold *     Copyright 2010 Couchbase, Inc
466eb94d0SMike Wiederhold *
566eb94d0SMike Wiederhold *   Licensed under the Apache License, Version 2.0 (the "License");
666eb94d0SMike Wiederhold *   you may not use this file except in compliance with the License.
766eb94d0SMike Wiederhold *   You may obtain a copy of the License at
866eb94d0SMike Wiederhold *
966eb94d0SMike Wiederhold *       http://www.apache.org/licenses/LICENSE-2.0
1066eb94d0SMike Wiederhold *
1166eb94d0SMike Wiederhold *   Unless required by applicable law or agreed to in writing, software
1266eb94d0SMike Wiederhold *   distributed under the License is distributed on an "AS IS" BASIS,
1366eb94d0SMike Wiederhold *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1466eb94d0SMike Wiederhold *   See the License for the specific language governing permissions and
1566eb94d0SMike Wiederhold *   limitations under the License.
1666eb94d0SMike Wiederhold */
17ee087db5SRichard de Mellow#pragma once
188da7efd6SSean Lynch
19244c0146SMike Wiederhold#include "locks.h"
2044137813SDave Rigby#include "utility.h"
211c6ea0d1SDave Rigby#include <atomic>
221c6ea0d1SDave Rigby#include <iostream>
231c6ea0d1SDave Rigby#include <memory>
24244c0146SMike Wiederhold
257c6809d2STrond Norbyetemplate <typename T>
2666bb41adSDave Rigbyvoid atomic_setIfBigger(std::atomic<T> &obj, const T &newValue) {
277c6809d2STrond Norbye    T oldValue = obj.load();
287c6809d2STrond Norbye    while (newValue > oldValue) {
297c6809d2STrond Norbye        if (obj.compare_exchange_strong(oldValue, newValue)) {
30fa78b9bbSTrond Norbye            break;
317c6809d2STrond Norbye        }
327c6809d2STrond Norbye        oldValue = obj.load();
337c6809d2STrond Norbye    }
347c6809d2STrond Norbye}
35fa78b9bbSTrond Norbye
367c6809d2STrond Norbyetemplate <typename T>
3766bb41adSDave Rigbyvoid atomic_setIfLess(std::atomic<T> &obj, const T &newValue) {
387c6809d2STrond Norbye    T oldValue = obj.load();
397c6809d2STrond Norbye    while (newValue < oldValue) {
407c6809d2STrond Norbye        if (obj.compare_exchange_strong(oldValue, newValue)) {
417c6809d2STrond Norbye            break;
427c6809d2STrond Norbye        }
437c6809d2STrond Norbye        oldValue = obj.load();
447c6809d2STrond Norbye    }
457c6809d2STrond Norbye}
468da7efd6SSean Lynch
47c7dbf39fSMike Wiederholdtemplate <typename T>
4866bb41adSDave RigbyT atomic_swapIfNot(std::atomic<T> &obj, const T &badValue, const T &newValue) {
49c7dbf39fSMike Wiederhold    T oldValue;
50c7dbf39fSMike Wiederhold    while (true) {
51c7dbf39fSMike Wiederhold        oldValue = obj.load();
52c7dbf39fSMike Wiederhold        if (oldValue != badValue) {
53c7dbf39fSMike Wiederhold            if (obj.compare_exchange_strong(oldValue, newValue)) {
54c7dbf39fSMike Wiederhold                break;
55c7dbf39fSMike Wiederhold            }
56c7dbf39fSMike Wiederhold        } else {
57c7dbf39fSMike Wiederhold            break;
58c7dbf39fSMike Wiederhold        }
59c7dbf39fSMike Wiederhold    }
60c7dbf39fSMike Wiederhold    return oldValue;
61c7dbf39fSMike Wiederhold}
62c7dbf39fSMike Wiederhold
630821dc8fSDustin Sallings/**
640821dc8fSDustin Sallings * Atomic pointer.
650821dc8fSDustin Sallings *
660821dc8fSDustin Sallings * This does *not* make the item that's pointed to atomic.
670821dc8fSDustin Sallings */
688da7efd6SSean Lynchtemplate <typename T>
6966bb41adSDave Rigbyclass AtomicPtr : public std::atomic<T*> {
708da7efd6SSean Lynchpublic:
7166bb41adSDave Rigby    AtomicPtr(T *initial = NULL) : std::atomic<T*>(initial) {}
728da7efd6SSean Lynch
738da7efd6SSean Lynch    ~AtomicPtr() {}
748da7efd6SSean Lynch
757098d308SDaniel Owen    T *operator ->() const noexcept {
7666bb41adSDave Rigby        return std::atomic<T*>::load();
778da7efd6SSean Lynch    }
788da7efd6SSean Lynch
797098d308SDaniel Owen    T &operator *() const noexcept {
8066bb41adSDave Rigby        return *std::atomic<T*>::load();
818da7efd6SSean Lynch    }
82b92866bfSDustin Sallings
83b92866bfSDustin Sallings    operator bool() const {
8466bb41adSDave Rigby        return std::atomic<T*>::load() != NULL;
85b92866bfSDustin Sallings    }
86b92866bfSDustin Sallings
87b92866bfSDustin Sallings    bool operator !() const {
8866bb41adSDave Rigby        return std::atomic<T*>::load() == NULL;
89b92866bfSDustin Sallings    }
908da7efd6SSean Lynch};
918da7efd6SSean Lynch
920821dc8fSDustin Sallings/**
930821dc8fSDustin Sallings * A lighter-weight, smaller lock than a mutex.
940821dc8fSDustin Sallings *
950821dc8fSDustin Sallings * This is primarily useful when contention is rare.
960821dc8fSDustin Sallings */
97847a12acSSean Lynchclass SpinLock {
98847a12acSSean Lynchpublic:
99cba1306eSTrond Norbye    // It seems like inlining the code caused the dtrace probe to
100cba1306eSTrond Norbye    // be optimized away ;)
101d3b51162STrond Norbye    SpinLock();
102d3b51162STrond Norbye    ~SpinLock();
103d3b51162STrond Norbye
104b028e804SWillGardner    void lock(void);
105b028e804SWillGardner    void unlock(void);
106847a12acSSean Lynch
107d3b51162STrond Norbyeprivate:
1084da6ca96STrond Norbye    bool tryAcquire(void);
109847a12acSSean Lynch
110b028e804SWillGardner    std::atomic_flag lck;
111847a12acSSean Lynch    DISALLOW_COPY_AND_ASSIGN(SpinLock);
112847a12acSSean Lynch};
113847a12acSSean Lynch
114847a12acSSean Lynchtemplate <class T> class RCPtr;
1159801c79eSDaniel Owentemplate <class S, class Pointer, class Deleter>
1169801c79eSDaniel Owenclass SingleThreadedRCPtr;
117847a12acSSean Lynch
118be73017eSDustin Sallings/**
119dd6fb104SChiyoung Seo * A reference counted value (used by RCPtr and SingleThreadedRCPtr).
120be73017eSDustin Sallings */
121847a12acSSean Lynchclass RCValue {
122847a12acSSean Lynchpublic:
123847a12acSSean Lynch    RCValue() : _rc_refcount(0) {}
124e83145c8SDustin Sallings    RCValue(const RCValue &) : _rc_refcount(0) {}
125847a12acSSean Lynch    ~RCValue() {}
1267098d308SDaniel Owen
127847a12acSSean Lynchprivate:
1285f0c51c4STrond Norbye    template <class MyTT> friend class RCPtr;
1299801c79eSDaniel Owen    template <class MySS, class Pointer, class Deleter>
1309801c79eSDaniel Owen    friend class SingleThreadedRCPtr;
131105555aeSDustin Sallings    int _rc_incref() const {
132847a12acSSean Lynch        return ++_rc_refcount;
133847a12acSSean Lynch    }
134847a12acSSean Lynch
135105555aeSDustin Sallings    int _rc_decref() const {
136847a12acSSean Lynch        return --_rc_refcount;
137847a12acSSean Lynch    }
138847a12acSSean Lynch
1397098d308SDaniel Owen    // A get method to ensure that SingleThreadedRCPtr does not need to directly
1407098d308SDaniel Owen    // refer to a RCValue.
1417098d308SDaniel Owen    const RCValue& getRCValue() const {
1427098d308SDaniel Owen        return *this;
1437098d308SDaniel Owen    }
1447098d308SDaniel Owen
14566bb41adSDave Rigby    mutable std::atomic<int> _rc_refcount;
146847a12acSSean Lynch};
147847a12acSSean Lynch
1480821dc8fSDustin Sallings/**
1490821dc8fSDustin Sallings * Concurrent reference counted pointer.
1500821dc8fSDustin Sallings */
151847a12acSSean Lynchtemplate <class C>
152847a12acSSean Lynchclass RCPtr {
153847a12acSSean Lynchpublic:
154847a12acSSean Lynch    RCPtr(C *init = NULL) : value(init) {
155847a12acSSean Lynch        if (init != NULL) {
1567098d308SDaniel Owen            value->getRCValue()._rc_incref();
157847a12acSSean Lynch        }
158847a12acSSean Lynch    }
159847a12acSSean Lynch
160105555aeSDustin Sallings    RCPtr(const RCPtr<C> &other) : value(other.gimme()) {}
161847a12acSSean Lynch
1620d5a64feSDave Rigby    RCPtr(RCPtr<C>&& other) {
1630d5a64feSDave Rigby        value.store(other.value.load());
1640d5a64feSDave Rigby        other.value.store(nullptr);
1650d5a64feSDave Rigby    }
1660d5a64feSDave Rigby
167847a12acSSean Lynch    ~RCPtr() {
1687098d308SDaniel Owen        if (value && value->getRCValue()._rc_decref() == 0) {
169bc643383STrond Norbye            delete get();
170847a12acSSean Lynch        }
171847a12acSSean Lynch    }
172847a12acSSean Lynch
173847a12acSSean Lynch    void reset(C *newValue = NULL) {
174847a12acSSean Lynch        if (newValue != NULL) {
1757098d308SDaniel Owen            newValue->getRCValue()._rc_incref();
176847a12acSSean Lynch        }
177847a12acSSean Lynch        swap(newValue);
178847a12acSSean Lynch    }
179847a12acSSean Lynch
180105555aeSDustin Sallings    void reset(const RCPtr<C> &other) {
181847a12acSSean Lynch        swap(other.gimme());
182847a12acSSean Lynch    }
183847a12acSSean Lynch
184847a12acSSean Lynch    // safe for the lifetime of this instance
185b572011aSChiyoung Seo    C *get() const {
186847a12acSSean Lynch        return value;
187847a12acSSean Lynch    }
188847a12acSSean Lynch
189b572011aSChiyoung Seo    RCPtr<C> & operator =(const RCPtr<C> &other) {
190847a12acSSean Lynch        reset(other);
191b572011aSChiyoung Seo        return *this;
192847a12acSSean Lynch    }
193847a12acSSean Lynch
194847a12acSSean Lynch    C &operator *() const {
195847a12acSSean Lynch        return *value;
196847a12acSSean Lynch    }
197847a12acSSean Lynch
198847a12acSSean Lynch    C *operator ->() const {
199847a12acSSean Lynch        return value;
200847a12acSSean Lynch    }
201b92866bfSDustin Sallings
202b92866bfSDustin Sallings    bool operator! () const {
203b92866bfSDustin Sallings        return !value;
204b92866bfSDustin Sallings    }
205b92866bfSDustin Sallings
206b92866bfSDustin Sallings    operator bool () const {
207b92866bfSDustin Sallings        return (bool)value;
208b92866bfSDustin Sallings    }
209b92866bfSDustin Sallings
210847a12acSSean Lynchprivate:
211105555aeSDustin Sallings    C *gimme() const {
212b028e804SWillGardner        std::lock_guard<SpinLock> lh(lock);
213b92866bfSDustin Sallings        if (value) {
2147098d308SDaniel Owen            value->getRCValue()._rc_incref();
215847a12acSSean Lynch        }
216847a12acSSean Lynch        return value;
217847a12acSSean Lynch    }
218847a12acSSean Lynch
219847a12acSSean Lynch    void swap(C *newValue) {
220b028e804SWillGardner        C* tmp;
221b028e804SWillGardner        {
222b028e804SWillGardner            std::lock_guard<SpinLock> lh(lock);
223b028e804SWillGardner            tmp = value.exchange(newValue);
224b028e804SWillGardner        }
2257098d308SDaniel Owen        if (tmp != NULL && tmp->getRCValue()._rc_decref() == 0) {
226847a12acSSean Lynch            delete tmp;
227847a12acSSean Lynch        }
228847a12acSSean Lynch    }
229847a12acSSean Lynch
230847a12acSSean Lynch    AtomicPtr<C> value;
231105555aeSDustin Sallings    mutable SpinLock lock; // exists solely for the purpose of implementing reset() safely
232847a12acSSean Lynch};
233847a12acSSean Lynch
2344a1704d4SDave Rigby/**
2354a1704d4SDave Rigby * Dynamic cast for RCPtr. Modelled on method of the same name for
2364a1704d4SDave Rigby * std::shared_ptr.
2374a1704d4SDave Rigby */
2384a1704d4SDave Rigbytemplate <class T, class U>
2394a1704d4SDave RigbyRCPtr<T> dynamic_pointer_cast(const RCPtr<U>& r) {
2404a1704d4SDave Rigby    T* p = dynamic_cast<T*>(r.get());
2414a1704d4SDave Rigby    return p ? RCPtr<T>(p) : RCPtr<T>();
2424a1704d4SDave Rigby}
2434a1704d4SDave Rigby
244dd6fb104SChiyoung Seo/**
245dd6fb104SChiyoung Seo * Single-threaded reference counted pointer.
246dd6fb104SChiyoung Seo * "Single-threaded" means that the reference counted pointer should be accessed
247dd6fb104SChiyoung Seo * by only one thread at any time or accesses to the reference counted pointer
248dd6fb104SChiyoung Seo * by multiple threads should be synchronized by the external lock.
2499801c79eSDaniel Owen *
2509801c79eSDaniel Owen * Takes the following template parameters:
2519801c79eSDaniel Owen * @tparam class T the class that the SingleThreadedRCPtr is pointing to.
2529801c79eSDaniel Owen * @tparam class Pointer the pointer type that the SingleThreadedRCPtr
2539801c79eSDaniel Owen * maintains a reference counter for.  It is defaulted to a T*, however can also
2549801c79eSDaniel Owen * be a TaggedPtr<T>.
2559801c79eSDaniel Owen * @tparam class Deleter the deleter function for deleting the object pointed to
2569801c79eSDaniel Owen * by SingleThreadedRCPtr.  It defaults to the std::default_delete function
2579801c79eSDaniel Owen * templated on class T.  However when the pointer type is a TaggedPtr<T> a
2589801c79eSDaniel Owen * specialised delete function must be provided.
259dd6fb104SChiyoung Seo */
2609801c79eSDaniel Owentemplate <class T, class Pointer = T*, class Deleter = std::default_delete<T>>
261dd6fb104SChiyoung Seoclass SingleThreadedRCPtr {
262dd6fb104SChiyoung Seopublic:
2639801c79eSDaniel Owen    using rcptr_type = SingleThreadedRCPtr<T, Pointer, Deleter>;
2649801c79eSDaniel Owen
2659801c79eSDaniel Owen    SingleThreadedRCPtr(Pointer init = nullptr) : value(init) {
2669801c79eSDaniel Owen        if (init != nullptr) {
2677098d308SDaniel Owen            value->getRCValue()._rc_incref();
268dd6fb104SChiyoung Seo        }
269dd6fb104SChiyoung Seo    }
270dd6fb104SChiyoung Seo
271568357e7SDave Rigby    // Copy construction - increases ref-count on object by 1.
2729801c79eSDaniel Owen    SingleThreadedRCPtr(const rcptr_type& other) : value(other.gimme()) {
2739801c79eSDaniel Owen    }
274dd6fb104SChiyoung Seo
275568357e7SDave Rigby    // Move construction - reference count is unchanged.
2769801c79eSDaniel Owen    SingleThreadedRCPtr(rcptr_type&& other) : value(other.value) {
277568357e7SDave Rigby        other.value = nullptr;
278568357e7SDave Rigby    }
279568357e7SDave Rigby
2809801c79eSDaniel Owen    template <typename Y, typename P>
2819801c79eSDaniel Owen    SingleThreadedRCPtr(const SingleThreadedRCPtr<Y, P, Deleter>& other)
282de19498aSolivermd        : value(other.gimme()) {
283de19498aSolivermd    }
2850bc70e5bSDave Rigby    SingleThreadedRCPtr(std::unique_ptr<T>&& other)
2860bc70e5bSDave Rigby        : SingleThreadedRCPtr(other.release()) {
2870bc70e5bSDave Rigby    }
2880bc70e5bSDave Rigby
289dd6fb104SChiyoung Seo    ~SingleThreadedRCPtr() {
2907169f224SDaniel Owen        if (value != nullptr && value->getRCValue()._rc_decref() == 0) {
2919801c79eSDaniel Owen            Deleter()(value);
292dd6fb104SChiyoung Seo        }
293dd6fb104SChiyoung Seo    }
294dd6fb104SChiyoung Seo
2959801c79eSDaniel Owen    void reset(Pointer newValue = nullptr) {
2969801c79eSDaniel Owen        if (newValue != nullptr) {
2977098d308SDaniel Owen            newValue->getRCValue()._rc_incref();
298dd6fb104SChiyoung Seo        }
299dd6fb104SChiyoung Seo        swap(newValue);
300dd6fb104SChiyoung Seo    }
301dd6fb104SChiyoung Seo
3029801c79eSDaniel Owen    void reset(const rcptr_type& other) {
303dd6fb104SChiyoung Seo        swap(other.gimme());
304dd6fb104SChiyoung Seo    }
305dd6fb104SChiyoung Seo
3068f1b9af4SDave Rigby    // Swap - reference count is unchanged on each pointed-to object.
3079801c79eSDaniel Owen    void swap(rcptr_type& other) {
3088f1b9af4SDave Rigby        std::swap(this->value, other.value);
3098f1b9af4SDave Rigby    }
3108f1b9af4SDave Rigby
31127ea3feaSolivermd    int refCount() const {
3127098d308SDaniel Owen        return value->getRCValue()._rc_refcount.load();
31327ea3feaSolivermd    }
315dd6fb104SChiyoung Seo    // safe for the lifetime of this instance
3169801c79eSDaniel Owen    Pointer get() const {
317dd6fb104SChiyoung Seo        return value;
318dd6fb104SChiyoung Seo    }
319dd6fb104SChiyoung Seo
32090c623cfSDave Rigby    /**
32190c623cfSDave Rigby     * Returns a reference to the owned pointer.
32290c623cfSDave Rigby     *
32390c623cfSDave Rigby     * WARNING WARNING WARNING
32490c623cfSDave Rigby     *
32590c623cfSDave Rigby     * This function is inheritly unsafe; as it exposes the internal
32690c623cfSDave Rigby     * managed pointer. Incorrect use of this could lead to memory
32790c623cfSDave Rigby     * leaks, crashes etc.  Unless you really know what you're doing
32890c623cfSDave Rigby     * don't use this!
32990c623cfSDave Rigby     */
33090c623cfSDave Rigby    Pointer& unsafeGetPointer() {
33190c623cfSDave Rigby        return value;
33290c623cfSDave Rigby    }
33390c623cfSDave Rigby
3349801c79eSDaniel Owen    rcptr_type& operator=(const rcptr_type& other) {
335dd6fb104SChiyoung Seo        reset(other);
336dd6fb104SChiyoung Seo        return *this;
337dd6fb104SChiyoung Seo    }
338dd6fb104SChiyoung Seo
339568357e7SDave Rigby    // Move-assignment - reference count is unchanged of incoming item.
3409801c79eSDaniel Owen    rcptr_type& operator=(rcptr_type&& other) {
341568357e7SDave Rigby        swap(other.value);
342568357e7SDave Rigby        other.value = nullptr;
343568357e7SDave Rigby        return *this;
344568357e7SDave Rigby    }
345568357e7SDave Rigby
346dd6fb104SChiyoung Seo    T &operator *() const {
347dd6fb104SChiyoung Seo        return *value;
348dd6fb104SChiyoung Seo    }
349dd6fb104SChiyoung Seo
3509801c79eSDaniel Owen    Pointer operator ->() const {
351dd6fb104SChiyoung Seo        return value;
352dd6fb104SChiyoung Seo    }
353dd6fb104SChiyoung Seo
354dd6fb104SChiyoung Seo    bool operator! () const {
3557169f224SDaniel Owen        return value == nullptr;
356dd6fb104SChiyoung Seo    }
357dd6fb104SChiyoung Seo
358dd6fb104SChiyoung Seo    operator bool () const {
3597169f224SDaniel Owen        return value != nullptr;
360dd6fb104SChiyoung Seo    }
361dd6fb104SChiyoung Seo
362dd6fb104SChiyoung Seoprivate:
3639801c79eSDaniel Owen    template <typename Y, typename P, typename D>
364de19498aSolivermd    friend class SingleThreadedRCPtr;
3669801c79eSDaniel Owen    Pointer gimme() const {
3677169f224SDaniel Owen        if (value != nullptr) {
3687098d308SDaniel Owen            value->getRCValue()._rc_incref();
369dd6fb104SChiyoung Seo        }
370dd6fb104SChiyoung Seo        return value;
371dd6fb104SChiyoung Seo    }
372dd6fb104SChiyoung Seo
3739801c79eSDaniel Owen    void swap(Pointer newValue) {
3749801c79eSDaniel Owen        Pointer old = value;
375dd6fb104SChiyoung Seo        value = newValue;
3769801c79eSDaniel Owen        if (old != nullptr && old->getRCValue()._rc_decref() == 0) {
3779801c79eSDaniel Owen            Deleter()(old);
378dd6fb104SChiyoung Seo        }
379dd6fb104SChiyoung Seo    }
380dd6fb104SChiyoung Seo
3819801c79eSDaniel Owen    Pointer value;
382dd6fb104SChiyoung Seo};
383dd6fb104SChiyoung Seo
3849801c79eSDaniel Owentemplate <typename T, typename Pointer, typename Deleter, class... Args>
3859801c79eSDaniel OwenSingleThreadedRCPtr<T, Pointer, Deleter> make_STRCPtr(Args&&... args) {
3869801c79eSDaniel Owen    return SingleThreadedRCPtr<T, Pointer, Deleter>(
3879801c79eSDaniel Owen            new T(std::forward<Args>(args)...));
389dd3e745fSDave Rigby
3908f1b9af4SDave Rigby// Makes SingleThreadedRCPtr support Swappable
3919801c79eSDaniel Owentemplate <typename T, typename Pointer, typename Deleter>
3929801c79eSDaniel Owenvoid swap(SingleThreadedRCPtr<T, Pointer, Deleter>& a,
3939801c79eSDaniel Owen          SingleThreadedRCPtr<T, Pointer, Deleter>& b) {
3948f1b9af4SDave Rigby    a.swap(b);
3958f1b9af4SDave Rigby}
3968f1b9af4SDave Rigby
397dd3e745fSDave Rigby/**
398dd3e745fSDave Rigby * Debugging wrapper around std::atomic which print all accesses to the atomic
399dd3e745fSDave Rigby * value to stderr.
400dd3e745fSDave Rigby */
401dd3e745fSDave Rigbytemplate <typename T>
402dd3e745fSDave Rigbyclass LoggedAtomic {
403dd3e745fSDave Rigbypublic:
404dd3e745fSDave Rigby    LoggedAtomic(T initial)
405dd3e745fSDave Rigby        : value(initial) {
4062252bb7aSDave Rigby        std::lock_guard<std::mutex> lock(stderr_mutex);
4072252bb7aSDave Rigby        std::cerr << "LoggedAtomic[" << this << "]::LoggedAtomic: "
4082252bb7aSDave Rigby                  << value.load() << std::endl;
409dd3e745fSDave Rigby
410dd3e745fSDave Rigby    }
411dd3e745fSDave Rigby
4122252bb7aSDave Rigby    T operator=(T desired) {
4132252bb7aSDave Rigby        std::lock_guard<std::mutex> lock(stderr_mutex);
4142252bb7aSDave Rigby        value.store(desired);
4152252bb7aSDave Rigby        std::cerr << "LoggedAtomic[" << this << "]::operator=: "
4162252bb7aSDave Rigby                  << value.load() << std::endl;
4172252bb7aSDave Rigby        return value.load();
4182252bb7aSDave Rigby    }
4192252bb7aSDave Rigby
420dd3e745fSDave Rigby    T load() const {
4212252bb7aSDave Rigby        std::lock_guard<std::mutex> lock(stderr_mutex);
422dd3e745fSDave Rigby        auto result = value.load();
4232252bb7aSDave Rigby        std::cerr << "LoggedAtomic[" << this << "]::load: " << result
4242252bb7aSDave Rigby                  << std::endl;
425dd3e745fSDave Rigby        return result;
426dd3e745fSDave Rigby    }
427dd3e745fSDave Rigby
428dd3e745fSDave Rigby    void store(T desired) {
4292252bb7aSDave Rigby        std::lock_guard<std::mutex> lock(stderr_mutex);
430dd3e745fSDave Rigby        value.store(desired);
4312252bb7aSDave Rigby        std::cerr << "LoggedAtomic[" << this << "]::store: " << value.load()
4322252bb7aSDave Rigby                  << std::endl;
4332252bb7aSDave Rigby    }
4342252bb7aSDave Rigby
4352252bb7aSDave Rigby    operator T() const {
4362252bb7aSDave Rigby        std::lock_guard<std::mutex> lock(stderr_mutex);
4372252bb7aSDave Rigby        auto result = value.load();
4382252bb7aSDave Rigby        std::cerr << "LoggedAtomic[" << this << "]::operator T: " << result
4392252bb7aSDave Rigby                  << std::endl;
4402252bb7aSDave Rigby        return result;
4412252bb7aSDave Rigby    }
4422252bb7aSDave Rigby
44346dc1225SDave Rigby    T exchange(T desired, std::memory_order order = std::memory_order_seq_cst) {
44446dc1225SDave Rigby        std::lock_guard<std::mutex> lock(stderr_mutex);
44546dc1225SDave Rigby        std::cerr << "LoggedAtomic[" << this << "]::exchange("
44646dc1225SDave Rigby                  << "desired:" << desired << ") = ";
44746dc1225SDave Rigby        auto result = value.exchange(desired, order);
44846dc1225SDave Rigby        std::cerr << result << std::endl;
44946dc1225SDave Rigby        return result;
45046dc1225SDave Rigby    }
45146dc1225SDave Rigby
452f3dcd5beSManu Dhundi    bool compare_exchange_strong(T& expected, T desired,
453f3dcd5beSManu Dhundi                                 std::memory_order order =
454f3dcd5beSManu Dhundi                                      std::memory_order_seq_cst ) {
455f3dcd5beSManu Dhundi        std::lock_guard<std::mutex> lock(stderr_mutex);
456f3dcd5beSManu Dhundi        std::cerr << "LoggedAtomic[" << this << "]::compare_exchange_strong("
457f3dcd5beSManu Dhundi                  << "expected:" << expected << ", desired:) = " << desired;
458f3dcd5beSManu Dhundi        auto result = value.compare_exchange_strong(expected, desired, order);
459f3dcd5beSManu Dhundi        std::cerr << result << std::endl;
460f3dcd5beSManu Dhundi        return result;
461f3dcd5beSManu Dhundi    }
462f3dcd5beSManu Dhundi
4632252bb7aSDave Rigby    T fetch_add(T arg,
4642252bb7aSDave Rigby                std::memory_order order = std::memory_order_seq_cst ) {
4652252bb7aSDave Rigby        std::lock_guard<std::mutex> lock(stderr_mutex);
4662252bb7aSDave Rigby        T result = value.fetch_add(arg, order);
4672252bb7aSDave Rigby        std::cerr << "LoggedAtomic[" << this << "]::fetch_add(" << arg
4682252bb7aSDave Rigby                  << "): " << result << std::endl;
4692252bb7aSDave Rigby        return value.load();
4702252bb7aSDave Rigby    }
4712252bb7aSDave Rigby
4722252bb7aSDave Rigby    T fetch_sub(T arg,
4732252bb7aSDave Rigby                std::memory_order order = std::memory_order_seq_cst ) {
4742252bb7aSDave Rigby        std::lock_guard<std::mutex> lock(stderr_mutex);