Summary: This change applies all Arcanist recommended lint changes, which amounts to changing copyright headers and some cases of whitespace changes. Reviewed By: yungsters Differential Revision: D40060899 fbshipit-source-id: b62f9472e6ef58a3fc3d22eed661578a2635cb1f
146 lines
4.1 KiB
C++
146 lines
4.1 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include <forward_list>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <stack>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#ifndef YOGA_EXPORT
|
|
#ifdef _MSC_VER
|
|
#define YOGA_EXPORT
|
|
#else
|
|
#define YOGA_EXPORT __attribute__((visibility("default")))
|
|
#endif
|
|
#endif
|
|
|
|
namespace facebook {
|
|
namespace yoga {
|
|
|
|
namespace detail {
|
|
|
|
class YOGA_EXPORT FreeList {
|
|
std::stack<void*> free_;
|
|
void* getRaw();
|
|
|
|
public:
|
|
FreeList();
|
|
~FreeList();
|
|
|
|
void put(std::mutex&, void*);
|
|
|
|
template <typename T>
|
|
T* get() {
|
|
return static_cast<T*>(getRaw());
|
|
}
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
/// SingleWriterValueList is a data structure that holds a list of values. Each
|
|
/// value can be borrowed for exclusive writing, and will not be exposed to
|
|
/// another borrower until returned.
|
|
/// Additionaly, the whole list of values can be accessed for reading via const
|
|
/// iterators. Read consistency depends on CPU internals, i.e. whether values
|
|
/// are written to memory atomically.
|
|
///
|
|
/// A typical usage scenario would be a set of threads, where each thread
|
|
/// borrows a value for lock free writing, e.g. as a thread local variable. This
|
|
/// avoids the usage of atomics, or locking of shared memory, which both can
|
|
/// lead to increased latency due to CPU cache flushes and waits.
|
|
///
|
|
/// Values are heap allocated (via forward_list), which typically will avoid
|
|
/// multiple values being allocated in the same CPU cache line, which would also
|
|
/// lead to cache flushing.
|
|
///
|
|
/// SingleWriterValueList never deallocates, to guarantee the validity of
|
|
/// references and iterators. However, memory returned by a borrower can be
|
|
/// borrowed again.
|
|
///
|
|
/// SingleWriterValueList supports return policies as second template parameter,
|
|
/// i.e. an optional mutation of values after a borrower returns them. The
|
|
/// default policy is to do nothing. SingleWriterValueList::resetPolicy is a
|
|
/// convenience method that will move assign the default value of a type.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// static SingleWriterValueList<int> counters;
|
|
/// thread_local auto localCounter = counters.borrow();
|
|
///
|
|
/// /* per thread */
|
|
/// localCounter =+ n;
|
|
///
|
|
/// /* anywhere */
|
|
/// std::accumulate(counters.begin(), counters.end(), 0);
|
|
///
|
|
template <typename T, void (*ReturnPolicy)(T&) = nullptr>
|
|
class YOGA_EXPORT SingleWriterValueList {
|
|
std::forward_list<T> values_{};
|
|
std::mutex acquireMutex_{};
|
|
detail::FreeList freeValuesList_{};
|
|
|
|
T* allocValue() {
|
|
values_.emplace_front();
|
|
return &values_.front();
|
|
}
|
|
|
|
void returnRef(T* value) {
|
|
if (ReturnPolicy != nullptr) {
|
|
ReturnPolicy(*value);
|
|
}
|
|
freeValuesList_.put(acquireMutex_, value);
|
|
}
|
|
|
|
public:
|
|
using const_iterator = decltype(values_.cbegin());
|
|
|
|
/// RAII representation of a single value, borrowed for exclusive writing.
|
|
/// Instances cannot be copied, and will return the borrowed value to the
|
|
/// owner upon destruction.
|
|
class Borrowed {
|
|
T* value_;
|
|
SingleWriterValueList* owner_;
|
|
|
|
public:
|
|
Borrowed(T* value, SingleWriterValueList* owner)
|
|
: value_{value}, owner_{owner} {}
|
|
~Borrowed() {
|
|
if (owner_ != nullptr && value_ != nullptr) {
|
|
owner_->returnRef(value_);
|
|
}
|
|
}
|
|
|
|
Borrowed(Borrowed&& other) = default;
|
|
Borrowed& operator=(Borrowed&& other) = default;
|
|
|
|
// no copies allowed
|
|
Borrowed(const Borrowed&) = delete;
|
|
Borrowed& operator=(const Borrowed&) = delete;
|
|
|
|
T& get() { return *value_; }
|
|
T& operator*() { return get(); }
|
|
};
|
|
|
|
Borrowed borrow() {
|
|
std::lock_guard<std::mutex> lock{acquireMutex_};
|
|
T* value = freeValuesList_.get<T>();
|
|
return {value != nullptr ? value : allocValue(), this};
|
|
}
|
|
|
|
const_iterator cbegin() const { return values_.cbegin(); };
|
|
const_iterator cend() const { return values_.cend(); };
|
|
const_iterator begin() const { return cbegin(); };
|
|
const_iterator end() const { return cend(); };
|
|
|
|
static void resetPolicy(T& value) { value = std::move(T{}); }
|
|
};
|
|
|
|
} // namespace yoga
|
|
} // namespace facebook
|