|
19 | 19 | #include <folly/Random.h> |
20 | 20 | #include <folly/Singleton.h> |
21 | 21 | #include <folly/synchronization/Baton.h> |
| 22 | +#include <folly/synchronization/Latch.h> |
22 | 23 |
|
23 | 24 | #include <algorithm> |
24 | 25 | #include <chrono> |
@@ -2469,6 +2470,80 @@ class BaseAllocatorTest : public AllocatorTest<AllocatorT> { |
2469 | 2470 | } |
2470 | 2471 | } |
2471 | 2472 |
|
| 2473 | + // tests case that correct parent item is acquired after move |
| 2474 | + void testChainedItemParentAcquireAfterMoveLoop() { |
| 2475 | + // create an allocator worth 250 slabs |
| 2476 | + // first slab is for overhead, second is parent class |
| 2477 | + // third is chained item 1 and rest are for new chained item alloc |
| 2478 | + // to move to. |
| 2479 | + std::unique_ptr<AllocatorT> alloc; |
| 2480 | + typename AllocatorT::Config config; |
| 2481 | + config.configureChainedItems(); |
| 2482 | + config.setCacheSize(250 * Slab::kSize); |
| 2483 | + |
| 2484 | + const std::set<uint32_t> allocSizes = {1024, 2048}; |
| 2485 | + auto sizes = std::vector<uint32_t>{500, 1500}; |
| 2486 | + std::atomic<uint64_t> numMoves{0}; |
| 2487 | + std::atomic<uint64_t> numReplaces{0}; |
| 2488 | + PoolId pid; |
| 2489 | + |
| 2490 | + using Item = typename AllocatorT::Item; |
| 2491 | + config.enableMovingOnSlabRelease([&](Item& oldItem, Item& newItem, |
| 2492 | + Item* parentPtr) { |
| 2493 | + assert(oldItem.getSize() == newItem.getSize()); |
| 2494 | + assert(oldItem.isChainedItem()); |
| 2495 | + std::memcpy(newItem.getMemory(), oldItem.getMemory(), oldItem.getSize()); |
| 2496 | + folly::Latch latch(1); |
| 2497 | + auto insertThread = std::make_unique<std::thread>([&]() { |
| 2498 | + ASSERT_NO_THROW({ |
| 2499 | + auto parentReplacement = |
| 2500 | + alloc->allocate(pid, parentPtr->getKey(), sizes[0]); |
| 2501 | + Item* parentCopy = parentPtr; |
| 2502 | + latch.count_down(); |
| 2503 | + while (parentCopy->isMoving()) |
| 2504 | + ; |
| 2505 | + alloc->insertOrReplace(parentReplacement); |
| 2506 | + ++numReplaces; |
| 2507 | + }); |
| 2508 | + }); |
| 2509 | + insertThread->detach(); |
| 2510 | + latch.wait(); |
| 2511 | + ++numMoves; |
| 2512 | + }); |
| 2513 | + |
| 2514 | + alloc = std::make_unique<AllocatorT>(config); |
| 2515 | + |
| 2516 | + const size_t numBytes = alloc->getCacheMemoryStats().ramCacheSize; |
| 2517 | + const auto poolSize = numBytes; |
| 2518 | + pid = alloc->addPool("one", poolSize, allocSizes); |
| 2519 | + |
| 2520 | + auto allocFn = [&](std::string keyPrefix, std::vector<uint32_t> sizes) { |
| 2521 | + for (unsigned int loop = 0; loop < 20; ++loop) { |
| 2522 | + for (unsigned int i = 0; i < 2048; ++i) { |
| 2523 | + const auto key = keyPrefix + folly::to<std::string>(loop) + "_" + |
| 2524 | + folly::to<std::string>(i); |
| 2525 | + auto itemHandle = |
| 2526 | + util::allocateAccessible(*alloc, pid, key, sizes[0]); |
| 2527 | + auto childItem = alloc->allocateChainedItem(itemHandle, sizes[1]); |
| 2528 | + ASSERT_NE(nullptr, childItem); |
| 2529 | + |
| 2530 | + alloc->addChainedItem(itemHandle, std::move(childItem)); |
| 2531 | + } |
| 2532 | + } |
| 2533 | + }; |
| 2534 | + allocFn(std::string{"yolo"}, sizes); |
| 2535 | + |
| 2536 | + ClassId cid = static_cast<ClassId>(1); |
| 2537 | + for (int i = 0; i < 20; i++) { |
| 2538 | + alloc->releaseSlab(pid, cid, SlabReleaseMode::kRebalance); |
| 2539 | + } |
| 2540 | + while (alloc->getSlabReleaseStats().numSlabReleaseForRebalance < 20) { |
| 2541 | + sleep(1); |
| 2542 | + } |
| 2543 | + // for ASSERT_EXIT |
| 2544 | + exit(0); |
| 2545 | + } |
| 2546 | + |
2472 | 2547 | // create a chain of allocations, replace the allocation and ensure that the |
2473 | 2548 | // order is preserved. |
2474 | 2549 | void testChainedAllocsReplaceInChain() { |
|
0 commit comments