diff --git a/src/rt/cpp/when.h b/src/rt/cpp/when.h index 82642e55..d4bf18f0 100644 --- a/src/rt/cpp/when.h +++ b/src/rt/cpp/when.h @@ -6,6 +6,7 @@ #include "cown.h" #include "cown_array.h" +#include #include #include #include @@ -140,7 +141,7 @@ namespace verona::cpp template auto convert_access(cown_ptr&& c) { - return Access(c); + return Access(std::move(c)); } template @@ -149,74 +150,117 @@ namespace verona::cpp return AccessBatch(c); } - template + class DynamicAtomicBatch; + + 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; - /// 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; - template - friend class Batch; + BehaviourCore* barray[bs]; - template - void create_behaviour(BehaviourCore** barray) + friend class DynamicAtomicBatch; + + public: + Batch() { - 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); - } + assert(0); } - public: - Batch(std::tuple args) : when_batch(std::move(args)) {} + Batch(BehaviourCore* b) + { + assert(0); + } Batch(const Batch&) = delete; + Batch(BehaviourCore** b1, BehaviourCore** b2, size_t s1, size_t s2) + { + std::memcpy(barray, b1, s1 * sizeof(BehaviourCore*)); + std::memcpy(&barray[s1], b2, s2 * sizeof(BehaviourCore*)); + } + ~Batch() { - if constexpr (sizeof...(Args) > 0) + if constexpr (bs > 0) { if (part_of_larger_batch) return; - BehaviourCore* barray[sizeof...(Args)]; - create_behaviour(barray); - - BehaviourCore::schedule_many(barray, sizeof...(Args)); + BehaviourCore::schedule_many(barray, bs); } } - 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(barray, wb.barray, bs, bs2); } }; + /** + * This class does not handle reference counting for cowns. + * Make sure the cown_ptrs will be available till scheduling happens + */ + class DynamicAtomicBatch + { + BehaviourCore** barray; + size_t size; + + public: + DynamicAtomicBatch() : barray(nullptr), size(0) {} + ~DynamicAtomicBatch() + { + if (size == 0) + return; + + BehaviourCore::schedule_many(barray, size); + delete barray; + } + + template + auto operator+(Batch&& b) + { + // Batch has a When, which holds a reference to a cown_ptr, need to keep + // this reference around! + b.part_of_larger_batch = true; + + auto new_array = new BehaviourCore*[size + bs]; + + if (barray) + { + std::memcpy(new_array, barray, size * sizeof(BehaviourCore*)); + delete barray; + } + + barray = new_array; + std::memcpy(&barray[size], b.barray, bs * sizeof(BehaviourCore*)); + + size += bs; + + return this; + } + + DynamicAtomicBatch(const DynamicAtomicBatch&) = delete; + DynamicAtomicBatch(DynamicAtomicBatch&&) = delete; + }; + + template<> + Batch<0>::Batch() + {} + + template<> + Batch<1>::Batch(BehaviourCore* b) + { + barray[0] = b; + } + /** * Represents a single when statement. * @@ -242,7 +286,7 @@ namespace verona::cpp struct is_batch> : std::true_type {}; - template + template friend class Batch; /// Set of cowns used by this behaviour. @@ -263,6 +307,8 @@ namespace verona::cpp Request* req_extended; bool is_req_extended; + BehaviourCore* behaviour_body; + /** * This uses template programming to turn the std::tuple into a C style * stack allocated array. @@ -372,11 +418,12 @@ namespace verona::cpp return acquired_cown(*c.t); } - auto to_tuple() + template + void create_behaviour_new() { - if constexpr (sizeof...(Args) == 0) + if constexpr (index >= sizeof...(Args)) { - return std::make_tuple(std::forward(f)); + return; } else { @@ -388,18 +435,19 @@ namespace verona::cpp 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)); - }); + auto closure = [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)); + }; + behaviour_body = Behaviour::prepare_to_schedule< + typename std::remove_reference::type>( + count, r, std::move(closure)); } } @@ -418,18 +466,12 @@ namespace verona::cpp req_extended = reinterpret_cast( heap::alloc(req_count * (sizeof(Request)))); } - } - 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; + std::cout << "Will create behaviour\n"; + create_behaviour_new(); } + When(When&&) = delete; When(const When&) = delete; ~When() @@ -439,6 +481,11 @@ namespace verona::cpp heap::dealloc(req_extended); } } + + BehaviourCore* get_behaviour_body() + { + return behaviour_body; + } }; /** @@ -479,12 +526,12 @@ namespace verona::cpp { // Execute now atomic batch makes no sense. verona::rt::schedule_lambda(std::forward(f)); - return Batch(std::make_tuple()); + return Batch<0>(); } else { - return Batch( - std::make_tuple(When(std::forward(f), std::move(cown_tuple)))); + return Batch<1>( + When(std::forward(f), std::move(cown_tuple)).get_behaviour_body()); } } }; @@ -495,12 +542,6 @@ namespace verona::cpp template Access(const cown_ptr&) -> Access; - /** - * Template deduction guide for Batch. - */ - template - Batch(std::tuple) -> Batch; - /** * Implements a Verona-like `when` statement. * diff --git a/test/func/dynamic-atomic-sched/dynamic-atomic-sched.cc b/test/func/dynamic-atomic-sched/dynamic-atomic-sched.cc new file mode 100644 index 00000000..1878053d --- /dev/null +++ b/test/func/dynamic-atomic-sched/dynamic-atomic-sched.cc @@ -0,0 +1,40 @@ +// Copyright Microsoft and Project Verona Contributors. +// SPDX-License-Identifier: MIT + +#include +#include + +class Body +{ +public: + ~Body() + { + Logging::cout() << "Body destroyed" << Logging::endl; + } +}; + +using namespace verona::cpp; + +void test_body() +{ + auto log = make_cown(); + + { + DynamicAtomicBatch dab; + for (int i = 0; i < 2; i++) + { + dab + (when(log) << [=](auto b) { + std::cout << "Behaviour " << i << std::endl; + }); + } + } +} + +int main(int argc, char** argv) +{ + SystematicTestHarness harness(argc, argv); + + harness.run(test_body); + + return 0; +}