Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yielding behaviours part2 #29

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/rt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ endif()

target_compile_definitions(verona_rt INTERFACE -DSNMALLOC_CHEAP_CHECKS)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)

warnings_high()
102 changes: 102 additions & 0 deletions src/rt/cpp/coro.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright Microsoft and Project Verona Contributors.
// SPDX-License-Identifier: MIT
#pragma once

#include <iostream>

#ifdef COROUTINES
# ifdef EXPERIMENTAL_CORO
# include <experimental/coroutine>
# else
# include <coroutine>
# endif
#endif

#include "../sched/behaviour.h"

namespace verona::cpp
{
#ifndef COROUTINES
struct coroutine
{};
#else

# ifdef EXPERIMENTAL_CORO
using namespace std::experimental;
# else
using namespace std;
# endif

struct coroutine
{
struct promise_type;
using handle_type = coroutine_handle<promise_type>;

struct promise_type
{
coroutine get_return_object()
{
return {handle_type::from_promise(*this)};
}
suspend_always initial_suspend() noexcept
{
return {};
}
suspend_always final_suspend() noexcept
{
return {};
}
void unhandled_exception() {}
void return_void() {}
};

handle_type h_;
bool initialized = false;

coroutine(handle_type h) : h_(h), initialized(true) {}
coroutine() : h_(nullptr), initialized(false) {}

void resume()
{
h_.resume();
}

bool done() const
{
return h_.done();
}

void destroy()
{
h_.destroy();
}
};

template<typename F>
auto prepare_coro_lambda(F&& f)
{
coroutine coro_state;

auto coro_f = [f = std::move(f),
coro_state = std::move(coro_state)](auto... args) mutable {
if (coro_state.initialized == false)
{
coro_state = std::move(f(args...));
}

coro_state.resume();

if (!(coro_state.done()))
{
verona::rt::Behaviour::behaviour_rerun() = true;
}
else
{
coro_state.destroy();
}
};

return coro_f;
}
#endif
} // namespace verona::cpp
78 changes: 65 additions & 13 deletions src/rt/cpp/when.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#pragma once

#include "../sched/behaviour.h"
#include "coro.h"
#include "cown.h"
#include "cown_array.h"

Expand Down Expand Up @@ -30,6 +31,7 @@ namespace verona::cpp
class Access
{
using Type = T;

ActualCown<std::remove_const_t<T>>* t;
bool is_move;

Expand Down Expand Up @@ -58,6 +60,7 @@ namespace verona::cpp
class AccessBatch
{
using Type = T;

ActualCown<std::remove_const_t<T>>** act_array;
acquired_cown<T>* acq_array;
size_t arr_len;
Expand Down Expand Up @@ -385,16 +388,17 @@ namespace verona::cpp

size_t count = array_assign(r);

/// Effectively converts ActualCown<T>... to
/// acquired_cown... .
auto lift_f = [f = std::move(f)](Args... args) mutable {
std::move(f)(access_to_acquired(args)...);
};

return std::make_tuple(
count,
r,
[f = std::move(f), cown_tuple = std::move(cown_tuple)]() mutable {
/// Effectively converts ActualCown<T>... to
/// acquired_cown... .
auto lift_f = [f = std::move(f)](Args... args) mutable {
std::move(f)(access_to_acquired<typename Args::Type>(args)...);
};

[lift_f = std::move(lift_f),
cown_tuple = std::move(cown_tuple)]() mutable {
std::apply(std::move(lift_f), std::move(cown_tuple));
});
}
Expand Down Expand Up @@ -466,22 +470,70 @@ namespace verona::cpp

PreWhen(Args... args) : cown_tuple(std::move(args)...) {}

template<typename T>
struct return_coroutine_t
: public return_coroutine_t<decltype(&T::operator())>
{};

template<typename ClassType, typename... Args2>
struct return_coroutine_t<coroutine (ClassType::*)(Args2...) const>
{
static constexpr bool value = true;
};

template<typename ClassType, typename ReturnType, typename... Args2>
struct return_coroutine_t<ReturnType (ClassType::*)(Args2...) const>
{
static constexpr bool value = false;
};

template<typename ClassType, typename... Args2>
struct return_coroutine_t<coroutine (ClassType::*)(Args2...)>
{
static constexpr bool value = true;
};

template<typename ClassType, typename ReturnType, typename... Args2>
struct return_coroutine_t<ReturnType (ClassType::*)(Args2...)>
{
static constexpr bool value = false;
};

public:
template<typename F>
auto operator<<(F&& f)
{
Scheduler::stats().behaviour(sizeof...(Args));

if constexpr (sizeof...(Args) == 0)
if constexpr (return_coroutine_t<F>::value)
{
// Execute now atomic batch makes no sense.
verona::rt::schedule_lambda(std::forward<F>(f));
return Batch(std::make_tuple());
auto coro_f = prepare_coro_lambda(f);

if constexpr (sizeof...(Args) == 0)
{
// Execute now atomic batch makes no sense.
verona::rt::schedule_lambda(std::forward<decltype(coro_f)>(coro_f));
return Batch(std::make_tuple());
}
else
{
return Batch(std::make_tuple(When(
std::forward<decltype(coro_f)>(coro_f), std::move(cown_tuple))));
}
}
else
{
return Batch(
std::make_tuple(When(std::forward<F>(f), std::move(cown_tuple))));
if constexpr (sizeof...(Args) == 0)
{
// Execute now atomic batch makes no sense.
verona::rt::schedule_lambda(std::forward<F>(f));
return Batch(std::make_tuple());
}
else
{
return Batch(
std::make_tuple(When(std::forward<F>(f), std::move(cown_tuple))));
}
}
}
};
Expand Down
1 change: 1 addition & 0 deletions src/rt/sched/behaviour.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace verona::rt
{
behaviour_rerun() = false;
Scheduler::schedule(work);
Scheduler::local()->return_next_work();
return;
}

Expand Down
1 change: 1 addition & 0 deletions src/rt/sched/schedulerthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ namespace verona::rt
friend class ThreadSync<SchedulerThread>;
LocalSync local_sync{};
#endif
friend class Behaviour;

Alloc* alloc = nullptr;
Core* victim = nullptr;
Expand Down
13 changes: 13 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ foreach(TEST_MODE "sys" "con")
target_compile_definitions(${TESTNAME} PRIVATE USE_FLIGHT_RECORDER)
endif ()
endif ()
if (${TEST} STREQUAL "coro")
target_compile_definitions(${TESTNAME} PRIVATE COROUTINES)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_definitions(${TESTNAME} PRIVATE EXPERIMENTAL_CORO)
target_compile_options(${TESTNAME} PRIVATE -stdlib=libc++ -fcoroutines-ts)
target_link_libraries(${TESTNAME} -lc++)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${TESTNAME} PRIVATE -fcoroutines)
elseif (MSVC)
target_compile_definitions(${TESTNAME} PRIVATE EXPERIMENTAL_CORO)
target_compile_options(${TESTNAME} PRIVATE /await)
endif()
endif ()
if (${TEST} STREQUAL "runtimepause")
# This example uses external non-determinism. So add FLIGHT_RECORDER for CI crashes
if (VERONA_CI_BUILD)
Expand Down
12 changes: 6 additions & 6 deletions test/func/atomic-sched/atomic-sched.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ void test_body()
auto log2 = make_cown<Body>();

(when(log) <<
[=](auto b) {
[=](acquired_cown<Body>) {
for (int i = 0; i < 10; i++)
{
Logging::cout() << "Behaviour 1\n";
// sleep(1);
}
}) +
(when(log2) << [=](auto) {
(when(log2) << [=](acquired_cown<Body>) {
for (int i = 0; i < 10; i++)
{
Logging::cout() << "Behaviour 2\n";
Expand All @@ -46,13 +46,13 @@ void test_body_same()
auto log = make_cown<Body>();

(when(log) <<
[=](auto b) {
[=](acquired_cown<Body> b) {
for (int i = 0; i < 10; i++)
{
Logging::cout() << "Behaviour 1" << Logging::endl;
}
}) +
(when(log) << [=](auto) {
(when(log) << [=](acquired_cown<Body>) {
for (int i = 0; i < 10; i++)
{
Logging::cout() << "Behaviour 2" << Logging::endl;
Expand All @@ -69,15 +69,15 @@ void test_body_smart()
auto ptr = std::make_unique<int>(42);

(when(log) <<
[=, ptr = std::move(ptr)](auto b) {
[=, ptr = std::move(ptr)](acquired_cown<Body> 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(log2) << [=](acquired_cown<Body>) {
for (int i = 0; i < 10; i++)
{
Logging::cout() << "Behaviour 2\n";
Expand Down
12 changes: 8 additions & 4 deletions test/func/backpressure/deadlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,14 @@ namespace backpressure_deadlock
auto c3 = make_cown<C>();

for (size_t i = 0; i < 100; i++)
when(c1) << [](auto) {};
when(c1) << [](acquired_cown<C>) {};

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](acquired_cown<C>) { when(c1) << [](acquired_cown<C>) {}; };
when(c2) << [c2, c3](acquired_cown<C>) {
when(c2, c3) << [](acquired_cown<C>, acquired_cown<C>) {};
};
when(c1) << [c1, c2](acquired_cown<C>) {
when(c1, c2) << [](acquired_cown<C>, acquired_cown<C>) {};
};
}
}
6 changes: 4 additions & 2 deletions test/func/backpressure/unblock.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ namespace backpressure_unblock
while (i > 0)
{
i--;
when(sender) << [receiver](auto) { when(receiver) << [](auto) {}; };
when(sender) << [receiver](acquired_cown<Body>) {
when(receiver) << [](acquired_cown<Body>) {};
};
}
};
}
Expand All @@ -67,6 +69,6 @@ namespace backpressure_unblock

overload(sender1, receiver1);
overload(sender2, receiver2);
when(sender1, receiver2) << [](auto, auto) {};
when(sender1, receiver2) << [](acquired_cown<Body>, acquired_cown<Body>) {};
}
}
Loading