Move SingleWriterValueList to fbandroid (#1196)
Summary: Pull Request resolved: https://github.com/facebook/yoga/pull/1196 This is projected to the Yoga OSS repo as its own top-level directory, and its own static library, but we only ever use this in one specific place in fbandroid. Move this code there and to the same library. Reviewed By: rshest Differential Revision: D42240879 fbshipit-source-id: 97687310339fa05016d98b7c11574ea3e1c2b9a3
This commit is contained in:
committed by
Facebook GitHub Bot
parent
287c48f7e3
commit
fb4917c03c
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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 "SingleWriterValueList.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace yoga {
|
||||
namespace detail {
|
||||
|
||||
void* FreeList::getRaw() {
|
||||
if (free_.size() == 0)
|
||||
return nullptr;
|
||||
|
||||
auto ptr = free_.top();
|
||||
free_.pop();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeList::put(std::mutex& mutex, void* ptr) {
|
||||
std::lock_guard<std::mutex> lock{mutex};
|
||||
free_.push(ptr);
|
||||
}
|
||||
|
||||
FreeList::FreeList() = default;
|
||||
FreeList::~FreeList() = default;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace yoga
|
||||
} // namespace facebook
|
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* 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
|
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* 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 <gtest/gtest.h>
|
||||
#include <yoga/util/SingleWriterValueList.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace facebook {
|
||||
namespace yoga {
|
||||
|
||||
static_assert(
|
||||
!std::is_copy_constructible<SingleWriterValueList<int>>::value,
|
||||
"SingleWriterValueList must not be copyable");
|
||||
static_assert(
|
||||
!std::is_copy_assignable<SingleWriterValueList<int>>::value,
|
||||
"SingleWriterValueList must not be copyable");
|
||||
static_assert(
|
||||
!std::is_copy_constructible<SingleWriterValueList<int>::Borrowed>::value,
|
||||
"SingleWriterValueList::Borrowed must not be copyable");
|
||||
static_assert(
|
||||
!std::is_copy_assignable<SingleWriterValueList<int>::Borrowed>::value,
|
||||
"SingleWriterValueList::Borrowed must not be copyable");
|
||||
static_assert(
|
||||
std::is_move_constructible<SingleWriterValueList<int>::Borrowed>::value,
|
||||
"SingleWriterValueList::Borrowed must be movable");
|
||||
static_assert(
|
||||
std::is_move_assignable<SingleWriterValueList<int>::Borrowed>::value,
|
||||
"SingleWriterValueList::Borrowed must be movable");
|
||||
|
||||
TEST(SingleWriterValueList, borrowsAreExclusive) {
|
||||
SingleWriterValueList<int> x{};
|
||||
|
||||
auto a = x.borrow();
|
||||
auto b = x.borrow();
|
||||
|
||||
ASSERT_NE(&a.get(), &b.get());
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, borrowsSupportDereference) {
|
||||
SingleWriterValueList<int> x{};
|
||||
|
||||
auto a = x.borrow();
|
||||
*a = 123;
|
||||
|
||||
ASSERT_EQ(*a, 123);
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, borrowsHaveGetMethod) {
|
||||
SingleWriterValueList<int> x{};
|
||||
|
||||
auto a = x.borrow();
|
||||
a.get() = 123;
|
||||
|
||||
ASSERT_EQ(a.get(), 123);
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, exposesBorrowsViaIterator) {
|
||||
SingleWriterValueList<int> x{};
|
||||
|
||||
auto a = x.borrow();
|
||||
auto b = x.borrow();
|
||||
|
||||
*a = 12;
|
||||
*b = 34;
|
||||
|
||||
int sum = 0;
|
||||
for (auto& i : x) {
|
||||
sum += i;
|
||||
}
|
||||
ASSERT_EQ(sum, 12 + 34);
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, exposesBorrowsViaConstIterator) {
|
||||
SingleWriterValueList<int> x{};
|
||||
|
||||
auto a = x.borrow();
|
||||
auto b = x.borrow();
|
||||
|
||||
*a = 12;
|
||||
*b = 34;
|
||||
|
||||
ASSERT_EQ(std::accumulate(x.cbegin(), x.cend(), 0), 12 + 34);
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, doesNotDeallocateReturnedBorrows) {
|
||||
SingleWriterValueList<int> x{};
|
||||
|
||||
std::unordered_set<const int*> values;
|
||||
{
|
||||
auto a = x.borrow();
|
||||
auto b = x.borrow();
|
||||
values.insert(&a.get());
|
||||
values.insert(&b.get());
|
||||
}
|
||||
|
||||
auto it = x.begin();
|
||||
|
||||
ASSERT_NE(it, x.end());
|
||||
ASSERT_NE(values.find(&*it), values.end());
|
||||
|
||||
ASSERT_NE(++it, x.end());
|
||||
ASSERT_NE(values.find(&*it), values.end());
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, reusesReturnedBorrows) {
|
||||
SingleWriterValueList<int> x{};
|
||||
|
||||
int* firstBorrow;
|
||||
{
|
||||
auto a = x.borrow();
|
||||
firstBorrow = &a.get();
|
||||
}
|
||||
|
||||
auto b = x.borrow();
|
||||
|
||||
ASSERT_EQ(&b.get(), firstBorrow);
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, keepsValuesAfterReturning) {
|
||||
SingleWriterValueList<int> x{};
|
||||
|
||||
{
|
||||
auto a = x.borrow();
|
||||
*a = 123;
|
||||
}
|
||||
|
||||
ASSERT_EQ(*x.begin(), 123);
|
||||
}
|
||||
|
||||
static void addOne(int& v) {
|
||||
v += 1;
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, allowsCustomReturnPolicy) {
|
||||
SingleWriterValueList<int, addOne> x{};
|
||||
|
||||
{
|
||||
auto a = x.borrow();
|
||||
*a = 123;
|
||||
}
|
||||
|
||||
ASSERT_EQ(*x.begin(), 124);
|
||||
}
|
||||
|
||||
TEST(SingleWriterValueList, hasConvenienceResetPolicy) {
|
||||
SingleWriterValueList<int, SingleWriterValueList<int>::resetPolicy> x{};
|
||||
|
||||
{
|
||||
auto a = x.borrow();
|
||||
*a = 123;
|
||||
}
|
||||
|
||||
ASSERT_EQ(*x.begin(), 0);
|
||||
}
|
||||
|
||||
} // namespace yoga
|
||||
} // namespace facebook
|
Reference in New Issue
Block a user