/* * Copyright (c) Meta Platforms, Inc. and 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 #include #include #include #include #include using folly::PackedSyncPtr; namespace { // Compile time check for packability. This requires that // PackedSyncPtr is a POD struct on gcc. FOLLY_PACK_PUSH struct ignore { PackedSyncPtr foo; char c; } FOLLY_PACK_ATTR; FOLLY_PACK_POP static_assert(sizeof(ignore) == 9, "PackedSyncPtr wasn't packable"); } // namespace TEST(PackedSyncPtr, Basic) { PackedSyncPtr> sp; sp.init(new std::pair[2]); EXPECT_EQ(sizeof(sp), 8); sp->first = 5; EXPECT_EQ(sp[0].first, 5); sp[1].second = 7; EXPECT_EQ(sp[1].second, 7); sp.lock(); EXPECT_EQ(sp[1].second, 7); sp[0].first = 9; EXPECT_EQ(sp->first, 9); sp.unlock(); EXPECT_EQ((sp.get() + 1)->second, 7); sp.lock(); EXPECT_EQ(sp.extra(), 0); sp.setExtra(0x13); EXPECT_EQ(sp.extra(), 0x13); EXPECT_EQ((sp.get() + 1)->second, 7); delete[] sp.get(); auto newP = new std::pair(); sp.set(newP); EXPECT_EQ(sp.extra(), 0x13); EXPECT_EQ(sp.get(), newP); sp.unlock(); delete sp.get(); } // Here we use the PackedSyncPtr to lock the whole SyncVec (base, *base, and sz) template struct SyncVec { PackedSyncPtr base; SyncVec() { base.init(); } ~SyncVec() { free(base.get()); } void push_back(const T& t) { base.set((T*)realloc(base.get(), (base.extra() + 1) * sizeof(T))); base[base.extra()] = t; base.setExtra(base.extra() + 1); } void lock() { base.lock(); } void unlock() { base.unlock(); } T* begin() const { return base.get(); } T* end() const { return base.get() + base.extra(); } }; typedef SyncVec VecT; typedef std::unordered_map Map; const int mapCap = 1317; const int nthrs = 297; static Map map(mapCap); // Each app thread inserts it's ID into every vec in map // map is read only, so doesn't need any additional locking void appThread(intptr_t id) { for (auto& kv : map) { kv.second.lock(); kv.second.push_back(id); kv.second.unlock(); } } TEST(PackedSyncPtr, Application) { for (int64_t i = 0; i < mapCap / 2; ++i) { map.insert(std::make_pair(i, VecT())); } std::vector thrs; for (intptr_t i = 0; i < nthrs; i++) { thrs.push_back(std::thread(appThread, i)); } for (auto& t : thrs) { t.join(); } for (auto& kv : map) { // Make sure every thread successfully inserted it's ID into every vec std::set idsFound; for (auto& elem : kv.second) { EXPECT_TRUE(idsFound.insert(elem).second); // check for dups } EXPECT_EQ(idsFound.size(), nthrs); // check they are all there } } TEST(PackedSyncPtr, extraData) { PackedSyncPtr p; p.init(); int* unaligned = reinterpret_cast(0xf003); p.lock(); p.set(unaligned); uintptr_t* bytes = reinterpret_cast(&p); LOG(INFO) << "Bytes integer is: 0x" << std::hex << *bytes; EXPECT_EQ(p.get(), unaligned); p.unlock(); }