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:
Nick Gerleman
2022-12-28 01:43:58 -08:00
committed by Facebook GitHub Bot
parent 287c48f7e3
commit fb4917c03c
3 changed files with 0 additions and 342 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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