Squashed 'third_party/marl/' changes from 748d3c161..3c643dd4c 3c643dd4c Reduce size of marl::ConditionVariable ffa88289a Pool::Loan::get(): Return nullptr if has no item. 6c9341313 Add a new test for 16-byte stack alignment for fibers. 924493413 Fix marl::parallelize race. git-subtree-dir: third_party/marl git-subtree-split: 3c643dd4c88d612a2622366074e029dca6a794d1
diff --git a/include/marl/conditionvariable.h b/include/marl/conditionvariable.h index 5a57db2..8579dcb 100644 --- a/include/marl/conditionvariable.h +++ b/include/marl/conditionvariable.h
@@ -17,7 +17,6 @@ #include "containers.h" #include "debug.h" -#include "defer.h" #include "memory.h" #include "mutex.h" #include "scheduler.h" @@ -159,10 +158,10 @@ if (pred()) { return true; } - numWaiting++; - defer(numWaiting--); if (auto fiber = Scheduler::Fiber::current()) { + numWaiting++; + // Currently executing on a scheduler fiber. // Yield to let other tasks run that can unblock this fiber. mutex.lock(); @@ -175,14 +174,18 @@ waiting.erase(it); mutex.unlock(); + numWaiting--; return res; - } else { - // Currently running outside of the scheduler. - // Delegate to the std::condition_variable. - numWaitingOnCondition++; - defer(numWaitingOnCondition--); - return lock.wait_until(condition, timeout, pred); } + + // Currently running outside of the scheduler. + // Delegate to the std::condition_variable. + numWaiting++; + numWaitingOnCondition++; + auto res = lock.wait_until(condition, timeout, pred); + numWaitingOnCondition--; + numWaiting--; + return res; } } // namespace marl
diff --git a/include/marl/containers.h b/include/marl/containers.h index fcac46e..acf421c 100644 --- a/include/marl/containers.h +++ b/include/marl/containers.h
@@ -292,6 +292,11 @@ list& operator=(const list&) = delete; list& operator=(list&&) = delete; + struct AllocationChain { + Allocation allocation; + AllocationChain* next; + }; + void grow(size_t count); static void unlink(Entry* entry, Entry*& list); @@ -300,7 +305,7 @@ Allocator* const allocator; size_t size_ = 0; size_t capacity = 0; - vector<Allocation, 8> allocations; + AllocationChain* allocations = nullptr; Entry* free = nullptr; Entry* head = nullptr; }; @@ -336,17 +341,19 @@ template <typename T> list<T>::list(Allocator* allocator /* = Allocator::Default */) - : allocator(allocator), allocations(allocator) { - grow(8); -} + : allocator(allocator) {} template <typename T> list<T>::~list() { for (auto el = head; el != nullptr; el = el->next) { el->data.~T(); } - for (auto alloc : allocations) { - allocator->free(alloc); + + auto curr = allocations; + while (curr != nullptr) { + auto next = curr->next; + allocator->free(curr->allocation); + curr = next; } } @@ -369,7 +376,7 @@ template <typename... Args> typename list<T>::iterator list<T>::emplace_front(Args&&... args) { if (free == nullptr) { - grow(capacity); + grow(std::max<size_t>(capacity, 8)); } auto entry = free; @@ -395,9 +402,13 @@ template <typename T> void list<T>::grow(size_t count) { + auto const entriesSize = sizeof(Entry) * count; + auto const allocChainOffset = alignUp(entriesSize, alignof(AllocationChain)); + auto const allocSize = allocChainOffset + sizeof(AllocationChain); + Allocation::Request request; - request.size = sizeof(Entry) * count; - request.alignment = alignof(Entry); + request.size = allocSize; + request.alignment = std::max(alignof(Entry), alignof(AllocationChain)); request.usage = Allocation::Usage::List; auto alloc = allocator->allocate(request); @@ -412,7 +423,12 @@ free = entry; } - allocations.emplace_back(std::move(alloc)); + auto allocChain = reinterpret_cast<AllocationChain*>( + reinterpret_cast<uint8_t*>(alloc.ptr) + allocChainOffset); + + allocChain->allocation = alloc; + allocChain->next = allocations; + allocations = allocChain; capacity += count; }
diff --git a/include/marl/event.h b/include/marl/event.h index bac6078..dbc9f4f 100644 --- a/include/marl/event.h +++ b/include/marl/event.h
@@ -26,7 +26,7 @@ // Event is a synchronization primitive used to block until a signal is raised. class Event { public: - enum class Mode { + enum class Mode : uint8_t { // The event signal will be automatically reset when a call to wait() // returns. // A single call to signal() will only unblock a single (possibly @@ -115,9 +115,9 @@ marl::mutex mutex; ConditionVariable cv; + containers::vector<std::shared_ptr<Shared>, 1> deps; const Mode mode; bool signalled; - containers::vector<std::shared_ptr<Shared>, 2> deps; }; const std::shared_ptr<Shared> shared;
diff --git a/include/marl/memory.h b/include/marl/memory.h index 8c35e01..8608851 100644 --- a/include/marl/memory.h +++ b/include/marl/memory.h
@@ -31,10 +31,15 @@ // system. size_t pageSize(); +template <typename T> +inline T alignUp(T val, T alignment) { + return alignment * ((val + alignment - 1) / alignment); +} + // Allocation holds the result of a memory allocation from an Allocator. struct Allocation { // Intended usage of the allocation. Used for allocation trackers. - enum class Usage { + enum class Usage : uint8_t { Undefined = 0, Stack, // Fiber stack Create, // Allocator::create(), make_unique(), make_shared()
diff --git a/include/marl/parallelize.h b/include/marl/parallelize.h index d7ceadc..ce75b02 100644 --- a/include/marl/parallelize.h +++ b/include/marl/parallelize.h
@@ -22,13 +22,13 @@ namespace detail { -void parallelizeChain(WaitGroup*) {} +void parallelizeChain(WaitGroup&) {} template <typename F, typename... L> -void parallelizeChain(WaitGroup* wg, F&& f, L&&... l) { +void parallelizeChain(WaitGroup& wg, F&& f, L&&... l) { schedule([=] { f(); - wg->done(); + wg.done(); }); parallelizeChain(wg, std::forward<L>(l)...); } @@ -41,7 +41,7 @@ template <typename... FUNCTIONS> inline void parallelize(FUNCTIONS&&... functions) { WaitGroup wg(sizeof...(FUNCTIONS)); - detail::parallelizeChain(&wg, functions...); + detail::parallelizeChain(wg, functions...); wg.wait(); }
diff --git a/include/marl/pool.h b/include/marl/pool.h index 393b457..70d53c9 100644 --- a/include/marl/pool.h +++ b/include/marl/pool.h
@@ -192,7 +192,7 @@ template <typename T> T* Pool<T>::Loan::get() const { - return item->get(); + return item ? item->get() : nullptr; } ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/event_test.cpp b/src/event_test.cpp index d00e721..0250329 100644 --- a/src/event_test.cpp +++ b/src/event_test.cpp
@@ -13,6 +13,7 @@ // limitations under the License. #include "marl/event.h" +#include "marl/defer.h" #include "marl/waitgroup.h" #include "marl_test.h"
diff --git a/src/memory.cpp b/src/memory.cpp index b9f6cc1..3ccfa3d 100644 --- a/src/memory.cpp +++ b/src/memory.cpp
@@ -129,11 +129,6 @@ namespace { -template <typename T> -inline T alignUp(T val, T alignment) { - return alignment * ((val + alignment - 1) / alignment); -} - // pagedMalloc() allocates size bytes of uninitialized storage with the // specified minimum byte alignment using OS specific page mapping calls. // If guardLow is true then reads or writes to the page below the returned @@ -188,8 +183,8 @@ inline void* alignedMalloc(size_t alignment, size_t size) { size_t allocSize = size + alignment + sizeof(void*); auto allocation = malloc(allocSize); - auto aligned = reinterpret_cast<uint8_t*>( - alignUp(reinterpret_cast<uintptr_t>(allocation), alignment)); // align + auto aligned = reinterpret_cast<uint8_t*>(marl::alignUp( + reinterpret_cast<uintptr_t>(allocation), alignment)); // align memcpy(aligned + size, &allocation, sizeof(void*)); // pointer-to-allocation return aligned; }
diff --git a/src/osfiber_test.cpp b/src/osfiber_test.cpp index 3b6fe81..d31b3a8 100644 --- a/src/osfiber_test.cpp +++ b/src/osfiber_test.cpp
@@ -16,9 +16,14 @@ #include "marl_test.h" +namespace { + +auto constexpr fiberStackSize = 8 * 1024; + +} // anonymous namespace + TEST_F(WithoutBoundScheduler, OSFiber) { std::string str; - auto constexpr fiberStackSize = 8 * 1024; auto main = marl::OSFiber::createFiberFromCurrentThread(allocator); marl::Allocator::unique_ptr<marl::OSFiber> fiberA, fiberB, fiberC; fiberC = marl::OSFiber::createFiber(allocator, fiberStackSize, [&] { @@ -38,3 +43,26 @@ ASSERT_EQ(str, "CBA"); } + +TEST_F(WithoutBoundScheduler, StackAlignment) { + uintptr_t address = 0; + + struct alignas(16) AlignTo16Bytes { + uint64_t a, b; + }; + + auto main = marl::OSFiber::createFiberFromCurrentThread(allocator); + marl::Allocator::unique_ptr<marl::OSFiber> fiber; + fiber = marl::OSFiber::createFiber(allocator, fiberStackSize, [&] { + AlignTo16Bytes stack_var; + + address = reinterpret_cast<uintptr_t>(&stack_var); + + fiber->switchTo(main.get()); + }); + + main->switchTo(fiber.get()); + + ASSERT_TRUE((address & 15) == 0) + << "Stack variable had unaligned address: 0x" << std::hex << address; +}
diff --git a/src/pool_test.cpp b/src/pool_test.cpp index 6ae8f25..eec09b5 100644 --- a/src/pool_test.cpp +++ b/src/pool_test.cpp
@@ -26,6 +26,16 @@ marl::BoundedPool<int, 10> pool; } +TEST_P(WithBoundScheduler, UnboundedPoolLoan_GetNull) { + marl::UnboundedPool<int>::Loan loan; + ASSERT_EQ(loan.get(), nullptr); +} + +TEST_P(WithBoundScheduler, BoundedPoolLoan_GetNull) { + marl::BoundedPool<int, 10>::Loan loan; + ASSERT_EQ(loan.get(), nullptr); +} + TEST_P(WithBoundScheduler, UnboundedPool_Borrow) { marl::UnboundedPool<int> pool; for (int i = 0; i < 100; i++) {