Update Marl to 3c643dd4c

Changes:
    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.

Commands:
    ./third_party/update-marl.sh --squash

Bug: b/140546382
Change-Id: Ie3d376ae9e80d5a40960aee07b664be2d75c69d5
diff --git a/third_party/marl/include/marl/conditionvariable.h b/third_party/marl/include/marl/conditionvariable.h
index 5a57db2..8579dcb 100644
--- a/third_party/marl/include/marl/conditionvariable.h
+++ b/third_party/marl/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/third_party/marl/include/marl/containers.h b/third_party/marl/include/marl/containers.h
index fcac46e..acf421c 100644
--- a/third_party/marl/include/marl/containers.h
+++ b/third_party/marl/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/third_party/marl/include/marl/event.h b/third_party/marl/include/marl/event.h
index bac6078..dbc9f4f 100644
--- a/third_party/marl/include/marl/event.h
+++ b/third_party/marl/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/third_party/marl/include/marl/memory.h b/third_party/marl/include/marl/memory.h
index 8c35e01..8608851 100644
--- a/third_party/marl/include/marl/memory.h
+++ b/third_party/marl/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/third_party/marl/include/marl/parallelize.h b/third_party/marl/include/marl/parallelize.h
index d7ceadc..ce75b02 100644
--- a/third_party/marl/include/marl/parallelize.h
+++ b/third_party/marl/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/third_party/marl/include/marl/pool.h b/third_party/marl/include/marl/pool.h
index 393b457..70d53c9 100644
--- a/third_party/marl/include/marl/pool.h
+++ b/third_party/marl/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/third_party/marl/src/event_test.cpp b/third_party/marl/src/event_test.cpp
index d00e721..0250329 100644
--- a/third_party/marl/src/event_test.cpp
+++ b/third_party/marl/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/third_party/marl/src/memory.cpp b/third_party/marl/src/memory.cpp
index b9f6cc1..3ccfa3d 100644
--- a/third_party/marl/src/memory.cpp
+++ b/third_party/marl/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/third_party/marl/src/osfiber_test.cpp b/third_party/marl/src/osfiber_test.cpp
index 3b6fe81..d31b3a8 100644
--- a/third_party/marl/src/osfiber_test.cpp
+++ b/third_party/marl/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/third_party/marl/src/pool_test.cpp b/third_party/marl/src/pool_test.cpp
index 6ae8f25..eec09b5 100644
--- a/third_party/marl/src/pool_test.cpp
+++ b/third_party/marl/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++) {