Commit c226674c authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

cut Tearable

Summary: It is unused. An upcoming Concurrency TS will have a replacement.

Reviewed By: mshneer

Differential Revision: D29597486

fbshipit-source-id: a108b945ce32eb17cedefad89630c9171fc5c9c2
parent cb55944f
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <atomic>
#include <cstring>
#include <type_traits>
#include <folly/Traits.h>
namespace folly {
/**
* This class allows you to perform torn loads and stores on the bits of a
* trivially-copyable type T without triggering undefined behavior. You may
* encounter corrupt data, but should not encounter nasal demons.
*
* This class provides no atomicity or memory ordering. Loads and stores are
* expected often to be data races. Synchronization is expected to be provided
* externally, and this class is helpful in building higher-level optimistic
* concurrency tools in combination with externally-provided synchronization.
*
* To see why this is useful, consider the guarantees provided by
* std::atomic<T>. It ensures that every load returns a T that was stored in the
* atomic. If T is too large to be read/written with a single load/store
* instruction, std::atomic<T> falls back to locking to provide this guarantee.
* Users pay this cost even if they have some higher-level mechanism (an
* external lock, version numbers, other application-level reasoning) that makes
* them resilient to torn reads. Tearable<T> allows concurrent access without
* these costs.
*
* For types smaller than the processor word size, prefer std::atomic<T>.
*/
template <typename T>
class Tearable {
public:
// We memcpy the object representation, and the destructor would not know how
// to deal with an object state it doesn't understand.
static_assert(
is_trivially_copyable<T>::value,
"Tearable types must be trivially copyable.");
Tearable() noexcept {
for (std::size_t i = 0; i < kNumDataWords; ++i) {
std::atomic_init(&data_[i], RawWord{});
}
}
Tearable(const T& val) : Tearable() { store(val); }
// Note that while filling dst with invalid data should be fine, *doing
// anything* with the result may trigger undefined behavior unless you've
// verified that the data you read was consistent.
void load(T& dst) const {
RawWord newDst[kNumDataWords];
for (std::size_t i = 0; i < kNumDataWords; ++i) {
newDst[i] = data_[i].load(std::memory_order_relaxed);
}
std::memcpy(&dst, newDst, sizeof(T));
}
void store(const T& val) {
RawWord newData[kNumDataWords];
std::memcpy(newData, &val, sizeof(T));
for (std::size_t i = 0; i < kNumDataWords; ++i) {
data_[i].store(newData[i], std::memory_order_relaxed);
}
}
private:
// A union gets us memcpy-like copy semantics always.
union RawWord {
// "unsigned" here matters; we may read uninitialized values (in the
// trailing data word in write(), for instance).
unsigned char data alignas(void*)[sizeof(void*)];
};
const static std::size_t kNumDataWords =
(sizeof(T) + sizeof(RawWord) - 1) / sizeof(RawWord);
std::atomic<RawWord> data_[kNumDataWords];
};
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/synchronization/Tearable.h>
#include <atomic>
#include <thread>
#include <folly/portability/GTest.h>
using namespace folly;
namespace {
struct Data {
Data(unsigned char value) { setValue(value); }
void setValue(unsigned char value) {
for (auto& item : contents) {
item = value;
}
}
void checkValue(unsigned char value) {
for (auto& item : contents) {
ASSERT_EQ(value, item);
}
}
void checkValue2(unsigned char value1, unsigned char value2) {
for (auto& item : contents) {
ASSERT_TRUE(item == value1 || item == value2);
}
}
// Note the odd size -- this will hopefully expose layout bugs under
// sanitizers.
unsigned char contents[99];
};
static_assert(is_trivially_copyable<Data>::value, "not trivially-copyable");
TEST(TearableTest, BasicOperations) {
Tearable<Data> tearable;
Data src(0);
Data dst(1);
for (char c = 0; c < 10; ++c) {
src.setValue(c);
tearable.store(src);
tearable.load(dst);
dst.checkValue(c);
}
}
TEST(TearableTest, Races) {
std::atomic<bool> stop(false);
Tearable<Data> tearable(Data(1));
std::thread write1([&]() {
Data data0(1);
while (!stop.load(std::memory_order_relaxed)) {
tearable.store(data0);
}
});
std::thread write2([&]() {
Data data1(2);
while (!stop.load(std::memory_order_relaxed)) {
tearable.store(data1);
}
});
Data val(0);
for (int i = 0; i < 100 * 1000; ++i) {
tearable.load(val);
val.checkValue2(1, 2);
}
stop.store(true, std::memory_order_relaxed);
write1.join();
write2.join();
}
} // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment