fish-shell/src/global_safety.h
Aaron Gyes 14d2a6d8ff IWYU-guided #include rejiggering.
Let's hope this doesn't causes build failures for e.g. musl: I just
know it's good on macOS and our Linux CI.

It's been a long time.

One fix this brings, is I discovered we #include assert.h or cassert
in a lot of places. If those ever happen to be in a file that doesn't
include common.h, or we are before common.h gets included, we're
unawaringly working with the system 'assert' macro again, which
may get disabled for debug builds or at least has different
behavior on crash. We undef 'assert' and redefine it in common.h.

Those were all eliminated, except in one catch-22 spot for
maybe.h: it can't include common.h. A fix might be to
make a fish_assert.h that *usually* common.h exports.
2022-08-20 23:55:18 -07:00

66 lines
2 KiB
C++

// Support for enforcing correct access to globals.
#ifndef FISH_GLOBAL_SAFETY_H
#define FISH_GLOBAL_SAFETY_H
#include "config.h" // IWYU pragma: keep
#include <atomic>
#include "common.h"
/// A latch variable may only be set once.
/// The value is immortal.
template <typename T>
class latch_t : noncopyable_t, nonmovable_t {
T *value_{};
public:
operator T *() { return value_; }
operator const T *() const { return value_; }
T *operator->() { return value_; }
const T *operator->() const { return value_; }
bool is_set() const { return value_ != nullptr; }
void operator=(std::unique_ptr<T> value) {
assert(value_ == nullptr && "Latch variable initialized multiple times");
assert(value != nullptr && "Latch variable initialized with null");
// Note: deliberate leak.
value_ = value.release();
}
void operator=(T &&value) { *this = make_unique<T>(std::move(value)); }
};
/// An atomic type that always use relaxed reads.
template <typename T>
class relaxed_atomic_t {
std::atomic<T> value_{};
public:
relaxed_atomic_t() = default;
relaxed_atomic_t(T value) : value_(value) {}
operator T() const volatile { return value_.load(std::memory_order_relaxed); }
void operator=(T v) { return value_.store(v, std::memory_order_relaxed); }
void operator=(T v) volatile { return value_.store(v, std::memory_order_relaxed); }
// Perform a CAS operation, returning whether it succeeded.
bool compare_exchange(T expected, T desired) {
return value_.compare_exchange_strong(expected, desired, std::memory_order_relaxed);
}
// postincrement
T operator++(int) { return value_.fetch_add(1, std::memory_order_relaxed); }
T operator--(int) { return value_.fetch_sub(1, std::memory_order_relaxed); }
// preincrement
T operator++() { return value_.fetch_add(1, std::memory_order_relaxed) + 1; }
T operator--() { return value_.fetch_sub(1, std::memory_order_relaxed) - 1; }
};
using relaxed_atomic_bool_t = relaxed_atomic_t<bool>;
#endif