From 8ae42cbe6194ee8b1c6d61fbfc508609b06d2552 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 7 May 2025 09:56:23 +0100 Subject: [PATCH 1/4] Rewrite C++ API Use the final parameter to `when` as the closure to call. This simplifies various parts of the C++ API, and have a, hopefully, easier to follow code structure. --- src/rt/cpp/cown.h | 49 +- src/rt/cpp/cown_array.h | 55 +- src/rt/cpp/lambdabehaviour.h | 6 - src/rt/cpp/when.h | 661 +++++++----------- src/rt/ds/morebits.h | 2 + src/rt/sched/cown.h | 2 +- test/func/atomic-sched/atomic-sched.cc | 124 ++-- test/func/backpressure/deadlock.h | 8 +- test/func/backpressure/unblock.h | 8 +- test/func/cown_weak_ref/cown_weak_ref.cc | 133 ++-- test/func/diningphilosophers/diningphil.cc | 174 +---- .../dynamic-cownset-readonly.cc | 59 +- test/func/dynamic-cownset/dynamic-cownset.cc | 49 +- test/func/early_release/early_release.cc | 24 +- test/func/external_threading/main.cc | 3 +- test/func/fair/fair.cc | 12 +- test/func/fair_variance/fair_variance.cc | 18 +- test/func/multimessage/multimessage.cc | 8 +- test/func/readonly/readonly.cc | 66 +- .../run-at-termination/run-at-termination.cc | 4 +- test/func/runtimepause/runtimepause.cc | 17 +- test/func/simp1/simp1.cc | 2 +- test/func/simp2/simp2.cc | 4 +- test/func/simp3/simp3.cc | 4 +- test/func/simp4/simp4.cc | 6 +- test/func/simp5/simp5.cc | 15 +- test/func/simp6/simp6.cc | 20 +- test/func/simp_read/simp_read.cc | 16 +- test/func/simp_weak1/simp_weak1.cc | 4 +- test/func/simp_weak2/simp_weak2.cc | 20 +- test/func/simp_weak3/simp_weak3.cc | 12 +- test/func/steal/steal.cc | 2 +- test/func/weak_leak_bug/weak_leak_bug.cc | 4 +- test/func/when-repeated-cown/repeat-cown.cc | 14 +- test/func/when-transfer/transfer.cc | 68 +- test/func/yield/yield.cc | 14 +- test/perf/backpressure1/backpressure1.cc | 8 +- test/perf/banking/verona_banks.cc | 51 +- test/perf/dining_phil/verona_dining_phil.cc | 22 +- test/perf/hashtable/hashtable.cc | 86 +-- test/perf/smallbank/smallbank.cc | 63 +- test/perf/worksteal/worksteal.cc | 20 +- 42 files changed, 876 insertions(+), 1061 deletions(-) diff --git a/src/rt/cpp/cown.h b/src/rt/cpp/cown.h index 0feb113e..a93f9494 100644 --- a/src/rt/cpp/cown.h +++ b/src/rt/cpp/cown.h @@ -189,11 +189,7 @@ namespace verona::cpp }; private: - template - friend class Access; - - template - friend class AccessBatch; + friend class When; /** * Internal Verona runtime cown for this type. @@ -203,7 +199,7 @@ namespace verona::cpp /** * Accesses the internal Verona runtime cown for this handle. */ - Cown* underlying_cown() + Cown* underlying_cown() const { return allocated_cown; } @@ -294,6 +290,11 @@ namespace verona::cpp return allocated_cown == nullptr; } + bool operator!=(std::nullptr_t) + { + return allocated_cown != nullptr; + } + /** * Sets the cown_ptr to nullptr, and decrements the reference count * if it was not already nullptr. @@ -331,7 +332,6 @@ namespace verona::cpp template friend cown_ptr make_cown(Args&&...); - template friend class When; }; @@ -404,36 +404,39 @@ namespace verona::cpp class acquired_cown { /// Needed to build one from inside a `When` - template friend class When; - template - friend class AccessBatch; + template + friend struct acquired_cown_span; private: /// Underlying cown that has been acquired. /// Runtime is actually holding this reference count. - ActualCown>& origin_cown; + Slot* wrapped_slot; - /// Constructor is private, as only `When` can construct one. - acquired_cown(ActualCown>& origin) - : origin_cown(origin) - {} + ActualCown>* raw_cown() const + { + return static_cast>*>( + wrapped_slot->cown()); + } public: + /// Constructor is private, as only `When` can construct one. + acquired_cown(Slot* slot) : wrapped_slot(slot) {} + /// Get a handle on the underlying cown. cown_ptr> cown() const { - verona::rt::Cown::acquire(&origin_cown); - return cown_ptr(&origin_cown); + verona::rt::Cown::acquire(raw_cown()); + return cown_ptr>(raw_cown()); } T& get_ref() const { if constexpr (std::is_const()) - return const_cast(origin_cown.value); + return const_cast(raw_cown()->value); else - return origin_cown.value; + return raw_cown()->value; } T& operator*() @@ -463,4 +466,12 @@ namespace verona::cpp acquired_cown& operator=(const acquired_cown&) = delete; /// @} }; + + template + Logging::SysLog& + operator<<(Logging::SysLog& log, const verona::cpp::acquired_cown& cown) + { + log << &cown.get_ref(); + return log; + } } // namespace verona::rt diff --git a/src/rt/cpp/cown_array.h b/src/rt/cpp/cown_array.h index 298f6748..e01fde13 100644 --- a/src/rt/cpp/cown_array.h +++ b/src/rt/cpp/cown_array.h @@ -16,8 +16,12 @@ namespace verona::cpp * * The destructor calls the destructor of each cown_ptr and frees the * allocated array. + * + * The template argument owning is used to determine if the cown_array + * should own the cown_ptr array or not. If it is false, the cown_array + * will not allocate a new array and will not free it in the destructor. */ - template + template struct cown_array { cown_ptr* array; @@ -25,20 +29,23 @@ namespace verona::cpp void constr_helper(cown_ptr* arr) { - array = reinterpret_cast*>( - heap::alloc(length * sizeof(cown_ptr*))); + if constexpr (owning) + { + array = reinterpret_cast*>( + heap::alloc(length * sizeof(cown_ptr*))); - for (size_t i = 0; i < length; i++) - new (&array[i]) cown_ptr(arr[i]); + for (size_t i = 0; i < length; i++) + new (&array[i]) cown_ptr(arr[i]); + } + else + { + array = arr; + } } - template cown_array(cown_ptr* array_, size_t length_) : length(length_) { - if constexpr (should_move == false) - { - constr_helper(array_); - } + constr_helper(array_); } cown_array(const cown_array& o) @@ -49,12 +56,24 @@ namespace verona::cpp ~cown_array() { - if (array) + if constexpr (owning) { - for (size_t i = 0; i < length; i++) - array[i].~cown_ptr(); + if (array) + { + for (size_t i = 0; i < length; i++) + array[i].~cown_ptr(); + heap::dealloc(array); + } + } + } + + void steal() + { + if constexpr (owning) + { heap::dealloc(array); + array = nullptr; } } @@ -72,15 +91,15 @@ namespace verona::cpp * We use inheritance to allow us to construct a cown_array from a * cown_array. */ - template - class cown_array : public cown_array + template + class cown_array : public cown_array { public: - cown_array(const cown_array& other) : cown_array(other){}; + cown_array(const cown_array& other) : cown_array(other){}; }; - template - cown_array read(cown_array cown) + template + cown_array read(cown_array cown) { Logging::cout() << "Read returning const array ptr" << Logging::endl; return cown; diff --git a/src/rt/cpp/lambdabehaviour.h b/src/rt/cpp/lambdabehaviour.h index 3a8c1bc9..8423d90d 100644 --- a/src/rt/cpp/lambdabehaviour.h +++ b/src/rt/cpp/lambdabehaviour.h @@ -20,12 +20,6 @@ namespace verona::rt Behaviour::schedule(count, cowns, std::forward(f)); } - template - static void schedule_lambda(size_t count, Request* requests, Be&& f) - { - Behaviour::schedule(count, requests, std::forward(f)); - } - template static void schedule_lambda(Be&& f) { diff --git a/src/rt/cpp/when.h b/src/rt/cpp/when.h index 0c6dc9f1..f7455f01 100644 --- a/src/rt/cpp/when.h +++ b/src/rt/cpp/when.h @@ -16,506 +16,385 @@ namespace verona::cpp using namespace verona::rt; template - struct acquired_cown_span + class acquired_cown_span { - acquired_cown* array; - size_t length; - }; - - /** - * Used to track the type of access request by embedding const into - * the type T, or not having const. - */ - template - class Access - { - using Type = T; - ActualCown>* t; - bool is_move; - - public: - Access(const cown_ptr& c) : t(c.allocated_cown), is_move(false) - { - assert(c.allocated_cown != nullptr); - } - - Access(cown_ptr&& c) : t(c.allocated_cown), is_move(true) - { - assert(c.allocated_cown != nullptr); - c.allocated_cown = nullptr; - } - - template friend class When; - }; - - /** - * Used to track the type of access request in the case of cown_array - * Ownership is handled the same for all cown_ptr in the span. - * If is_move is true, all cown_ptrs will be moved. - */ - template - class AccessBatch - { - using Type = T; - ActualCown>** act_array; - acquired_cown* acq_array; - size_t arr_len; - bool is_move; - - void constr_helper(const cown_array& ptr_span) - { - // Allocate the actual_cown and the acquired_cown array - // The acquired_cown array is after the actual_cown one - size_t act_size = - ptr_span.length * sizeof(ActualCown>*); - size_t acq_size = - ptr_span.length * sizeof(acquired_cown>); - act_array = reinterpret_cast>**>( - heap::alloc(act_size + acq_size)); - - for (size_t i = 0; i < ptr_span.length; i++) - { - act_array[i] = ptr_span.array[i].allocated_cown; - } - arr_len = ptr_span.length; - acq_array = - reinterpret_cast*>((char*)(act_array) + act_size); + Slot* array; + size_t length_; - for (size_t i = 0; i < ptr_span.length; i++) - { - new (&acq_array[i]) acquired_cown(*ptr_span.array[i].allocated_cown); - } - } + acquired_cown_span(Slot* arr, size_t len) : array(arr), length_(len) {} public: - AccessBatch(const cown_array& ptr_span) : is_move(false) + acquired_cown operator[](size_t i) { - constr_helper(ptr_span); + return acquired_cown(&array[i]); } - AccessBatch(cown_array&& ptr_span) : is_move(true) + size_t length() const { - constr_helper(ptr_span); - - ptr_span.length = 0; - ptr_span.arary = nullptr; + return length_; } - AccessBatch(AccessBatch&& old) - { - act_array = old.act_array; - acq_array = old.acq_array; - arr_len = old.arr_len; - is_move = old.is_move; - - old.acq_array = nullptr; - old.act_array = nullptr; - old.arr_len = 0; - } - - ~AccessBatch() - { - if (act_array) - { - heap::dealloc(act_array); - } - } - - AccessBatch& operator=(AccessBatch&&) = delete; - AccessBatch(const AccessBatch&) = delete; - AccessBatch& operator=(const AccessBatch&) = delete; - - template - friend class When; + // Remove move and copy constructors to prevent copying. + acquired_cown_span(const acquired_cown_span&) = delete; + acquired_cown_span& operator=(const acquired_cown_span&) = delete; + acquired_cown_span(acquired_cown_span&&) = delete; + acquired_cown_span& operator=(acquired_cown_span&&) = delete; }; - template - auto convert_access(const cown_ptr& c) - { - return Access(c); - } - - template - auto convert_access(cown_ptr&& c) - { - return Access(std::move(c)); - } - - template - auto convert_access(const cown_array& c) - { - return AccessBatch(c); - } - - template + template class Batch { - /// This is a tuple of - /// (exists Ts. When) - /// As existential types are not supported this is using inferred template - /// parameters. - std::tuple when_batch; + // Verona RT collection of behaviours to be scheduled. + std::array when_batch; /// This is used to prevent the destructor from scheduling the behaviour /// more than once. /// If this batch is combined with another batch, then the destructor of /// the uncombined batches should not run. - bool part_of_larger_batch = false; + bool part_of_larger_batch{false}; - template + template friend class Batch; - template - void create_behaviour(BehaviourCore** barray) + // To be initialised by the + operator. + template + Batch(Batch&& b1, Batch&& b2) { - if constexpr (index >= sizeof...(Args)) - { - return; - } - else - { - auto&& w = std::get(when_batch); - // Add the behaviour here - auto t = w.to_tuple(); - barray[index] = Behaviour::prepare_to_schedule< - typename std::remove_reference(t))>::type>( - std::move(std::get<0>(t)), - std::move(std::get<1>(t)), - std::move(std::get<2>(t))); - create_behaviour(barray); - } + b1.part_of_larger_batch = true; + b2.part_of_larger_batch = true; + for (size_t i = 0; i < Size1; i++) + when_batch[i] = b1.when_batch[i]; + for (size_t i = 0; i < Size2; i++) + when_batch[i + Size1] = b2.when_batch[i]; } public: - Batch(std::tuple args) : when_batch(std::move(args)) {} + Batch(BehaviourCore* b) : when_batch({b}) { + Logging::cout() << "Batch created " << this << " contents " << b << Logging::endl; + if ((uintptr_t)b < 4096) + abort(); + } Batch(const Batch&) = delete; ~Batch() { - if constexpr (sizeof...(Args) > 0) - { - if (part_of_larger_batch) - return; - - BehaviourCore* barray[sizeof...(Args)]; - create_behaviour(barray); + if (part_of_larger_batch) + return; - BehaviourCore::schedule_many(barray, sizeof...(Args)); - } + BehaviourCore::schedule_many(when_batch.data(), Size); } - template - auto operator+(Batch&& wb) + template + auto operator+(Batch&& wb) && { - wb.part_of_larger_batch = true; - this->part_of_larger_batch = true; - return Batch( - std::tuple_cat(std::move(this->when_batch), std::move(wb.when_batch))); + return Batch(std::move(*this), std::move(wb)); } }; - /** - * Represents a single when statement. - * - * It carries all the information needed to create the behaviour. - */ - template class When { - template - struct is_read_only : std::false_type - {}; - template - struct is_read_only&> : std::true_type - {}; - template - struct is_read_only&> : std::true_type - {}; - - template - struct is_batch : std::false_type - {}; - template - struct is_batch> : std::true_type - {}; - - template - friend class Batch; + template + friend Batch<1> when(Args&&... args); - /// Set of cowns used by this behaviour. - std::tuple cown_tuple; + template + struct Last; - /// The closure to be executed. - F f; + template + struct Last + { + using type = typename Last::type; + }; - /// Used as a temporary to build the behaviour. - /// The stack lifetime is tricky, and this avoids - /// a heap allocation. - Request requests[sizeof...(Args)]; + template + struct Last + { + using type = T; + }; - // If cown_ptr spans provided more requests are required - // and thus are dynamically allocated. - // If is_req_extended is true, then req_extended holds an array of Request - // and the above requests[] array is not used. - Request* req_extended; - bool is_req_extended; + template + static auto last(Arg&& arg, Args&&... args) + { + if constexpr (sizeof...(Args) == 0) + return std::forward(arg); + else + return last(std::forward(args)...); + } + ///@{ /** - * This uses template programming to turn the std::tuple into a C style - * stack allocated array. - * The index template parameter is used to perform each the assignment for - * each index. + * Helper to get the type of the cown from a cown_ptr or cown_array. + * + * This is used to get the type of the cown in a `when` clause. */ - template - static void array_assign_helper_access(Request* req, Access& p) + template + struct CownType; + + template + struct CownType> { - if constexpr (is_read_only()) - *req = Request::read(p.t); - else - *req = Request::write(p.t); + using type = T; + }; - if (p.is_move) - req->mark_move(); + template + struct CownType> + { + using type = T; + }; - assert(req->cown() != nullptr); - } + template + using GetCownType = + typename CownType>>::type; + ///@} - template - static size_t - array_assign_helper_access_batch(Request* req, AccessBatch& p) + ///@{ + /** + * Helper to distinguish cown_ptr and cown_array. + */ + template + struct IsCownArray_t { - size_t it_cnt = 0; - for (size_t i = 0; i < p.arr_len; i++) - { - if constexpr (is_read_only()) - *req = Request::read(p.act_array[i]); - else - *req = Request::write(p.act_array[i]); + static constexpr bool value = false; + }; - if (p.is_move) - req->mark_move(); + template + struct IsCownArray_t> + { + static constexpr bool value = true; + }; - req++; - it_cnt++; - } + template + static constexpr bool IsCownArray = + IsCownArray_t>>::value; + ///@} - return it_cnt; - } + struct Spec + { + size_t slot_count; + size_t span_count; + + Spec operator+(Spec other) const + { + return {slot_count + other.slot_count, span_count + other.span_count}; + } + }; - template - size_t array_assign(Request* requests) + template + static Spec calculate_spec(Arg&& arg, Args&&... args) { - if constexpr (index >= sizeof...(Args)) + if constexpr (sizeof...(args) == 0) { - return 0; + return {0, 0}; } else { - size_t it_cnt; - - auto& p = std::get(cown_tuple); - if constexpr (is_batch< - typename std::remove_reference::type>()) + auto rest = calculate_spec(std::forward(args)...); + if constexpr (IsCownArray) { - it_cnt = array_assign_helper_access_batch(requests, p); - requests += it_cnt; + Spec s = {arg.length, 1}; + return rest + s; } else { - array_assign_helper_access(requests, p); - requests++; - it_cnt = 1; + Spec s = {1, 0}; + return rest + s; } - return it_cnt + array_assign(requests); } } - template - size_t get_cown_count(size_t count = 0) + // Internal structure for representing a cown acquired by a `when`. + // This representation does not have the restrictions on lifetime + // theat `acquired_cown` has, so we can move it around. + template + struct AccessCown + { + Slot* slot; + + AccessCown(Slot* s) : slot(s) {} + }; + + // Internal structure for representing a cown array acquired by a `when`. + // See AccessCown for details. + template + struct AccessCownArray + { + Slot* slots; + size_t length; + }; + + // build a tuple using the type signature of the cown arguments. + // Needs to be passed the slots and lengths of any cown arrays. + template + static auto construct_access_tuple(Slot* slots, size_t* lengths) { - if constexpr (index >= sizeof...(Args)) + if constexpr (sizeof...(Args) == 0) { - return count; + return std::make_tuple(); } else { - auto& p = std::get(cown_tuple); - size_t to_add; - if constexpr (is_batch< - typename std::remove_reference::type>()) - to_add = p.arr_len; + if constexpr (IsCownArray) + { + size_t length = lengths[0]; + auto cown_tuple = + std::make_tuple>>({slots, length}); + return std::tuple_cat( + cown_tuple, + construct_access_tuple(slots + length, lengths + 1)); + } else - to_add = 1; - - return get_cown_count(count + to_add); + { + auto cown_tuple = + std::make_tuple>>(slots); + return std::tuple_cat( + cown_tuple, construct_access_tuple(slots + 1, lengths)); + } } } + ///@{ /** - * Converts a single `cown_ptr` into a `acquired_cown`. - * - * Needs to be a separate function for the template parameter to work. + * Convert the internal representation into an unmovable/copyable one. */ - template - static auto access_to_acquired(AccessBatch& c) + template + static auto convert_to_acquired(T); + + template + static auto convert_to_acquired(AccessCown ac) { - return acquired_cown_span{c.acq_array, c.arr_len}; + return acquired_cown{ac.slot}; } - template - static auto access_to_acquired(Access& c) + template + static auto convert_to_acquired(AccessCownArray ac) { - assert(c.t != nullptr); - return acquired_cown(*c.t); + return acquired_cown_span{ac.slots, ac.length}; } + ///@} - auto to_tuple() + // build a tuple using the type signature of the cown arguments. + // Needs to be passed the slots and lengths of any cown arrays. + template + static void + initialise_slots(Slot* slots, size_t* lengths, Arg&& cp, Args&&... args) { - if constexpr (sizeof...(Args) == 0) - { - return std::make_tuple(std::forward(f)); - } - else + if constexpr (sizeof...(Args) > 0) { - Request* r; - if (is_req_extended) - r = req_extended; + if constexpr (IsCownArray) + { + size_t length = cp.length; + lengths[0] = length; + + for (size_t i = 0; i < length; i++) + { + new (&slots[i]) Slot(cp.array[i].underlying_cown()); + if constexpr (std::is_const>()) + { + slots[i].set_read_only(); + } + if constexpr (std::is_rvalue_reference::value) + { + slots[i].set_move(); + } + } + if constexpr (std::is_rvalue_reference::value) + { + cp.steal(); + } + initialise_slots( + slots + length, lengths + 1, std::forward(args)...); + return; + } else - r = reinterpret_cast(&requests); - - size_t count = array_assign(r); - - return std::make_tuple( - count, - r, - [f = std::move(f), cown_tuple = std::move(cown_tuple)]() mutable { - /// Effectively converts ActualCown... to - /// acquired_cown... . - auto lift_f = [f = std::move(f)](Args... args) mutable { - std::move(f)(access_to_acquired(args)...); - }; - - std::apply(std::move(lift_f), std::move(cown_tuple)); - }); + { + new (slots) Slot(cp.underlying_cown()); + if constexpr (std::is_const>()) + { + slots[0].set_read_only(); + } + if constexpr (std::is_rvalue_reference::value) + { + slots[0].set_move(); + cp.allocated_cown = nullptr; + } + initialise_slots(slots + 1, lengths, std::forward(args)...); + } } } - public: - When(F&& f_) : f(std::forward(f_)) {} - - When(F&& f_, std::tuple cown_tuple_) - : f(std::forward(f_)), - cown_tuple(std::move(cown_tuple_)), - is_req_extended(false) + template + static void invoke(Work* work) { - const size_t req_count = get_cown_count(); - if (req_count > sizeof...(Args)) - { - is_req_extended = true; - req_extended = reinterpret_cast( - heap::alloc(req_count * (sizeof(Request)))); - } - } + using Be = typename Last<_Args...>::type; + // Dispatch to the body of the behaviour. + BehaviourCore* b = BehaviourCore::from_work(work); + Be* body = b->template get_body(); + // After the body we store the lengths of any of the spans. + size_t* lengths = (size_t*)(body + 1); - When(When&& o) - : cown_tuple(std::move(o.cown_tuple)), - f(std::forward(o.f)), - is_req_extended(o.is_req_extended), - req_extended(o.req_extended) - { - o.req_extended = nullptr; - o.is_req_extended = false; - } + auto* slots = b->get_slots(); - When(const When&) = delete; + // Get the cown array from the slots. + auto cown_tuple = construct_access_tuple<_Args...>(slots, lengths); - ~When() - { - if (is_req_extended) + std::apply( + [&](auto&&... args) { (*body)(convert_to_acquired(args)...); }, + cown_tuple); + + if (rerun()) { - heap::dealloc(req_extended); + rerun() = false; + Scheduler::schedule(work); + return; } - } - }; - /** - * Class for staging the when creation. - * - * Do not call directly use `when` - * - * This provides an operator << to apply the closure. This allows the - * argument order to be more sensible, as variadic arguments have to be last. - * - * when (cown1, ..., cownn) << closure; - * - * Allows the variadic number of cowns to occur before the closure. - */ - template - class PreWhen - { - // Note only requires friend when Args2 == Args - // but C++ doesn't like this. - template - friend auto when(Args2&&... args); - - /** - * Internally uses AcquiredCown. The cown is only acquired after the - * behaviour is scheduled. - */ - std::tuple cown_tuple; + // Dealloc behaviour + body->~Be(); - PreWhen(Args... args) : cown_tuple(std::move(args)...) {} + BehaviourCore::finished(work); + } public: - template - auto operator<<(F&& f) + static bool& rerun() { - Scheduler::stats().behaviour(sizeof...(Args)); - - if constexpr (sizeof...(Args) == 0) - { - // Execute now atomic batch makes no sense. - verona::rt::schedule_lambda(std::forward(f)); - return Batch(std::make_tuple()); - } - else - { - return Batch( - std::make_tuple(When(std::forward(f), std::move(cown_tuple)))); - } + static thread_local bool rerun = false; + return rerun; } }; - /** - * Template deduction guide for Access. - */ - template - Access(const cown_ptr&) -> Access; - - /** - * Template deduction guide for Batch. - */ template - Batch(std::tuple) -> Batch; - - /** - * Implements a Verona-like `when` statement. - * - * Uses `<<` to apply the closure. - * - * This should really take a type of - * ((cown_ptr& | cown_ptr&& | cown_array& || - * cown_array&& )... To get the universal reference type to work, we - * can't place this constraint on it directly, as it needs to be on a type - * argument. - */ - template - auto when(Args&&... args) + Batch<1> when(Args&&... args) { - return PreWhen(convert_access(std::forward(args))...); + // Calculate the number of slots and spans required for the behaviour. + auto spec = When::calculate_spec(std::forward(args)...); + + // Extract behaviour type + using Be = typename When::Last::type; + + // These assertions are basically checking that we won't break any + // alignment assumptions on Be. If we add some actual alignment, then + // this can be improved. + static_assert( + alignof(Be) <= sizeof(void*), "Alignment not supported, yet!"); + + // The payload is the Be followed by the size of each of the spans. + auto behaviour_core = BehaviourCore::make( + spec.slot_count, + When::invoke, + sizeof(Be) + spec.span_count * sizeof(size_t)); + + // After the body we store the lengths of any of the spans. + auto body = behaviour_core->template get_body(); + size_t* lengths = (size_t*)(body + 1); + // Fill in slots and span sizes + When::initialise_slots( + behaviour_core->get_slots(), lengths, std::forward(args)...); + + new (body) + Be(std::forward(When::last(std::forward(args)...))); + + return {behaviour_core}; } - } // namespace verona::cpp diff --git a/src/rt/ds/morebits.h b/src/rt/ds/morebits.h index 538e4d0c..4fce9112 100644 --- a/src/rt/ds/morebits.h +++ b/src/rt/ds/morebits.h @@ -2,6 +2,8 @@ // SPDX-License-Identifier: MIT #pragma once +#include +#include #include namespace verona::rt diff --git a/src/rt/sched/cown.h b/src/rt/sched/cown.h index 9c59b306..a06e7e8f 100644 --- a/src/rt/sched/cown.h +++ b/src/rt/sched/cown.h @@ -64,7 +64,7 @@ namespace verona::rt public: // true means first reader is added, false otherwise - bool add_read(int readers = 1) + bool add_read(size_t readers = 1) { // Once a writer is waiting, no new readers can be added. assert(count % 2 == 0); diff --git a/test/func/atomic-sched/atomic-sched.cc b/test/func/atomic-sched/atomic-sched.cc index d6fa8a15..36f07a8d 100644 --- a/test/func/atomic-sched/atomic-sched.cc +++ b/test/func/atomic-sched/atomic-sched.cc @@ -15,23 +15,23 @@ class Body using namespace verona::cpp; -template -auto long_chain_helper(T obj) +template +auto long_chain_helper(T obj, F&& body) { if constexpr (r == true) { - return when(read(obj)); + return when(read(obj), std::forward(body)); } else { - return when(obj); + return when(obj, std::forward(body)); } } template auto make_var_chain(cown_ptr log, size_t n = 1) { - return (long_chain_helper(log) << [=](auto b) { + return long_chain_helper(log, [=](auto b) { for (int i = 0; i < 10; i++) { Logging::cout() << "Behaviour " << n << Logging::endl; @@ -44,15 +44,16 @@ auto make_var_chain(cown_ptr log, size_t n = 1) template auto make_var_chain(cown_ptr log, size_t n = 1) { - return (long_chain_helper(log) << - [=](auto b) { - for (int i = 0; i < 10; i++) - { - Logging::cout() << "Behaviour " << n << Logging::endl; - Systematic::yield(); - // sleep(1); - } - }) + + return (long_chain_helper( + log, + [=](auto b) { + for (int i = 0; i < 10; i++) + { + Logging::cout() << "Behaviour " << n << Logging::endl; + Systematic::yield(); + // sleep(1); + } + })) + (make_var_chain(log, n + 1)); } @@ -63,21 +64,22 @@ void test_body() auto log = make_cown(); auto log2 = make_cown(); - (when(log) << - [=](auto b) { - for (int i = 0; i < 10; i++) - { - Logging::cout() << "Behaviour 1\n"; - // sleep(1); - } - }) + - (when(log2) << [=](auto) { + (when( + log, + [=](auto b) { + for (int i = 0; i < 10; i++) + { + Logging::cout() << "Behaviour 1\n"; + // sleep(1); + } + })) + + (when(log2, [=](auto) { for (int i = 0; i < 10; i++) { Logging::cout() << "Behaviour 2\n"; // sleep(1); } - }); + })); } void test_body_read_mixed() @@ -87,21 +89,22 @@ void test_body_read_mixed() auto log = make_cown(); auto log2 = make_cown(); - (when(read(log)) << - [=](acquired_cown b) { - for (int i = 0; i < 10; i++) - { - Logging::cout() << "Behaviour 1\n"; - // sleep(1); - } - }) + - (when(log2) << [=](auto) { + (when( + read(log), + [=](acquired_cown b) { + for (int i = 0; i < 10; i++) + { + Logging::cout() << "Behaviour 1\n"; + // sleep(1); + } + })) + + (when(log2, [=](auto) { for (int i = 0; i < 10; i++) { Logging::cout() << "Behaviour 2\n"; // sleep(1); } - }); + })); } void test_body_smart() @@ -112,40 +115,41 @@ void test_body_smart() auto log2 = make_cown(); auto ptr = std::make_unique(42); - (when(log) << - [=, ptr = std::move(ptr)](auto b) { - std::cout << "ptr = " << *ptr << std::endl; - for (int i = 0; i < 10; i++) - { - Logging::cout() << "Behaviour 1\n"; - // sleep(1); - } - }) + - (when(log2) << [=](auto) { + (when( + log, + [=, ptr = std::move(ptr)](auto b) { + std::cout << "ptr = " << *ptr << std::endl; + for (int i = 0; i < 10; i++) + { + Logging::cout() << "Behaviour 1\n"; + // sleep(1); + } + })) + + (when(log2, [=](auto) { for (int i = 0; i < 10; i++) { Logging::cout() << "Behaviour 2\n"; // sleep(1); } - }); + })); } void test_body_concurrent_1() { auto log = make_cown(); - when() << [=]() { make_var_chain(log); }; + when([=]() { make_var_chain(log); }); - when() << [=]() { make_var_chain(log); }; + when([=]() { make_var_chain(log); }); } void test_body_concurrent_2() { auto log = make_cown(); - when() << [=]() { make_var_chain(log); }; + when([=]() { make_var_chain(log); }); - when() << [=]() { make_var_chain(log); }; + when([=]() { make_var_chain(log); }); } template @@ -157,21 +161,23 @@ void test_body_long_chain_var() } auto repeat_shape = [](auto c1, auto c2, auto c3) { - return (when(c1, c2) << - [=](auto b1, auto b2) { - for (int i = 0; i < 10; i++) - { - Logging::cout() << "Behaviour 1\n"; - // sleep(1); - } - }) + - (when(c3) << [=](auto b) { + return (when( + c1, + c2, + [=](auto b1, auto b2) { + for (int i = 0; i < 10; i++) + { + Logging::cout() << "Behaviour 1\n"; + // sleep(1); + } + })) + + (when(c3, [=](auto b) { for (int i = 0; i < 10; i++) { Logging::cout() << "Behaviour 2\n"; // sleep(1); } - }); + })); }; void test_body_repeat1() diff --git a/test/func/backpressure/deadlock.h b/test/func/backpressure/deadlock.h index 624ac402..cb536d31 100644 --- a/test/func/backpressure/deadlock.h +++ b/test/func/backpressure/deadlock.h @@ -59,10 +59,10 @@ namespace backpressure_deadlock auto c3 = make_cown(); for (size_t i = 0; i < 100; i++) - when(c1) << [](auto) {}; + when(c1, [](auto) {}); - when(c3) << [c1](auto) { when(c1) << [](auto) {}; }; - when(c2) << [c2, c3](auto) { when(c2, c3) << [](auto, auto) {}; }; - when(c1) << [c1, c2](auto) { when(c1, c2) << [](auto, auto) {}; }; + when(c3, [c1](auto) { when(c1, [](auto) {}); }); + when(c2, [c2, c3](auto) { when(c2, c3, [](auto, auto) {}); }); + when(c1, [c1, c2](auto) { when(c1, c2, [](auto, auto) {}); }); } } diff --git a/test/func/backpressure/unblock.h b/test/func/backpressure/unblock.h index 5da40896..56debabb 100644 --- a/test/func/backpressure/unblock.h +++ b/test/func/backpressure/unblock.h @@ -48,14 +48,14 @@ namespace backpressure_unblock void overload(cown_ptr sender, cown_ptr receiver) { - when() << [sender, receiver]() { + when([sender, receiver]() { size_t i = 100; while (i > 0) { i--; - when(sender) << [receiver](auto) { when(receiver) << [](auto) {}; }; + when(sender, [receiver](auto) { when(receiver, [](auto) {}); }); } - }; + }); } void test() @@ -67,6 +67,6 @@ namespace backpressure_unblock overload(sender1, receiver1); overload(sender2, receiver2); - when(sender1, receiver2) << [](auto, auto) {}; + when(sender1, receiver2, [](auto, auto) {}); } } diff --git a/test/func/cown_weak_ref/cown_weak_ref.cc b/test/func/cown_weak_ref/cown_weak_ref.cc index 8f60b176..8b6d1032 100644 --- a/test/func/cown_weak_ref/cown_weak_ref.cc +++ b/test/func/cown_weak_ref/cown_weak_ref.cc @@ -1,5 +1,6 @@ // Copyright Microsoft and Project Verona Contributors. // SPDX-License-Identifier: MIT +#include #include /** @@ -17,126 +18,82 @@ * messages to be received. **/ -struct MyCown : VCown +using namespace verona::cpp; + +struct MyCown { - MyCown* parent; // Weak + cown_ptr::weak parent; - MyCown* left; // Strong - MyCown* right; // Strong + cown_ptr left; + cown_ptr right; size_t up_count = 0; - void trace(ObjectStack& os) const + MyCown(cown_ptr::weak&& parent) : parent(parent) { - if (left != nullptr) - os.push(left); - if (right != nullptr) - os.push(right); - - // Do not push parent, as this is a weak reference. + Logging::cout() << "Creating " << this << std::endl; } + MyCown(const MyCown&) = default; + MyCown(MyCown&&) = default; + ~MyCown() { - if (parent != nullptr) - parent->weak_release(); - Logging::cout() << "Destroying " << this << " up_count " << up_count << std::endl; } }; -const char* spaces[] = { - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - "", -}; - -MyCown* make_tree(int n, MyCown* p) +cown_ptr make_tree(int n, cown_ptr::weak&& p) { if (n == 0) - return nullptr; + return {}; - auto c = new MyCown; + auto c = make_cown(std::move(p)); - Logging::cout() << "Cown " << spaces[n] << c << std::endl; + when(c, [n](acquired_cown c) { + c->left = make_tree(n - 1, c.cown()); + c->right = make_tree(n - 1, c.cown()); + Logging::cout() << "Creating " << c << " with n = " << n << std::endl; + }); - c->left = make_tree(n - 1, c); - c->right = make_tree(n - 1, c); - if (p != nullptr) - p->weak_acquire(); - c->parent = p; return c; } -struct Up +void up(acquired_cown& c) { - MyCown* m; - Up(MyCown* m) : m(m) {} - - static void weak_send(MyCown* m) - { - if (m->parent != nullptr) - { - if (m->parent->acquire_strong_from_weak()) - { - schedule_lambda(m->parent, Up(m->parent)); - } - } - } - - void operator()() - { - Logging::cout() << "Up on " << m << std::endl; - - m->up_count++; - - Up::weak_send(m); - } -}; + auto parent = c->parent.promote(); + if (parent == nullptr) + return; + + Logging::cout() << "Parent is alive" << std::endl; + when(std::move(parent), [](acquired_cown c) { + c->up_count++; + Logging::cout() << "Up on " << c << std::endl; + up(c); + }); +} -struct Down +void down(cown_ptr& c) { - MyCown* m; - Down(MyCown* m) : m(m) {} + if (c == nullptr) + return; - void operator()() - { - Logging::cout() << "Down on " << m << std::endl; + when(c, [](acquired_cown c) { + Logging::cout() << "Down on " << c << std::endl; - Up::weak_send(m); - - if (m->left != nullptr) - { - schedule_lambda(m->left, Down(m->left)); - } - - if (m->right != nullptr) - { - schedule_lambda(m->right, Down(m->right)); - } - } -}; + up(c); + down(c->left); + down(c->right); + }); +} void run_test() { - auto t = make_tree(9, nullptr); + auto t = make_tree(9, {}); - schedule_lambda(t, Down(t)); - schedule_lambda(t, Down(t)); + down(t); + down(t); } int main(int argc, char** argv) diff --git a/test/func/diningphilosophers/diningphil.cc b/test/func/diningphilosophers/diningphil.cc index e16b6155..6ca0d5b1 100644 --- a/test/func/diningphilosophers/diningphil.cc +++ b/test/func/diningphilosophers/diningphil.cc @@ -3,7 +3,10 @@ #include #include -struct Fork : public VCown +#include + +using namespace verona::cpp; +struct Fork { size_t id; size_t uses_expected; @@ -17,117 +20,33 @@ struct Fork : public VCown } }; -struct Ping -{ - void operator()() {} -}; - -/** - * This Message holds on to the only reference to a Cown, that it - * will "Ping" once it is delivered. This is used to find missing - * scans of messages. If a message is not scanned, the Cown c - * will be reclaimable, once this message is delivered it will be - * deallocated. - **/ -struct KeepAlive -{ - Cown* c; - - KeepAlive() - { - c = new Fork(999); - schedule_lambda(c, Ping()); - } - - void trace(ObjectStack& fields) const - { - fields.push(c); - } - - void operator()() - { - schedule_lambda(c, Ping()); - } -}; - -struct Philosopher : public VCown -{ - size_t id; - std::vector forks; - size_t to_eat; - - Philosopher(size_t id_, std::vector forks_, size_t to_eat_) - : id(id_), forks(forks_), to_eat(to_eat_) - {} - - void trace(ObjectStack& fields) const - { - for (auto f : forks) - { - fields.push(f); - } - } -}; - -void eat_send(Philosopher* p); - -struct Ponder -{ - Philosopher* p; - - Ponder(Philosopher* p) : p(p) {} - - void operator()() - { - Logging::cout() << "Philosopher " << p->id << " " << p << " pondering " - << p->to_eat << std::endl; - eat_send(p); - } -}; - -struct Eat -{ - Philosopher* eater; - - void operator()() - { - Logging::cout() << "Philosopher " << eater->id << " " << eater - << " eating (" << this << ")" << std::endl; - for (auto f : eater->forks) - { - ((Fork*)f)->uses++; - } - - schedule_lambda(eater, Ponder(eater)); - } - - Eat(Philosopher* p_) : eater(p_) - { - Logging::cout() << "Eat Message " << this << " for Philosopher " << p_->id - << " " << p_ << std::endl; - } - void trace(ObjectStack& fields) const - { - Logging::cout() << "Calling custom trace" << std::endl; - fields.push(eater); - } -}; - -void eat_send(Philosopher* p) +void eat(size_t id, std::vector> forks, size_t to_eat) { - if (p->to_eat == 0) + if (to_eat == 0) { - Logging::cout() << "Releasing Philosopher " << p->id << " " << p - << std::endl; - Cown::release(p); + Logging::cout() << "Releasing Philosopher " << id << std::endl; return; } - - p->to_eat--; - schedule_lambda(p->forks.size(), p->forks.data(), Eat(p)); - - schedule_lambda(p->forks[0], KeepAlive()); + // Subtle lifetime management here. `forks.data()` is used after + // the std::move in the when. This is safe as the vector won't reallocate + // the underlying array inside the when, but it is almost certainly + // UB. + cown_array forkspan(forks.data(), forks.size()); + when(forkspan, [id, forks = std::move(forks), to_eat](auto f) { + Logging::cout() << "Philosopher " << id << " eating " + << to_eat << std::endl; + for (size_t i = 0; i < f.length(); i++) + { + Logging::cout() << "Fork " << f[i].cown() << " " << f[i]->id << std::endl; + f[i]->uses++; + } + + eat(id, forks, to_eat - 1); + + // KeepAlive + when(forks[0], [](auto) {}); + }); } void test_dining( @@ -136,48 +55,29 @@ void test_dining( size_t fork_count, SystematicTestHarness* h) { - std::vector forks; + std::vector> forks; for (size_t i = 0; i < philosophers; i++) { - auto f = new Fork(i); - forks.push_back(f); - Logging::cout() << "Fork " << i << " " << f << std::endl; + forks.push_back(make_cown(i)); + Logging::cout() << "Fork " << i << " " << forks[i] << std::endl; } - verona::rt::Scramble scrambler; verona::rt::PRNG<> rand{h->current_seed()}; for (size_t i = 0; i < philosophers; i++) { - scrambler.setup(rand); - - std::vector my_forks; - - std::sort(forks.begin(), forks.end(), [&scrambler](Fork*& a, Fork*& b) { - return scrambler.perm(((Cown*)a)->id()) < - scrambler.perm(((Cown*)b)->id()); - }); + std::vector> my_forks; for (size_t j = 0; j < fork_count; j++) { - forks[j]->uses_expected += hunger; - Cown::acquire(forks[j]); - my_forks.push_back(forks[j]); + size_t fork_idx = rand.next64() % philosophers; + my_forks.push_back(forks[fork_idx]); + when (forks[fork_idx], [=](auto f) { + f->uses_expected += hunger; + }); } - - auto p = new Philosopher(i, my_forks, hunger); - schedule_lambda(p, Ponder(p)); - Logging::cout() << "Philosopher " << i << " " << p << std::endl; - for (size_t j = 0; j < fork_count; j++) - { - Logging::cout() << " Fork " << ((Fork*)my_forks[j])->id << " " - << my_forks[j] << std::endl; - } - } - - for (size_t i = 0; i < philosophers; i++) - { - Cown::release(forks[i]); + + eat(i, std::move(my_forks), hunger); } } diff --git a/test/func/dynamic-cownset-readonly/dynamic-cownset-readonly.cc b/test/func/dynamic-cownset-readonly/dynamic-cownset-readonly.cc index e7f3bf07..fc405f95 100644 --- a/test/func/dynamic-cownset-readonly/dynamic-cownset-readonly.cc +++ b/test/func/dynamic-cownset-readonly/dynamic-cownset-readonly.cc @@ -44,7 +44,7 @@ void test_span() cown_array t1{carray, 2}; - when(read(t1)) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(read(t1), [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_span_empty() @@ -53,7 +53,7 @@ void test_span_empty() cown_array t1{nullptr, 0}; - when(read(t1)) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(read(t1), [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_span_single() @@ -64,7 +64,7 @@ void test_span_single() cown_array t1{&log1, 1}; - when(read(t1)) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(read(t1), [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_multi_span() @@ -89,8 +89,9 @@ void test_multi_span() cown_array t2{carray2, 2}; - when(read(t1), read(t2)) << - [=](auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when(read(t1), read(t2), [=](auto, auto) { + Logging::cout() << "log" << Logging::endl; + }); } void test_mixed1() @@ -108,8 +109,9 @@ void test_mixed1() auto log3 = make_cown(1); - when(read(t1), log3) << - [=](auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when(read(t1), log3, [=](auto, auto) { + Logging::cout() << "log" << Logging::endl; + }); } void test_mixed2() @@ -127,8 +129,9 @@ void test_mixed2() auto log3 = make_cown(1); - when(read(log3), t1) << - [=](auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when(read(log3), t1, [=](auto, auto) { + Logging::cout() << "log" << Logging::endl; + }); } void test_mixed3() @@ -155,8 +158,9 @@ void test_mixed3() auto log5 = make_cown(4); - when(read(t1), log5, read(t2)) - << [=](auto, auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when(read(t1), log5, read(t2), [=](auto, auto, auto) { + Logging::cout() << "log" << Logging::endl; + }); } void test_mixed4() @@ -175,8 +179,9 @@ void test_mixed4() auto log3 = make_cown(3); auto log4 = make_cown(4); - when(read(log3), t1, read(log4)) - << [=](auto, auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when(read(log3), t1, read(log4), [=](auto, auto, auto) { + Logging::cout() << "log" << Logging::endl; + }); } void test_multi() @@ -192,9 +197,9 @@ void test_multi() cown_array t1{carray, 2}; - (when(read(t1)) << [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(read(log1)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when(read(t1), [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when( + read(log1), [=](auto) { Logging::cout() << "log" << Logging::endl; })); } void test_nest1() @@ -210,9 +215,9 @@ void test_nest1() cown_array t1{carray, 2}; - when(read(t1)) << [=](auto) { - when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; - }; + when(read(t1), [=](auto) { + when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); + }); } void test_nest2() @@ -228,9 +233,9 @@ void test_nest2() cown_array t1(carray, 2); - when(log1) << [=](auto) { - when(read(t1)) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; - }; + when(log1, [=](auto) { + when(read(t1), [=](auto) { Logging::cout() << "log" << Logging::endl; }); + }); } void test_move() @@ -246,8 +251,9 @@ void test_move() cown_array t1{carray, 2}; - when(std::move(read(t1))) - << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(std::move(read(t1)), [=](auto) { + Logging::cout() << "log" << Logging::endl; + }); } void test_repeated_cown() @@ -262,8 +268,9 @@ void test_repeated_cown() cown_array t1{carray, 2}; - when(std::move(read(t1))) - << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(std::move(read(t1)), [=](auto) { + Logging::cout() << "log" << Logging::endl; + }); } int main(int argc, char** argv) diff --git a/test/func/dynamic-cownset/dynamic-cownset.cc b/test/func/dynamic-cownset/dynamic-cownset.cc index c8d78f25..946a8f4f 100644 --- a/test/func/dynamic-cownset/dynamic-cownset.cc +++ b/test/func/dynamic-cownset/dynamic-cownset.cc @@ -44,7 +44,7 @@ void test_span() cown_array t1{carray, 2}; - when(t1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(t1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_span_empty() @@ -53,7 +53,7 @@ void test_span_empty() cown_array t1{nullptr, 0}; - when(t1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(t1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_span_single() @@ -64,7 +64,7 @@ void test_span_single() cown_array t1{&log1, 1}; - when(t1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(t1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_multi_span() @@ -89,8 +89,7 @@ void test_multi_span() cown_array t2{carray2, 2}; - when(t1, t2) << - [=](auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when(t1, t2, [=](auto, auto) { Logging::cout() << "log" << Logging::endl; }); } void test_mixed1() @@ -108,9 +107,9 @@ void test_mixed1() auto log3 = make_cown(1); - when(t1, log3) << [=](acquired_cown_span ca, acquired_cown a) { + when(t1, log3, [=](acquired_cown_span ca, acquired_cown a) { Logging::cout() << "log" << Logging::endl; - }; + }); } void test_mixed2() @@ -128,9 +127,9 @@ void test_mixed2() auto log3 = make_cown(1); - when(log3, t1) << [=](acquired_cown, acquired_cown_span ca) { + when(log3, t1, [=](acquired_cown, acquired_cown_span ca) { Logging::cout() << "log" << Logging::endl; - }; + }); } void test_mixed3() @@ -157,8 +156,9 @@ void test_mixed3() auto log5 = make_cown(4); - when(t1, log5, t2) << - [=](auto, auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when(t1, log5, t2, [=](auto, auto, auto) { + Logging::cout() << "log" << Logging::endl; + }); } void test_mixed4() @@ -177,8 +177,9 @@ void test_mixed4() auto log3 = make_cown(3); auto log4 = make_cown(4); - when(log3, t1, log4) << - [=](auto, auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when(log3, t1, log4, [=](auto, auto, auto) { + Logging::cout() << "log" << Logging::endl; + }); } void test_multi() @@ -194,8 +195,8 @@ void test_multi() cown_array t1{carray, 2}; - (when(t1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when(t1, [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; })); } void test_nest1() @@ -211,9 +212,9 @@ void test_nest1() cown_array t1{carray, 2}; - when(t1) << [=](auto) { - when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; - }; + when(t1, [=](auto) { + when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); + }); } void test_nest2() @@ -229,9 +230,9 @@ void test_nest2() cown_array t1(carray, 2); - when(log1) << [=](auto) { - when(t1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; - }; + when(log1, [=](auto) { + when(t1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); + }); } void test_move() @@ -247,8 +248,7 @@ void test_move() cown_array t1{carray, 2}; - when(std::move(t1)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(std::move(t1), [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_repeated_cown() @@ -263,8 +263,7 @@ void test_repeated_cown() cown_array t1{carray, 2}; - when(std::move(t1)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(std::move(t1), [=](auto) { Logging::cout() << "log" << Logging::endl; }); } int main(int argc, char** argv) diff --git a/test/func/early_release/early_release.cc b/test/func/early_release/early_release.cc index 1b9a86cd..9e4c2656 100644 --- a/test/func/early_release/early_release.cc +++ b/test/func/early_release/early_release.cc @@ -51,12 +51,14 @@ * 3 Early release: finish * --------------------------- */ +#include #include +using namespace verona::cpp; -struct A : public VCown +struct A {}; -struct B : public VCown +struct B {}; std::atomic flag = false; @@ -85,15 +87,11 @@ void interleave() void early_release_test(bool first, bool second) { - Cown* cowns[2]; Logging::cout() << "Early release: begin" << Logging::endl; - auto* a = new A; - auto* b = new B; + auto a = make_cown(); + auto b = make_cown(); - cowns[0] = a; - cowns[1] = b; - - schedule_lambda(2, cowns, [=]() { + when(a, b, [=](auto, auto) { start(); // TODO readd early release @@ -108,17 +106,13 @@ void early_release_test(bool first, bool second) if (first) { - schedule_lambda(a, []() { interleave(); }); + when(std::move(a), [](auto) { interleave(); }); } - else - Cown::release(a); if (second) { - schedule_lambda(b, []() { interleave(); }); + when(std::move(b), [](auto) { interleave(); }); } - else - Cown::release(b); } int main(int argc, char** argv) diff --git a/test/func/external_threading/main.cc b/test/func/external_threading/main.cc index 21aed46e..b550d0fa 100644 --- a/test/func/external_threading/main.cc +++ b/test/func/external_threading/main.cc @@ -1,6 +1,7 @@ // Copyright Microsoft and Project Verona Contributors. // SPDX-License-Identifier: MIT #define VERONA_EXTERNAL_THREADING +#include #include #include #include @@ -9,7 +10,7 @@ constexpr const char* HARNESS_ARGV[] = {"binary", "--cores", "1"}; void test_cown() { - schedule_lambda([] { Logging::cout() << "Executed" << Logging::endl; }); + verona::cpp::when([] { Logging::cout() << "Executed" << Logging::endl; }); } /** diff --git a/test/func/fair/fair.cc b/test/func/fair/fair.cc index d87395c2..5e892267 100644 --- a/test/func/fair/fair.cc +++ b/test/func/fair/fair.cc @@ -20,8 +20,8 @@ struct A void loop(cown_ptr c) { - when(c) << [c = std::move(c)](auto a) { - auto& count = a->count; + when(std::move(c), [](auto c) { + auto& count = c->count; if (count == 0) { @@ -29,18 +29,18 @@ void loop(cown_ptr c) } count--; - loop(std::move(c)); - }; + loop(c.cown()); + }); } void basic_test() { - when() << []() { + when([]() { for (int i = 0; i < 6; ++i) { loop(make_cown(i)); } - }; + }); } int main(int argc, char** argv) diff --git a/test/func/fair_variance/fair_variance.cc b/test/func/fair_variance/fair_variance.cc index 4c530fef..5c83af64 100644 --- a/test/func/fair_variance/fair_variance.cc +++ b/test/func/fair_variance/fair_variance.cc @@ -25,37 +25,37 @@ double elapsed_secs[n_cowns]; void loop(cown_ptr c) { - when(c) << [c = std::move(c)](auto a) { - auto& count = a->count; - auto id = a->id; + when(c, [](auto c) { + auto& count = c->count; + auto id = c->id; if (count == start_count) { - a->begin = clock(); + c->begin = clock(); } if (count == 0) { clock_t end = clock(); - double elapsed_second = double(end - a->begin) / CLOCKS_PER_SEC; + double elapsed_second = double(end - c->begin) / CLOCKS_PER_SEC; elapsed_secs[id] = elapsed_second; // printf("%d: %f\n", a->id, elapsed_second); return; } count--; - loop(std::move(c)); - }; + loop(c.cown()); + }); } void spawn() { - when() << []() { + when([]() { for (int i = 0; i < n_cowns; ++i) { loop(make_cown(i)); } - }; + }); } void assert_variance() diff --git a/test/func/multimessage/multimessage.cc b/test/func/multimessage/multimessage.cc index c3c7dd43..1325ad49 100644 --- a/test/func/multimessage/multimessage.cc +++ b/test/func/multimessage/multimessage.cc @@ -30,16 +30,16 @@ void test_multimessage(size_t cores) { auto a1 = make_cown(3); - when(a1) << [](auto a) { + when(a1, [](auto a) { Logging::cout() << "got message on " << a.cown() << Logging::endl; - }; + }); auto a2 = make_cown(5); // We are transfering our cown references to the message here. - when(a1, a2) << [](auto a, auto b) { + when(a1, a2, [](auto a, auto b) { Logging::cout() << "result = " << (a->i + b->i) << Logging::endl; - }; + }); } sched.run(); heap::debug_check_empty(); diff --git a/test/func/readonly/readonly.cc b/test/func/readonly/readonly.cc index 73f10626..0e2a97d4 100644 --- a/test/func/readonly/readonly.cc +++ b/test/func/readonly/readonly.cc @@ -21,29 +21,33 @@ void test_read_only() accounts.push_back(make_cown(0)); cown_ptr common_account = make_cown(100); - when(common_account) << - [](acquired_cown account) { account->balance -= 10; }; + when(common_account, [](acquired_cown account) { + account->balance -= 10; + }); for (size_t i = 0; i < num_accounts; i++) { - when(accounts[i], read(common_account)) - << []( - acquired_cown write_account, - acquired_cown ro_account) { - write_account->balance = ro_account->balance; - }; + when( + accounts[i], + read(common_account), + []( + acquired_cown write_account, + acquired_cown ro_account) { + write_account->balance = ro_account->balance; + }); - when(read(accounts[i])) << [](acquired_cown account) { + when(read(accounts[i]), [](acquired_cown account) { check(account->balance == 90); - }; + }); } - when(common_account) << - [](acquired_cown account) { account->balance += 10; }; + when(common_account, [](acquired_cown account) { + account->balance += 10; + }); - when(read(common_account)) << [](acquired_cown account) { + when(read(common_account), [](acquired_cown account) { check(account->balance == 100); - }; + }); } void test_read_only_fast_send() @@ -52,24 +56,30 @@ void test_read_only_fast_send() cown_ptr account_one = make_cown(100); cown_ptr account_two = make_cown(100); - when(read(account_one), read(account_two)) - << []( - acquired_cown account_one, - acquired_cown account_two) { - check(account_one->balance == account_two->balance); - }; + when( + read(account_one), + read(account_two), + []( + acquired_cown account_one, + acquired_cown account_two) { + check(account_one->balance == account_two->balance); + }); - when(read(account_one), read(account_two)) - << []( - acquired_cown account_one, - acquired_cown account_two) { - check(account_one->balance == account_two->balance); - }; + when( + read(account_one), + read(account_two), + []( + acquired_cown account_one, + acquired_cown account_two) { + check(account_one->balance == account_two->balance); + }); - when(account_one, account_two) << + when( + account_one, + account_two, [](acquired_cown account_one, acquired_cown account_two) { check(account_one->balance == account_two->balance); - }; + }); } int main(int argc, char** argv) diff --git a/test/func/run-at-termination/run-at-termination.cc b/test/func/run-at-termination/run-at-termination.cc index 44c4caa1..bef54e0a 100644 --- a/test/func/run-at-termination/run-at-termination.cc +++ b/test/func/run-at-termination/run-at-termination.cc @@ -29,10 +29,10 @@ void test_body() // Run NUM_OPS behaviours, each incrementing ops by NUM_OPS. for (int i = 0; i < NUM_OPS; i++) { - when() << []() { + when([]() { for (int i = 0; i < NUM_OPS; i++) ops++; - }; + }); } } diff --git a/test/func/runtimepause/runtimepause.cc b/test/func/runtimepause/runtimepause.cc index 1d5a8f0e..64904575 100644 --- a/test/func/runtimepause/runtimepause.cc +++ b/test/func/runtimepause/runtimepause.cc @@ -1,20 +1,22 @@ // Copyright Microsoft and Project Verona Contributors. // SPDX-License-Identifier: MIT +#include #include #include #include #include using namespace snmalloc; +using namespace verona::cpp; using namespace verona::rt; -struct A : public VCown +struct A {}; void test_runtime_pause(SystematicTestHarness* harness, size_t pauses) { - schedule_lambda([harness, pauses]() { - auto a = new A; + when([harness, pauses]() { + auto a = make_cown(); Scheduler::add_external_event_source(); auto pauses_ = pauses; harness->external_thread([pauses_, a]() mutable { @@ -27,13 +29,16 @@ void test_runtime_pause(SystematicTestHarness* harness, size_t pauses) auto pause_time = std::chrono::milliseconds(dist(rng)); std::this_thread::sleep_for(pause_time); Logging::cout() << "Scheduling Message" << Logging::endl; - schedule_lambda(a, [i]() { + when(a, [i](auto) { Logging::cout() << "running message " << i << std::endl; }); } - schedule_lambda(a, [a]() { Cown::release(a); }); - schedule_lambda([]() { + // We need to clear out reference count, before we + // `remove_external_event_source`s. + a.clear(); + + when([]() { Logging::cout() << "Remove external event source" << std::endl; Scheduler::remove_external_event_source(); }); diff --git a/test/func/simp1/simp1.cc b/test/func/simp1/simp1.cc index dae5a7b6..d711acf5 100644 --- a/test/func/simp1/simp1.cc +++ b/test/func/simp1/simp1.cc @@ -21,7 +21,7 @@ void test_body() auto log = make_cown(); - when(log) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(log, [=](auto) { Logging::cout() << "log" << Logging::endl; }); } int main(int argc, char** argv) diff --git a/test/func/simp2/simp2.cc b/test/func/simp2/simp2.cc index e396a0b8..843fabae 100644 --- a/test/func/simp2/simp2.cc +++ b/test/func/simp2/simp2.cc @@ -22,8 +22,8 @@ void test_body() auto log1 = make_cown(); auto log2 = make_cown(); - when(log1, log2) << - [=](auto, auto) { Logging::cout() << "log" << Logging::endl; }; + when( + log1, log2, [=](auto, auto) { Logging::cout() << "log" << Logging::endl; }); } int main(int argc, char** argv) diff --git a/test/func/simp3/simp3.cc b/test/func/simp3/simp3.cc index 54602152..4b78774c 100644 --- a/test/func/simp3/simp3.cc +++ b/test/func/simp3/simp3.cc @@ -21,8 +21,8 @@ void test_body() auto log1 = make_cown(); - when(log1) << [](auto) { Logging::cout() << "log" << Logging::endl; }; - when(log1) << [](auto) { Logging::cout() << "log" << Logging::endl; }; + when(log1, [](auto) { Logging::cout() << "log" << Logging::endl; }); + when(log1, [](auto) { Logging::cout() << "log" << Logging::endl; }); } int main(int argc, char** argv) diff --git a/test/func/simp4/simp4.cc b/test/func/simp4/simp4.cc index 3ca63905..0352ac03 100644 --- a/test/func/simp4/simp4.cc +++ b/test/func/simp4/simp4.cc @@ -21,10 +21,10 @@ void test_body() auto log1 = make_cown(); - when(log1) << [](auto l) { + when(log1, [](auto l) { Logging::cout() << "log" << Logging::endl; - when(l.cown()) << [](auto) { Logging::cout() << "log" << Logging::endl; }; - }; + when(l.cown(), [](auto) { Logging::cout() << "log" << Logging::endl; }); + }); } int main(int argc, char** argv) diff --git a/test/func/simp5/simp5.cc b/test/func/simp5/simp5.cc index b6311a95..f18eb092 100644 --- a/test/func/simp5/simp5.cc +++ b/test/func/simp5/simp5.cc @@ -23,12 +23,15 @@ void test_body() auto log2 = make_cown(); auto log3 = make_cown(); - when(log1, log2) << - [=](auto, auto) { Logging::cout() << "log1" << Logging::endl; }; - when(log2, log3) << - [=](auto, auto) { Logging::cout() << "log2" << Logging::endl; }; - when(log1, log3) << - [=](auto, auto) { Logging::cout() << "log3" << Logging::endl; }; + when(log1, log2, [=](auto, auto) { + Logging::cout() << "log1" << Logging::endl; + }); + when(log2, log3, [=](auto, auto) { + Logging::cout() << "log2" << Logging::endl; + }); + when(log1, log3, [=](auto, auto) { + Logging::cout() << "log3" << Logging::endl; + }); } int main(int argc, char** argv) diff --git a/test/func/simp6/simp6.cc b/test/func/simp6/simp6.cc index a0bf6e97..3e0ca2b6 100644 --- a/test/func/simp6/simp6.cc +++ b/test/func/simp6/simp6.cc @@ -24,14 +24,18 @@ void test_body() auto log3 = make_cown(); auto log4 = make_cown(); - when(log1, log2) << - [=](auto, auto) { Logging::cout() << "log1" << Logging::endl; }; - when(log3, log4) << - [=](auto, auto) { Logging::cout() << "log2" << Logging::endl; }; - when(log2, log3) << - [=](auto, auto) { Logging::cout() << "log3" << Logging::endl; }; - when(log4, log1) << - [=](auto, auto) { Logging::cout() << "log4" << Logging::endl; }; + when(log1, log2, [=](auto, auto) { + Logging::cout() << "log1" << Logging::endl; + }); + when(log3, log4, [=](auto, auto) { + Logging::cout() << "log2" << Logging::endl; + }); + when(log2, log3, [=](auto, auto) { + Logging::cout() << "log3" << Logging::endl; + }); + when(log4, log1, [=](auto, auto) { + Logging::cout() << "log4" << Logging::endl; + }); } int main(int argc, char** argv) diff --git a/test/func/simp_read/simp_read.cc b/test/func/simp_read/simp_read.cc index 074fe666..794a5572 100644 --- a/test/func/simp_read/simp_read.cc +++ b/test/func/simp_read/simp_read.cc @@ -47,26 +47,26 @@ using namespace verona::cpp; void create_writer(cown_ptr c, size_t i) { - when() << [i, c]() { - when(c) << [=](auto) { + when([i, c]() { + when(c, [=](auto) { add_writer(); Logging::cout() << "write " << i << Logging::endl; Systematic::yield(); remove_writer(); - }; - }; + }); + }); } void create_reader(cown_ptr c, size_t i) { - when() << [i, c]() { - when(read(c)) << [=](auto) { + when([i, c]() { + when(read(c), [=](auto) { add_reader(); Logging::cout() << "read " << i << Logging::endl; Systematic::yield(); remove_reader(); - }; - }; + }); + }); } void test_body(size_t n) diff --git a/test/func/simp_weak1/simp_weak1.cc b/test/func/simp_weak1/simp_weak1.cc index a2c311a2..9d4d07bc 100644 --- a/test/func/simp_weak1/simp_weak1.cc +++ b/test/func/simp_weak1/simp_weak1.cc @@ -27,11 +27,11 @@ void test_body() auto log = make_cown(); - when(log) << [=](auto log) { + when(log, [=](auto log) { // Create a self reference, this should not prevent the body from // being destroyed. log->self = log.cown(); - }; + }); } int main(int argc, char** argv) diff --git a/test/func/simp_weak2/simp_weak2.cc b/test/func/simp_weak2/simp_weak2.cc index 5de78769..580e9932 100644 --- a/test/func/simp_weak2/simp_weak2.cc +++ b/test/func/simp_weak2/simp_weak2.cc @@ -41,29 +41,29 @@ void test_body() auto subject = make_cown(); auto observer = make_cown(); - when(subject) << + when( + subject, [observer = observer.get_weak()](acquired_cown subject) mutable { subject->observer = observer; - }; + }); - when(observer) << - [subject = subject](acquired_cown observer) mutable { - observer->subject = subject; - }; + when(observer, [subject = subject](acquired_cown observer) mutable { + observer->subject = subject; + }); - when(subject) << [](acquired_cown subject) mutable { + when(subject, [](acquired_cown subject) mutable { auto observer = subject->observer.promote(); if (observer) { - when(observer) << [](acquired_cown) { + when(observer, [](acquired_cown) { Logging::cout() << "Observer is alive" << Logging::endl; - }; + }); } else { Logging::cout() << "Observer is dead" << Logging::endl; } - }; + }); } int main(int argc, char** argv) diff --git a/test/func/simp_weak3/simp_weak3.cc b/test/func/simp_weak3/simp_weak3.cc index 73567556..60db93f2 100644 --- a/test/func/simp_weak3/simp_weak3.cc +++ b/test/func/simp_weak3/simp_weak3.cc @@ -23,7 +23,7 @@ class Tree // Build a balanced binary tree of a given depth void build(cown_ptr& curr, cown_ptr::weak parent, size_t depth) { - when(curr) << [parent = std::move(parent), depth](acquired_cown curr) { + when(curr, [parent = std::move(parent), depth](acquired_cown curr) { curr->parent = std::move(parent); if (depth > 0) { @@ -32,33 +32,33 @@ void build(cown_ptr& curr, cown_ptr::weak parent, size_t depth) build(curr->left, curr.cown().get_weak(), depth - 1); build(curr->right, curr.cown().get_weak(), depth - 1); } - }; + }); } // Walk up tree using parent pointers where they haven't been collected. void up(cown_ptr& curr) { - when(curr) << [](acquired_cown curr) { + when(curr, [](acquired_cown curr) { auto parent = curr->parent.promote(); if (parent) { Logging::cout() << "Parent is alive" << Logging::endl; up(parent); } - }; + }); } // Recursively decent the tree using strong references, and // perform up for each node. void down(cown_ptr& curr) { - when(curr) << [](acquired_cown curr) { + when(curr, [](acquired_cown curr) { Logging::cout() << "Node is alive" << Logging::endl; if (curr->left) down(curr->left); if (curr->right) down(curr->right); - }; + }); up(curr); } diff --git a/test/func/steal/steal.cc b/test/func/steal/steal.cc index c406034a..88714ec3 100644 --- a/test/func/steal/steal.cc +++ b/test/func/steal/steal.cc @@ -17,7 +17,7 @@ void schedule_run(size_t decay) return; auto runner = make_cown(); - when(runner) << [decay](auto) { schedule_run(decay - 1); }; + when(runner, [decay](auto) { schedule_run(decay - 1); }); } void basic_test(size_t cores) diff --git a/test/func/weak_leak_bug/weak_leak_bug.cc b/test/func/weak_leak_bug/weak_leak_bug.cc index bb5e9914..0edd1e1c 100644 --- a/test/func/weak_leak_bug/weak_leak_bug.cc +++ b/test/func/weak_leak_bug/weak_leak_bug.cc @@ -31,8 +31,8 @@ void run_test() // HERE: the weak RC is never released. weak_leak = t.get_weak(); - when(t) << - [](auto t) { Logging::cout() << "Msg on " << t.cown() << std::endl; }; + when( + t, [](auto t) { Logging::cout() << "Msg on " << t.cown() << std::endl; }); } int main(int argc, char** argv) diff --git a/test/func/when-repeated-cown/repeat-cown.cc b/test/func/when-repeated-cown/repeat-cown.cc index 42794a5e..ed5600f1 100644 --- a/test/func/when-repeated-cown/repeat-cown.cc +++ b/test/func/when-repeated-cown/repeat-cown.cc @@ -13,15 +13,17 @@ void test_acquire_cown_twice() auto log = make_cown(2); auto alog = make_cown(3); - when(log) << [=](auto) { Logging::cout() << "first log" << Logging::endl; }; + when(log, [=](auto) { Logging::cout() << "first log" << Logging::endl; }); - when(log, log) << - [=](auto, auto) { Logging::cout() << "second log" << Logging::endl; }; + when(log, log, [=](auto, auto) { + Logging::cout() << "second log" << Logging::endl; + }); - when(log, alog, log) << - [=](auto, auto, auto) { Logging::cout() << "third log" << Logging::endl; }; + when(log, alog, log, [=](auto, auto, auto) { + Logging::cout() << "third log" << Logging::endl; + }); - when(log) << [=](auto) { Logging::cout() << "final log" << Logging::endl; }; + when(log, [=](auto) { Logging::cout() << "final log" << Logging::endl; }); } int main(int argc, char** argv) diff --git a/test/func/when-transfer/transfer.cc b/test/func/when-transfer/transfer.cc index 76c4b15d..c44d5f78 100644 --- a/test/func/when-transfer/transfer.cc +++ b/test/func/when-transfer/transfer.cc @@ -21,8 +21,8 @@ void test_body_move() auto log = make_cown(); - when(std::move(log)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when( + std::move(log), [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_body_move_busy() @@ -31,9 +31,9 @@ void test_body_move_busy() auto log = make_cown(); - when(log) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; - when(std::move(log)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }; + when(log, [=](auto) { Logging::cout() << "log" << Logging::endl; }); + when( + std::move(log), [=](auto) { Logging::cout() << "log" << Logging::endl; }); } void test_sched_many_no_move() @@ -43,8 +43,8 @@ void test_sched_many_no_move() auto log1 = make_cown(); auto log2 = cown_ptr(log1); - (when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(log2) << [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when(log2, [=](auto) { Logging::cout() << "log" << Logging::endl; })); } void test_sched_many_no_move_busy() @@ -54,9 +54,9 @@ void test_sched_many_no_move_busy() auto log1 = make_cown(); auto log2 = cown_ptr(log1); - when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; - (when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(log2) << [=](auto) { Logging::cout() << "log" << Logging::endl; }); + when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when(log2, [=](auto) { Logging::cout() << "log" << Logging::endl; })); } void test_sched_many_move() @@ -66,10 +66,12 @@ void test_sched_many_move() auto log1 = make_cown(); auto log2 = make_cown(); - (when(std::move(log1)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(std::move(log2)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when( + std::move(log1), + [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when(std::move(log2), [=](auto) { + Logging::cout() << "log" << Logging::endl; + })); } void test_sched_many_move_busy() @@ -79,11 +81,13 @@ void test_sched_many_move_busy() auto log1 = make_cown(); auto log2 = make_cown(); - when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; - (when(std::move(log1)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(std::move(log2)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }); + when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when( + std::move(log1), + [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when(std::move(log2), [=](auto) { + Logging::cout() << "log" << Logging::endl; + })); } void test_sched_many_mixed() @@ -93,9 +97,10 @@ void test_sched_many_mixed() auto log1 = make_cown(); auto log2 = make_cown(); - (when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(std::move(log2)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when(std::move(log2), [=](auto) { + Logging::cout() << "log" << Logging::endl; + })); } void test_sched_many_mixed_busy() @@ -105,10 +110,11 @@ void test_sched_many_mixed_busy() auto log1 = make_cown(); auto log2 = make_cown(); - when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }; - (when(log1) << [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(std::move(log2)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }); + when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when(log1, [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when(std::move(log2), [=](auto) { + Logging::cout() << "log" << Logging::endl; + })); } void test_sched_many_move_same() @@ -118,10 +124,12 @@ void test_sched_many_move_same() auto log1 = make_cown(); auto log2 = cown_ptr(log1); - (when(std::move(log1)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }) + - (when(std::move(log2)) << - [=](auto) { Logging::cout() << "log" << Logging::endl; }); + (when( + std::move(log1), + [=](auto) { Logging::cout() << "log" << Logging::endl; })) + + (when(std::move(log2), [=](auto) { + Logging::cout() << "log" << Logging::endl; + })); } int main(int argc, char** argv) diff --git a/test/func/yield/yield.cc b/test/func/yield/yield.cc index bfd64c98..37b1b3e7 100644 --- a/test/func/yield/yield.cc +++ b/test/func/yield/yield.cc @@ -6,7 +6,7 @@ #define BEHAVIOUR_YIELD(X) \ { \ - verona::rt::Behaviour::behaviour_rerun() = true; \ + verona::cpp::When::rerun() = true; \ return X; \ } @@ -39,7 +39,7 @@ void test_state_machine() Logging::cout() << "Yield state machine test" << Logging::endl; auto state_cown = make_cown(); - when(state_cown) << [](auto state) { + when(state_cown, [](auto state) { switch (state->s) { case ObjectWithState::StateA: @@ -56,7 +56,7 @@ void test_state_machine() Logging::cout() << "In state C" << Logging::endl; break; } - }; + }); } void test_counter() @@ -65,7 +65,7 @@ void test_counter() auto counter_cown = make_cown(); - when(counter_cown) << [](auto counter) { + when(counter_cown, [](auto counter) { // Ensure that the next behaviour does not run assert(counter->c % 2 == 0); while (counter->c < 10) @@ -75,13 +75,13 @@ void test_counter() << Logging::endl; BEHAVIOUR_YIELD(); } - }; + }); - when(counter_cown) << [](auto counter) { + when(counter_cown, [](auto counter) { assert(counter->c == 10); Logging::cout() << "Incrementing counter by 1" << Logging::endl; counter->c++; - }; + }); } int main(int argc, char** argv) diff --git a/test/perf/backpressure1/backpressure1.cc b/test/perf/backpressure1/backpressure1.cc index 688497f8..a04c68de 100644 --- a/test/perf/backpressure1/backpressure1.cc +++ b/test/perf/backpressure1/backpressure1.cc @@ -152,10 +152,10 @@ int main(int argc, char** argv) proxy_chain.push_back(new Proxy(p)); auto e = make_cown(); - when(e) << [](auto) { + when(e, [](auto) { Logging::cout() << "Add external event source" << std::endl; Scheduler::add_external_event_source(); - }; + }); harness.external_thread([=]() { for (size_t i = 0; i < senders; i++) @@ -184,10 +184,10 @@ int main(int argc, char** argv) Cown::release(r); } - when(e) << [](auto) { + when(e, [](auto) { Logging::cout() << "Remove external event source" << std::endl; Scheduler::remove_external_event_source(); - }; + }); }); }); } diff --git a/test/perf/banking/verona_banks.cc b/test/perf/banking/verona_banks.cc index c628cbe0..1f9ebd8c 100644 --- a/test/perf/banking/verona_banks.cc +++ b/test/perf/banking/verona_banks.cc @@ -57,7 +57,7 @@ struct Log void log(cown_ptr> log, std::string) { - when(log) << [=](auto) { /*std::cout << msg << std::endl;*/ }; + when(log, [=](auto) { /*std::cout << msg << std::endl;*/ }); } void bank_job( @@ -76,32 +76,35 @@ void bank_job( // Schedule a transfer int64_t amount = 100; Accounts& accounts = *worker->accounts; - when(accounts[from_idx], accounts[to_idx]) - << [=](acquired_cown from, acquired_cown to) { - // Note that we could do - // (auto from, auto to) { - // We give explicit types for clarity. - busy_loop(WORK_USEC); - - if ((from->balance + from->overdraft) < amount) - { - log(l, "Insufficient funds"); - return; - } - else - { - from->balance -= amount; - to->balance += amount; - log(l, "Success"); - } - }; + when( + accounts[from_idx], + accounts[to_idx], + [=](acquired_cown from, acquired_cown to) { + // Note that we could do + // (auto from, auto to) { + // We give explicit types for clarity. + busy_loop(WORK_USEC); + + if ((from->balance + from->overdraft) < amount) + { + log(l, "Insufficient funds"); + return; + } + else + { + from->balance -= amount; + to->balance += amount; + log(l, "Success"); + } + }); if (repeats > 0) { // Reschedule bank_job // bank_job(worker, l, repeats - 1); - when(worker.cown()) << - [=](acquired_cown worker) { bank_job(worker, l, repeats - 1); }; + when(worker.cown(), [=](acquired_cown worker) { + bank_job(worker, l, repeats - 1); + }); } } @@ -125,9 +128,9 @@ void test_body() for (size_t j = 0; j < NUM_WORKERS; j++) { Logging::cout() << "Worker " << j << Logging::endl; - when(make_cown(accounts, j + 1)) << [=](acquired_cown w) { + when(make_cown(accounts, j + 1), [=](acquired_cown w) { bank_job(w, log, TRANSACTIONS / NUM_WORKERS); - }; + }); } } diff --git a/test/perf/dining_phil/verona_dining_phil.cc b/test/perf/dining_phil/verona_dining_phil.cc index 79f957e9..6988ea68 100644 --- a/test/perf/dining_phil/verona_dining_phil.cc +++ b/test/perf/dining_phil/verona_dining_phil.cc @@ -47,15 +47,17 @@ struct Philosopher { if (phil->hunger > 0) { - when(phil->left, phil->right) - << [phil = std::move(phil)]( - acquired_cown f1, acquired_cown f2) mutable { - f1->use(); - f2->use(); - busy_loop(WORK_USEC); - phil->hunger--; - eat(std::move(phil)); - }; + when( + phil->left, + phil->right, + [phil = std::move(phil)]( + acquired_cown f1, acquired_cown f2) mutable { + f1->use(); + f2->use(); + busy_loop(WORK_USEC); + phil->hunger--; + eat(std::move(phil)); + }); } } }; @@ -94,7 +96,7 @@ void test_body() void test1() { - when() << []() { test_body(); }; + when([]() { test_body(); }); } int verona_main(SystematicTestHarness& harness) diff --git a/test/perf/hashtable/hashtable.cc b/test/perf/hashtable/hashtable.cc index f76c2c6c..75a41671 100644 --- a/test/perf/hashtable/hashtable.cc +++ b/test/perf/hashtable/hashtable.cc @@ -129,54 +129,58 @@ void test_hash_table() write_buckets.size() > 0 ? write_buckets.data() : nullptr, write_buckets.size()}; - when(read(readers), writers) << [reader_idx, writer_idx]( - acquired_cown_span readers, - acquired_cown_span writers) { - Logging::cout() << "Num readers: " << readers.length - << " Num writers: " << writers.length << Logging::endl; -#ifdef DEBUG_RW - for (auto reader : reader_idx) - { - auto val = (*concurrency)[reader].fetch_add(2); - Logging::cout() << "Reader_idx " << reader << " rcount " << val + when( + read(readers), + writers, + [reader_idx, writer_idx]( + acquired_cown_span readers, + acquired_cown_span writers) { + Logging::cout() << "Num readers: " << readers.length() + << " Num writers: " << writers.length() << Logging::endl; - check(val % 2 == 0); - } - for (auto writer : writer_idx) - { - auto val = (*concurrency)[writer].fetch_add(1); - Logging::cout() << "Writer_idx " << writer << " rcount " << val - << Logging::endl; - check(val == 0); - } +#ifdef DEBUG_RW + for (auto reader : reader_idx) + { + auto val = (*concurrency)[reader].fetch_add(2); + Logging::cout() << "Reader_idx " << reader << " rcount " << val + << Logging::endl; + check(val % 2 == 0); + } + for (auto writer : writer_idx) + { + auto val = (*concurrency)[writer].fetch_add(1); + Logging::cout() << "Writer_idx " << writer << " rcount " << val + << Logging::endl; + check(val == 0); + } #endif - found_read_ops += readers.length; - found_write_ops += writers.length; - mixed_ops++; + found_read_ops += readers.length(); + found_write_ops += writers.length(); + mixed_ops++; - for (volatile int i = 0; i < read_loop_count * readers.length; i++) - Aal::pause(); - for (volatile int i = 0; i < write_loop_count * writers.length; i++) - Aal::pause(); + for (volatile int i = 0; i < read_loop_count * readers.length(); i++) + Aal::pause(); + for (volatile int i = 0; i < write_loop_count * writers.length(); i++) + Aal::pause(); #ifdef DEBUG_RW - for (auto reader : reader_idx) - { - auto val = (*concurrency)[reader].fetch_add(-2); - Logging::cout() << "Reader_idx " << reader << " rcount " << val - << Logging::endl; - check((val >= 2) && (val % 2 == 0)); - } - for (auto writer : writer_idx) - { - auto val = (*concurrency)[writer].fetch_add(-1); - Logging::cout() << "Writer_idx " << writer << " rcount " << val - << Logging::endl; - check(val == 1); - } + for (auto reader : reader_idx) + { + auto val = (*concurrency)[reader].fetch_add(-2); + Logging::cout() << "Reader_idx " << reader << " rcount " << val + << Logging::endl; + check((val >= 2) && (val % 2 == 0)); + } + for (auto writer : writer_idx) + { + auto val = (*concurrency)[writer].fetch_add(-1); + Logging::cout() << "Writer_idx " << writer << " rcount " << val + << Logging::endl; + check(val == 1); + } #endif - }; + }); } auto t2 = high_resolution_clock::now(); diff --git a/test/perf/smallbank/smallbank.cc b/test/perf/smallbank/smallbank.cc index 1b26639e..60ca9bba 100644 --- a/test/perf/smallbank/smallbank.cc +++ b/test/perf/smallbank/smallbank.cc @@ -48,12 +48,14 @@ void balance( // Return a promise auto pp = Promise::create_promise(); - when(account->second.checking, account->second.savings) << + when( + account->second.checking, + account->second.savings, [wp = std::move(pp.second)]( acquired_cown ch_acq, acquired_cown sa_acq) mutable { Promise::fulfill( std::move(wp), ch_acq->balance + sa_acq->balance); - }; + }); pp.first.then([](std::variant::PromiseErr> val) { if (!std::holds_alternative(val)) @@ -74,11 +76,11 @@ void deposit_checking( if (account == accounts.end()) return; - when(account->second.checking) << [=](acquired_cown ch_acq) { + when(account->second.checking, [=](acquired_cown ch_acq) { ch_acq->balance += amount; Logging::cout() << "Deposited " << amount << std::endl; - }; + }); } // TransactSavings: Add or remove from the savings account @@ -91,14 +93,14 @@ void transact_savings( if (account == accounts.end()) return; - when(account->second.savings) << [=](acquired_cown sa_acq) { + when(account->second.savings, [=](acquired_cown sa_acq) { if ((amount < 0) && (sa_acq->balance < (-1 * amount))) return; sa_acq->balance += amount; Logging::cout() << "TransactSavings: " << amount << " " << sa_acq->balance << std::endl; - }; + }); } // WriteCheck @@ -111,15 +113,17 @@ void write_check( if (account == accounts.end()) return; - when(account->second.savings, account->second.checking) - << [=](acquired_cown sa_acq, acquired_cown ch_acq) { - if (amount < (ch_acq->balance + sa_acq->balance)) - ch_acq->balance -= (amount + 1); - else - ch_acq->balance -= amount; - - Logging::cout() << "Write check: " << amount << std::endl; - }; + when( + account->second.savings, + account->second.checking, + [=](acquired_cown sa_acq, acquired_cown ch_acq) { + if (amount < (ch_acq->balance + sa_acq->balance)) + ch_acq->balance -= (amount + 1); + else + ch_acq->balance -= amount; + + Logging::cout() << "Write check: " << amount << std::endl; + }); } // Amalgamate: Move all funds from account 1 to the checking account 2 @@ -136,16 +140,16 @@ void amalgamate( when( account1->second.savings, account1->second.checking, - account2->second.checking) - << [=]( - acquired_cown sa_acq1, - acquired_cown ch_acq1, - acquired_cown ch_acq2) { - ch_acq2->balance += (sa_acq1->balance + ch_acq1->balance); - sa_acq1->balance = 0; - ch_acq1->balance = 0; - Logging::cout() << "Amalgamate" << std::endl; - }; + account2->second.checking, + [=]( + acquired_cown sa_acq1, + acquired_cown ch_acq1, + acquired_cown ch_acq2) { + ch_acq2->balance += (sa_acq1->balance + ch_acq1->balance); + sa_acq1->balance = 0; + ch_acq1->balance = 0; + Logging::cout() << "Amalgamate" << std::endl; + }); } struct Generator @@ -207,8 +211,9 @@ struct Generator // Reschedule if (g_acq->tx_count < PER_GEN_TX_COUNT) - when(g_acq.cown()) << - [](acquired_cown g_acq_new) { generate_tx(g_acq_new); }; + when(g_acq.cown(), [](acquired_cown g_acq_new) { + generate_tx(g_acq_new); + }); else g_acq->print_stats(); } @@ -231,9 +236,9 @@ void experiment_init() for (uint32_t i = 0; i < GENERATOR_COUNT; i++) { auto g = make_cown(accounts); - when(g) << [](acquired_cown g_acq_new) { + when(g, [](acquired_cown g_acq_new) { Generator::generate_tx(g_acq_new); - }; + }); } } diff --git a/test/perf/worksteal/worksteal.cc b/test/perf/worksteal/worksteal.cc index 13dd42e3..dd3da03a 100644 --- a/test/perf/worksteal/worksteal.cc +++ b/test/perf/worksteal/worksteal.cc @@ -53,7 +53,7 @@ void test() { auto sync = verona::cpp::make_cown(); - when(sync) << [](auto sync) { + when(sync, [](auto sync) { sync->start = high_resolution_clock::now(); sync->remaining_count = 1'000'000; @@ -61,13 +61,13 @@ void test() for (size_t i = 0; i < sync->remaining_count; i++) { // Schedule some work to be done - when() << []() {}; - when() << []() {}; - when() << []() {}; - when() << []() {}; + when([]() {}); + when([]() {}); + when([]() {}); + when([]() {}); // Every 5th work item will be counted back in for timing purposes - when() << [sync = sync.cown()]() { - when(sync) << [](auto sync) { + when([sync = sync.cown()]() { + when(sync, [](auto sync) { // Check if this is the last work item if (--sync->remaining_count == 0) { @@ -77,14 +77,14 @@ void test() duration_cast(sync->end - sync->start).count()); return; } - }; - }; + }); + }); } printf( "Scheduled all work took:\n\t%zu ms\n", duration_cast(high_resolution_clock::now() - sync->start) .count()); - }; + }); } int main(int argc, char** argv) From f6900e69a2390d500879a90ab9bcc47ddd203173 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 7 May 2025 09:58:52 +0100 Subject: [PATCH 2/4] Clangformat --- src/rt/cpp/cown_array.h | 5 +-- src/rt/cpp/when.h | 9 +++--- test/func/diningphilosophers/diningphil.cc | 36 ++++++++++------------ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/rt/cpp/cown_array.h b/src/rt/cpp/cown_array.h index e01fde13..13f5c5bb 100644 --- a/src/rt/cpp/cown_array.h +++ b/src/rt/cpp/cown_array.h @@ -95,11 +95,12 @@ namespace verona::cpp class cown_array : public cown_array { public: - cown_array(const cown_array& other) : cown_array(other){}; + cown_array(const cown_array& other) + : cown_array(other){}; }; template - cown_array read(cown_array cown) + cown_array read(cown_array cown) { Logging::cout() << "Read returning const array ptr" << Logging::endl; return cown; diff --git a/src/rt/cpp/when.h b/src/rt/cpp/when.h index f7455f01..c6e58c9e 100644 --- a/src/rt/cpp/when.h +++ b/src/rt/cpp/when.h @@ -71,8 +71,10 @@ namespace verona::cpp } public: - Batch(BehaviourCore* b) : when_batch({b}) { - Logging::cout() << "Batch created " << this << " contents " << b << Logging::endl; + Batch(BehaviourCore* b) : when_batch({b}) + { + Logging::cout() << "Batch created " << this << " contents " << b + << Logging::endl; if ((uintptr_t)b < 4096) abort(); } @@ -392,8 +394,7 @@ namespace verona::cpp When::initialise_slots( behaviour_core->get_slots(), lengths, std::forward(args)...); - new (body) - Be(std::forward(When::last(std::forward(args)...))); + new (body) Be(std::forward(When::last(std::forward(args)...))); return {behaviour_core}; } diff --git a/test/func/diningphilosophers/diningphil.cc b/test/func/diningphilosophers/diningphil.cc index 6ca0d5b1..acdfa95d 100644 --- a/test/func/diningphilosophers/diningphil.cc +++ b/test/func/diningphilosophers/diningphil.cc @@ -1,10 +1,9 @@ // Copyright Microsoft and Project Verona Contributors. // SPDX-License-Identifier: MIT +#include #include #include -#include - using namespace verona::cpp; struct Fork { @@ -20,7 +19,6 @@ struct Fork } }; - void eat(size_t id, std::vector> forks, size_t to_eat) { if (to_eat == 0) @@ -34,19 +32,19 @@ void eat(size_t id, std::vector> forks, size_t to_eat) // UB. cown_array forkspan(forks.data(), forks.size()); when(forkspan, [id, forks = std::move(forks), to_eat](auto f) { - Logging::cout() << "Philosopher " << id << " eating " - << to_eat << std::endl; - for (size_t i = 0; i < f.length(); i++) - { - Logging::cout() << "Fork " << f[i].cown() << " " << f[i]->id << std::endl; - f[i]->uses++; - } - - eat(id, forks, to_eat - 1); - - // KeepAlive - when(forks[0], [](auto) {}); - }); + Logging::cout() << "Philosopher " << id << " eating " << to_eat + << std::endl; + for (size_t i = 0; i < f.length(); i++) + { + Logging::cout() << "Fork " << f[i].cown() << " " << f[i]->id << std::endl; + f[i]->uses++; + } + + eat(id, forks, to_eat - 1); + + // KeepAlive + when(forks[0], [](auto) {}); + }); } void test_dining( @@ -72,11 +70,9 @@ void test_dining( { size_t fork_idx = rand.next64() % philosophers; my_forks.push_back(forks[fork_idx]); - when (forks[fork_idx], [=](auto f) { - f->uses_expected += hunger; - }); + when(forks[fork_idx], [=](auto f) { f->uses_expected += hunger; }); } - + eat(i, std::move(my_forks), hunger); } } From 87d126c4661ce7d4172bcf49229fdfb9d6a09f1b Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 7 May 2025 10:13:22 +0100 Subject: [PATCH 3/4] Remove UB --- test/func/diningphilosophers/diningphil.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/func/diningphilosophers/diningphil.cc b/test/func/diningphilosophers/diningphil.cc index acdfa95d..1781399d 100644 --- a/test/func/diningphilosophers/diningphil.cc +++ b/test/func/diningphilosophers/diningphil.cc @@ -26,12 +26,8 @@ void eat(size_t id, std::vector> forks, size_t to_eat) Logging::cout() << "Releasing Philosopher " << id << std::endl; return; } - // Subtle lifetime management here. `forks.data()` is used after - // the std::move in the when. This is safe as the vector won't reallocate - // the underlying array inside the when, but it is almost certainly - // UB. cown_array forkspan(forks.data(), forks.size()); - when(forkspan, [id, forks = std::move(forks), to_eat](auto f) { + when(forkspan, [id, forks, to_eat](auto f) { Logging::cout() << "Philosopher " << id << " eating " << to_eat << std::endl; for (size_t i = 0; i < f.length(); i++) From 075b7ad9f5bef37eda715f0b6b6cc9f8a2e87277 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 7 May 2025 13:05:49 +0100 Subject: [PATCH 4/4] Alter example to take local copies The staging of the move was occuring too early and making the cown null. I guess there was a subtle UB here. --- test/perf/dining_phil/verona_dining_phil.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/perf/dining_phil/verona_dining_phil.cc b/test/perf/dining_phil/verona_dining_phil.cc index 6988ea68..286c9dc0 100644 --- a/test/perf/dining_phil/verona_dining_phil.cc +++ b/test/perf/dining_phil/verona_dining_phil.cc @@ -47,9 +47,11 @@ struct Philosopher { if (phil->hunger > 0) { + auto l = phil->left; + auto r = phil->right; when( - phil->left, - phil->right, + l, + r, [phil = std::move(phil)]( acquired_cown f1, acquired_cown f2) mutable { f1->use();