Add Tearable, for concurrently-modified non-atomic objects.
[folly.git] / folly / synchronization / test / TearableTest.cpp
1 /*
2  * Copyright 2018-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/synchronization/Tearable.h>
18
19 #include <atomic>
20 #include <thread>
21
22 #include <folly/portability/GTest.h>
23
24 using namespace folly;
25
26 namespace {
27
28 struct Data {
29   Data(unsigned char value) {
30     setValue(value);
31   }
32
33   void setValue(unsigned char value) {
34     for (auto& item : contents) {
35       item = value;
36     }
37   }
38
39   void checkValue(unsigned char value) {
40     for (auto& item : contents) {
41       ASSERT_EQ(value, item);
42     }
43   }
44
45   void checkValue2(unsigned char value1, unsigned char value2) {
46     for (auto& item : contents) {
47       ASSERT_TRUE(item == value1 || item == value2);
48     }
49   }
50
51   // Note the odd size -- this will hopefully expose layout bugs under
52   // sanitizers.
53   unsigned char contents[99];
54 };
55
56 TEST(TearableTest, BasicOperations) {
57   Tearable<Data> tearable;
58   Data src(0);
59   Data dst(1);
60   for (char c = 0; c < 10; ++c) {
61     src.setValue(c);
62     tearable.store(src);
63     tearable.load(dst);
64     dst.checkValue(c);
65   }
66 }
67
68 TEST(TearableTest, Races) {
69   std::atomic<bool> stop(false);
70   Tearable<Data> tearable(Data(1));
71   std::thread write1([&]() {
72     Data data0(1);
73     while (!stop.load(std::memory_order_relaxed)) {
74       tearable.store(data0);
75     }
76   });
77   std::thread write2([&]() {
78     Data data1(2);
79     while (!stop.load(std::memory_order_relaxed)) {
80       tearable.store(data1);
81     }
82   });
83   Data val(0);
84   for (int i = 0; i < 100 * 1000; ++i) {
85     tearable.load(val);
86     val.checkValue2(1, 2);
87   }
88   stop.store(true, std::memory_order_relaxed);
89   write1.join();
90   write2.join();
91 }
92
93 } // namespace