folly copyright 2015 -> copyright 2016
[folly.git] / folly / test / PackedSyncPtrTest.cpp
1 /*
2  * Copyright 2016 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/PackedSyncPtr.h>
18
19 #include <cinttypes>
20 #include <gtest/gtest.h>
21 #include <thread>
22 #include <unordered_map>
23 #include <utility>
24
25 using folly::PackedSyncPtr;
26
27 namespace {
28
29 // Compile time check for packability.  This requires that
30 // PackedSyncPtr is a POD struct on gcc.
31 FOLLY_PACK_PUSH
32 struct ignore { PackedSyncPtr<int> foo; char c; } FOLLY_PACK_ATTR;
33 FOLLY_PACK_POP
34 static_assert(sizeof(ignore) == 9, "PackedSyncPtr wasn't packable");
35
36 }
37
38 TEST(PackedSyncPtr, Basic) {
39   PackedSyncPtr<std::pair<int,int>> sp;
40   sp.init(new std::pair<int,int>[2]);
41   EXPECT_EQ(sizeof(sp), 8);
42   sp->first = 5;
43   EXPECT_EQ(sp[0].first, 5);
44   sp[1].second = 7;
45   EXPECT_EQ(sp[1].second, 7);
46   sp.lock();
47   EXPECT_EQ(sp[1].second, 7);
48   sp[0].first = 9;
49   EXPECT_EQ(sp->first, 9);
50   sp.unlock();
51   EXPECT_EQ((sp.get() + 1)->second, 7);
52
53   sp.lock();
54   EXPECT_EQ(sp.extra(), 0);
55   sp.setExtra(0x13);
56   EXPECT_EQ(sp.extra(), 0x13);
57   EXPECT_EQ((sp.get() + 1)->second, 7);
58   delete[] sp.get();
59   auto newP = new std::pair<int,int>();
60   sp.set(newP);
61   EXPECT_EQ(sp.extra(), 0x13);
62   EXPECT_EQ(sp.get(), newP);
63   sp.unlock();
64   delete sp.get();
65 }
66
67 // Here we use the PackedSyncPtr to lock the whole SyncVec (base, *base, and sz)
68 template<typename T>
69 struct SyncVec {
70   PackedSyncPtr<T> base;
71   SyncVec() { base.init(); }
72   ~SyncVec() { free(base.get()); }
73   void push_back(const T& t) {
74     base.set((T*) realloc(base.get(),
75       (base.extra() + 1) * sizeof(T)));
76     base[base.extra()] = t;
77     base.setExtra(base.extra() + 1);
78   }
79   void lock() {
80     base.lock();
81   }
82   void unlock() {
83     base.unlock();
84   }
85
86   T* begin() const { return base.get(); }
87   T* end() const { return base.get() + base.extra(); }
88 };
89 typedef SyncVec<intptr_t> VecT;
90 typedef std::unordered_map<int64_t, VecT> Map;
91 const int mapCap = 1317;
92 const int nthrs = 297;
93 static Map map(mapCap);
94
95 // Each app thread inserts it's ID into every vec in map
96 // map is read only, so doesn't need any additional locking
97 void appThread(intptr_t id) {
98   for (auto& kv : map) {
99     kv.second.lock();
100     kv.second.push_back(id);
101     kv.second.unlock();
102   }
103 }
104
105 TEST(PackedSyncPtr, Application) {
106   for (int64_t i = 0; i < mapCap / 2; ++i) {
107     map.insert(std::make_pair(i, VecT()));
108   }
109   std::vector<std::thread> thrs;
110   for (intptr_t i = 0; i < nthrs; i++) {
111     thrs.push_back(std::thread(appThread, i));
112   }
113   for (auto& t : thrs) {
114     t.join();
115   }
116
117   for (auto& kv : map) {
118     // Make sure every thread successfully inserted it's ID into every vec
119     std::set<intptr_t> idsFound;
120     for (auto& elem : kv.second) {
121       EXPECT_TRUE(idsFound.insert(elem).second);  // check for dups
122     }
123     EXPECT_EQ(idsFound.size(), nthrs); // check they are all there
124   }
125 }
126
127 TEST(PackedSyncPtr, extraData) {
128   PackedSyncPtr<int> p;
129   p.init();
130   int* unaligned = reinterpret_cast<int*>(0xf003);
131   p.lock();
132   p.set(unaligned);
133   uintptr_t* bytes = reinterpret_cast<uintptr_t*>(&p);
134   LOG(INFO) << "Bytes integer is: 0x" << std::hex << *bytes;
135   EXPECT_EQ(p.get(), unaligned);
136   p.unlock();
137 }