Commit 2defafba by Ben Clayton

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
parent 27c6367d
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include "containers.h" #include "containers.h"
#include "debug.h" #include "debug.h"
#include "defer.h"
#include "memory.h" #include "memory.h"
#include "mutex.h" #include "mutex.h"
#include "scheduler.h" #include "scheduler.h"
...@@ -159,10 +158,10 @@ bool ConditionVariable::wait_until( ...@@ -159,10 +158,10 @@ bool ConditionVariable::wait_until(
if (pred()) { if (pred()) {
return true; return true;
} }
numWaiting++;
defer(numWaiting--);
if (auto fiber = Scheduler::Fiber::current()) { if (auto fiber = Scheduler::Fiber::current()) {
numWaiting++;
// Currently executing on a scheduler fiber. // Currently executing on a scheduler fiber.
// Yield to let other tasks run that can unblock this fiber. // Yield to let other tasks run that can unblock this fiber.
mutex.lock(); mutex.lock();
...@@ -175,14 +174,18 @@ bool ConditionVariable::wait_until( ...@@ -175,14 +174,18 @@ bool ConditionVariable::wait_until(
waiting.erase(it); waiting.erase(it);
mutex.unlock(); mutex.unlock();
numWaiting--;
return res; 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 } // namespace marl
......
...@@ -292,6 +292,11 @@ class list { ...@@ -292,6 +292,11 @@ class list {
list& operator=(const list&) = delete; list& operator=(const list&) = delete;
list& operator=(list&&) = delete; list& operator=(list&&) = delete;
struct AllocationChain {
Allocation allocation;
AllocationChain* next;
};
void grow(size_t count); void grow(size_t count);
static void unlink(Entry* entry, Entry*& list); static void unlink(Entry* entry, Entry*& list);
...@@ -300,7 +305,7 @@ class list { ...@@ -300,7 +305,7 @@ class list {
Allocator* const allocator; Allocator* const allocator;
size_t size_ = 0; size_t size_ = 0;
size_t capacity = 0; size_t capacity = 0;
vector<Allocation, 8> allocations; AllocationChain* allocations = nullptr;
Entry* free = nullptr; Entry* free = nullptr;
Entry* head = nullptr; Entry* head = nullptr;
}; };
...@@ -336,17 +341,19 @@ bool list<T>::iterator::operator!=(const iterator& rhs) const { ...@@ -336,17 +341,19 @@ bool list<T>::iterator::operator!=(const iterator& rhs) const {
template <typename T> template <typename T>
list<T>::list(Allocator* allocator /* = Allocator::Default */) list<T>::list(Allocator* allocator /* = Allocator::Default */)
: allocator(allocator), allocations(allocator) { : allocator(allocator) {}
grow(8);
}
template <typename T> template <typename T>
list<T>::~list() { list<T>::~list() {
for (auto el = head; el != nullptr; el = el->next) { for (auto el = head; el != nullptr; el = el->next) {
el->data.~T(); 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 T> ...@@ -369,7 +376,7 @@ template <typename T>
template <typename... Args> template <typename... Args>
typename list<T>::iterator list<T>::emplace_front(Args&&... args) { typename list<T>::iterator list<T>::emplace_front(Args&&... args) {
if (free == nullptr) { if (free == nullptr) {
grow(capacity); grow(std::max<size_t>(capacity, 8));
} }
auto entry = free; auto entry = free;
...@@ -395,9 +402,13 @@ void list<T>::erase(iterator it) { ...@@ -395,9 +402,13 @@ void list<T>::erase(iterator it) {
template <typename T> template <typename T>
void list<T>::grow(size_t count) { 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; Allocation::Request request;
request.size = sizeof(Entry) * count; request.size = allocSize;
request.alignment = alignof(Entry); request.alignment = std::max(alignof(Entry), alignof(AllocationChain));
request.usage = Allocation::Usage::List; request.usage = Allocation::Usage::List;
auto alloc = allocator->allocate(request); auto alloc = allocator->allocate(request);
...@@ -412,7 +423,12 @@ void list<T>::grow(size_t count) { ...@@ -412,7 +423,12 @@ void list<T>::grow(size_t count) {
free = entry; 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; capacity += count;
} }
......
...@@ -26,7 +26,7 @@ namespace marl { ...@@ -26,7 +26,7 @@ namespace marl {
// Event is a synchronization primitive used to block until a signal is raised. // Event is a synchronization primitive used to block until a signal is raised.
class Event { class Event {
public: public:
enum class Mode { enum class Mode : uint8_t {
// The event signal will be automatically reset when a call to wait() // The event signal will be automatically reset when a call to wait()
// returns. // returns.
// A single call to signal() will only unblock a single (possibly // A single call to signal() will only unblock a single (possibly
...@@ -115,9 +115,9 @@ class Event { ...@@ -115,9 +115,9 @@ class Event {
marl::mutex mutex; marl::mutex mutex;
ConditionVariable cv; ConditionVariable cv;
containers::vector<std::shared_ptr<Shared>, 1> deps;
const Mode mode; const Mode mode;
bool signalled; bool signalled;
containers::vector<std::shared_ptr<Shared>, 2> deps;
}; };
const std::shared_ptr<Shared> shared; const std::shared_ptr<Shared> shared;
......
...@@ -31,10 +31,15 @@ namespace marl { ...@@ -31,10 +31,15 @@ namespace marl {
// system. // system.
size_t pageSize(); 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. // Allocation holds the result of a memory allocation from an Allocator.
struct Allocation { struct Allocation {
// Intended usage of the allocation. Used for allocation trackers. // Intended usage of the allocation. Used for allocation trackers.
enum class Usage { enum class Usage : uint8_t {
Undefined = 0, Undefined = 0,
Stack, // Fiber stack Stack, // Fiber stack
Create, // Allocator::create(), make_unique(), make_shared() Create, // Allocator::create(), make_unique(), make_shared()
......
...@@ -22,13 +22,13 @@ namespace marl { ...@@ -22,13 +22,13 @@ namespace marl {
namespace detail { namespace detail {
void parallelizeChain(WaitGroup*) {} void parallelizeChain(WaitGroup&) {}
template <typename F, typename... L> template <typename F, typename... L>
void parallelizeChain(WaitGroup* wg, F&& f, L&&... l) { void parallelizeChain(WaitGroup& wg, F&& f, L&&... l) {
schedule([=] { schedule([=] {
f(); f();
wg->done(); wg.done();
}); });
parallelizeChain(wg, std::forward<L>(l)...); parallelizeChain(wg, std::forward<L>(l)...);
} }
...@@ -41,7 +41,7 @@ void parallelizeChain(WaitGroup* wg, F&& f, L&&... l) { ...@@ -41,7 +41,7 @@ void parallelizeChain(WaitGroup* wg, F&& f, L&&... l) {
template <typename... FUNCTIONS> template <typename... FUNCTIONS>
inline void parallelize(FUNCTIONS&&... functions) { inline void parallelize(FUNCTIONS&&... functions) {
WaitGroup wg(sizeof...(FUNCTIONS)); WaitGroup wg(sizeof...(FUNCTIONS));
detail::parallelizeChain(&wg, functions...); detail::parallelizeChain(wg, functions...);
wg.wait(); wg.wait();
} }
......
...@@ -192,7 +192,7 @@ T* Pool<T>::Loan::operator->() const { ...@@ -192,7 +192,7 @@ T* Pool<T>::Loan::operator->() const {
template <typename T> template <typename T>
T* Pool<T>::Loan::get() const { T* Pool<T>::Loan::get() const {
return item->get(); return item ? item->get() : nullptr;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include "marl/event.h" #include "marl/event.h"
#include "marl/defer.h"
#include "marl/waitgroup.h" #include "marl/waitgroup.h"
#include "marl_test.h" #include "marl_test.h"
......
...@@ -129,11 +129,6 @@ inline void protectPage(void* addr) { ...@@ -129,11 +129,6 @@ inline void protectPage(void* addr) {
namespace { 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 // pagedMalloc() allocates size bytes of uninitialized storage with the
// specified minimum byte alignment using OS specific page mapping calls. // specified minimum byte alignment using OS specific page mapping calls.
// If guardLow is true then reads or writes to the page below the returned // If guardLow is true then reads or writes to the page below the returned
...@@ -188,8 +183,8 @@ void pagedFree(void* ptr, ...@@ -188,8 +183,8 @@ void pagedFree(void* ptr,
inline void* alignedMalloc(size_t alignment, size_t size) { inline void* alignedMalloc(size_t alignment, size_t size) {
size_t allocSize = size + alignment + sizeof(void*); size_t allocSize = size + alignment + sizeof(void*);
auto allocation = malloc(allocSize); auto allocation = malloc(allocSize);
auto aligned = reinterpret_cast<uint8_t*>( auto aligned = reinterpret_cast<uint8_t*>(marl::alignUp(
alignUp(reinterpret_cast<uintptr_t>(allocation), alignment)); // align reinterpret_cast<uintptr_t>(allocation), alignment)); // align
memcpy(aligned + size, &allocation, sizeof(void*)); // pointer-to-allocation memcpy(aligned + size, &allocation, sizeof(void*)); // pointer-to-allocation
return aligned; return aligned;
} }
......
...@@ -16,9 +16,14 @@ ...@@ -16,9 +16,14 @@
#include "marl_test.h" #include "marl_test.h"
namespace {
auto constexpr fiberStackSize = 8 * 1024;
} // anonymous namespace
TEST_F(WithoutBoundScheduler, OSFiber) { TEST_F(WithoutBoundScheduler, OSFiber) {
std::string str; std::string str;
auto constexpr fiberStackSize = 8 * 1024;
auto main = marl::OSFiber::createFiberFromCurrentThread(allocator); auto main = marl::OSFiber::createFiberFromCurrentThread(allocator);
marl::Allocator::unique_ptr<marl::OSFiber> fiberA, fiberB, fiberC; marl::Allocator::unique_ptr<marl::OSFiber> fiberA, fiberB, fiberC;
fiberC = marl::OSFiber::createFiber(allocator, fiberStackSize, [&] { fiberC = marl::OSFiber::createFiber(allocator, fiberStackSize, [&] {
...@@ -38,3 +43,26 @@ TEST_F(WithoutBoundScheduler, OSFiber) { ...@@ -38,3 +43,26 @@ TEST_F(WithoutBoundScheduler, OSFiber) {
ASSERT_EQ(str, "CBA"); 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;
}
...@@ -26,6 +26,16 @@ TEST_P(WithBoundScheduler, BoundedPool_ConstructDestruct) { ...@@ -26,6 +26,16 @@ TEST_P(WithBoundScheduler, BoundedPool_ConstructDestruct) {
marl::BoundedPool<int, 10> pool; 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) { TEST_P(WithBoundScheduler, UnboundedPool_Borrow) {
marl::UnboundedPool<int> pool; marl::UnboundedPool<int> pool;
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment