forked from OSchip/llvm-project
[coroutines] Add end-to-end tests within libc++
This patch adds end-to-end/breathing tests for coroutines into libc++. The tests aren't specifically to test libc++ requirements but instead are intented to ensure coroutines are working fine in general. Although libc++ isn't exactly the most correct place for these tests to live, there is one major advantage. The libc++ test suite is also used by MSVC and by adding the tests here it ensures they will be run against all currently available coroutine implementations. llvm-svn: 304101
This commit is contained in:
parent
cf09175de8
commit
c34a497b40
|
@ -0,0 +1,72 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// UNSUPPORTED: c++98, c++03, c++11
|
||||||
|
// REQUIRES: fcoroutines-ts
|
||||||
|
|
||||||
|
// RUN: %build -fcoroutines-ts
|
||||||
|
// RUN: %run
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace std::experimental;
|
||||||
|
|
||||||
|
struct coro_t {
|
||||||
|
struct promise_type {
|
||||||
|
coro_t get_return_object() {
|
||||||
|
coroutine_handle<promise_type>{};
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
suspend_never initial_suspend() { return {}; }
|
||||||
|
suspend_never final_suspend() { return {}; }
|
||||||
|
void return_void(){}
|
||||||
|
static void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
~B() {}
|
||||||
|
bool await_ready() { return true; }
|
||||||
|
B await_resume() { return {}; }
|
||||||
|
template <typename F> void await_suspend(F) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct A {
|
||||||
|
~A(){}
|
||||||
|
bool await_ready() { return true; }
|
||||||
|
int await_resume() { return 42; }
|
||||||
|
template <typename F> void await_suspend(F) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
int last_value = -1;
|
||||||
|
void set_value(int x) {
|
||||||
|
last_value = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
coro_t f(int n) {
|
||||||
|
if (n == 0) {
|
||||||
|
set_value(0);
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
int val = co_await A{};
|
||||||
|
set_value(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
coro_t g() { B val = co_await B{}; }
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
last_value = -1;
|
||||||
|
f(0);
|
||||||
|
assert(last_value == 0);
|
||||||
|
f(1);
|
||||||
|
assert(last_value == 42);
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// UNSUPPORTED: c++98, c++03, c++11
|
||||||
|
// REQUIRES: fcoroutines-ts
|
||||||
|
|
||||||
|
// RUN: %build -fcoroutines-ts
|
||||||
|
// RUN: %run
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace std::experimental;
|
||||||
|
|
||||||
|
struct coro_t {
|
||||||
|
struct promise_type {
|
||||||
|
coro_t get_return_object() {
|
||||||
|
coroutine_handle<promise_type>{};
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
suspend_never initial_suspend() { return {}; }
|
||||||
|
suspend_never final_suspend() { return {}; }
|
||||||
|
void return_void(){}
|
||||||
|
void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NoSuspend {
|
||||||
|
bool await_ready() { return false; }
|
||||||
|
void await_resume() {}
|
||||||
|
template <typename F> bool await_suspend(F) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DoSuspend {
|
||||||
|
bool await_ready() { return false; }
|
||||||
|
void await_resume() {}
|
||||||
|
template <typename F> bool await_suspend(F) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool f_started, f_resumed = false;
|
||||||
|
coro_t f() {
|
||||||
|
f_started = true;
|
||||||
|
co_await DoSuspend{};
|
||||||
|
f_resumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool g_started, g_resumed = false;
|
||||||
|
coro_t g() {
|
||||||
|
g_started = true;
|
||||||
|
co_await NoSuspend{};
|
||||||
|
g_resumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
assert(!f_started && !f_resumed && !g_started && !g_resumed);
|
||||||
|
f();
|
||||||
|
assert(f_started && !f_resumed);
|
||||||
|
g();
|
||||||
|
assert(g_started && g_resumed);
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// UNSUPPORTED: c++98, c++03, c++11
|
||||||
|
// REQUIRES: fcoroutines-ts
|
||||||
|
|
||||||
|
// RUN: %build -fcoroutines-ts
|
||||||
|
// RUN: %run
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
#include <cassert>
|
||||||
|
using namespace std::experimental;
|
||||||
|
|
||||||
|
struct error {};
|
||||||
|
|
||||||
|
template <typename T, typename Error = int>
|
||||||
|
struct expected {
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
T val;
|
||||||
|
Error error;
|
||||||
|
};
|
||||||
|
Data data;
|
||||||
|
|
||||||
|
struct DataPtr {
|
||||||
|
Data *p;
|
||||||
|
~DataPtr() { delete p; }
|
||||||
|
};
|
||||||
|
|
||||||
|
expected() {}
|
||||||
|
expected(T val) : data{std::move(val),{}} {}
|
||||||
|
expected(struct error, Error error) : data{{}, std::move(error)} {}
|
||||||
|
expected(DataPtr & p) : data{std::move(p.p->val), std::move(p.p->error)} {}
|
||||||
|
|
||||||
|
struct promise_type {
|
||||||
|
Data* data;
|
||||||
|
DataPtr get_return_object() { data = new Data; return {data}; }
|
||||||
|
suspend_never initial_suspend() { return {}; }
|
||||||
|
suspend_never final_suspend() { return {}; }
|
||||||
|
void return_value(T v) { data->val = std::move(v); data->error = {};}
|
||||||
|
void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool await_ready() { return !data.error; }
|
||||||
|
T await_resume() { return std::move(data.val); }
|
||||||
|
void await_suspend(coroutine_handle<promise_type> h) {
|
||||||
|
h.promise().data->error =std::move(data.error);
|
||||||
|
h.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
T const& value() { return data.val; }
|
||||||
|
Error const& error() { return data.error; }
|
||||||
|
};
|
||||||
|
|
||||||
|
expected<int> g() { return {0}; }
|
||||||
|
expected<int> h() { return {error{}, 42}; }
|
||||||
|
|
||||||
|
extern "C" void print(int);
|
||||||
|
|
||||||
|
bool f1_started, f1_resumed = false;
|
||||||
|
expected<int> f1() {
|
||||||
|
f1_started = true;
|
||||||
|
(void)(co_await g());
|
||||||
|
f1_resumed = true;
|
||||||
|
co_return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool f2_started, f2_resumed = false;
|
||||||
|
expected<int> f2() {
|
||||||
|
f2_started = true;
|
||||||
|
(void)(co_await h());
|
||||||
|
f2_resumed = true;
|
||||||
|
co_return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto c1 = f1();
|
||||||
|
assert(f1_started && f1_resumed);
|
||||||
|
assert(c1.value() == 100);
|
||||||
|
assert(c1.error() == 0);
|
||||||
|
|
||||||
|
auto c2 = f2();
|
||||||
|
assert(f2_started && !f2_resumed);
|
||||||
|
assert(c2.value() == 0);
|
||||||
|
assert(c2.error() == 42);
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// UNSUPPORTED: c++98, c++03, c++11
|
||||||
|
// REQUIRES: fcoroutines-ts
|
||||||
|
|
||||||
|
// RUN: %build -fcoroutines-ts
|
||||||
|
// RUN: %run
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace std::experimental;
|
||||||
|
|
||||||
|
int alive = 0;
|
||||||
|
int ctor_called = 0;
|
||||||
|
int dtor_called = 0;
|
||||||
|
void reset() {
|
||||||
|
assert(alive == 0);
|
||||||
|
alive = 0;
|
||||||
|
ctor_called = 0;
|
||||||
|
dtor_called = 0;
|
||||||
|
}
|
||||||
|
struct Noisy {
|
||||||
|
Noisy() { ++alive; ++ctor_called; }
|
||||||
|
Noisy(Noisy const&) = delete;
|
||||||
|
~Noisy() { --alive; ++dtor_called; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bug {
|
||||||
|
bool await_ready() { return true; }
|
||||||
|
void await_suspend(std::experimental::coroutine_handle<>) {}
|
||||||
|
Noisy await_resume() { return {}; }
|
||||||
|
};
|
||||||
|
struct coro2 {
|
||||||
|
struct promise_type {
|
||||||
|
suspend_never initial_suspend() { return{}; }
|
||||||
|
suspend_never final_suspend() { return{}; }
|
||||||
|
coro2 get_return_object() { return{}; }
|
||||||
|
void return_void() {}
|
||||||
|
Bug yield_value(int) { return {}; }
|
||||||
|
void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks that destructors are correctly invoked for the object returned by
|
||||||
|
// coawait.
|
||||||
|
// CHECK-LABEL: @a(
|
||||||
|
coro2 a() {
|
||||||
|
reset();
|
||||||
|
{
|
||||||
|
auto x = co_await Bug{};
|
||||||
|
assert(alive == 1);
|
||||||
|
assert(ctor_called == 1);
|
||||||
|
assert(dtor_called == 0);
|
||||||
|
}
|
||||||
|
assert(alive == 0);
|
||||||
|
assert(dtor_called == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
coro2 b() {
|
||||||
|
reset();
|
||||||
|
{
|
||||||
|
co_await Bug{};
|
||||||
|
assert(ctor_called == 1);
|
||||||
|
assert(dtor_called == 1);
|
||||||
|
assert(alive == 0);
|
||||||
|
}
|
||||||
|
assert(ctor_called == 1);
|
||||||
|
assert(dtor_called == 1);
|
||||||
|
assert(alive == 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
coro2 c() {
|
||||||
|
reset();
|
||||||
|
{
|
||||||
|
auto x = co_yield 42;
|
||||||
|
assert(alive == 1);
|
||||||
|
assert(ctor_called == 1);
|
||||||
|
assert(dtor_called == 0);
|
||||||
|
}
|
||||||
|
assert(alive == 0);
|
||||||
|
assert(ctor_called == 1);
|
||||||
|
assert(dtor_called == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
coro2 d() {
|
||||||
|
reset();
|
||||||
|
{
|
||||||
|
co_yield 42;
|
||||||
|
assert(ctor_called == 1);
|
||||||
|
assert(dtor_called == 1);
|
||||||
|
assert(alive == 0);
|
||||||
|
}
|
||||||
|
assert(alive == 0);
|
||||||
|
assert(ctor_called == 1);
|
||||||
|
assert(dtor_called == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
a();
|
||||||
|
b();
|
||||||
|
c();
|
||||||
|
d();
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// UNSUPPORTED: c++98, c++03, c++11
|
||||||
|
// REQUIRES: fcoroutines-ts
|
||||||
|
|
||||||
|
// RUN: %build -fcoroutines-ts
|
||||||
|
// RUN: %run
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "coroutine_types.h"
|
||||||
|
|
||||||
|
using namespace std::experimental;
|
||||||
|
|
||||||
|
struct minig {
|
||||||
|
struct promise_type {
|
||||||
|
int current_value;
|
||||||
|
suspend_always yield_value(int value) {
|
||||||
|
this->current_value = value;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
suspend_always initial_suspend() { return {}; }
|
||||||
|
suspend_always final_suspend() { return {}; }
|
||||||
|
minig get_return_object() { return minig{this}; };
|
||||||
|
void return_void() {}
|
||||||
|
void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool move_next() {
|
||||||
|
p.resume();
|
||||||
|
return !p.done();
|
||||||
|
}
|
||||||
|
int current_value() { return p.promise().current_value; }
|
||||||
|
|
||||||
|
minig(minig &&rhs) : p(rhs.p) { rhs.p = nullptr; }
|
||||||
|
|
||||||
|
~minig() {
|
||||||
|
if (p)
|
||||||
|
p.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit minig(promise_type *p)
|
||||||
|
: p(coroutine_handle<promise_type>::from_promise(*p)) {}
|
||||||
|
|
||||||
|
coroutine_handle<promise_type> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
minig mini_count(int n) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
co_yield i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generator<int> count(int n) {
|
||||||
|
for (int i = 0; i < n; ++i)
|
||||||
|
co_yield i;
|
||||||
|
}
|
||||||
|
|
||||||
|
generator<int> range(int from, int n) {
|
||||||
|
for (int i = from; i < n; ++i)
|
||||||
|
co_yield i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_count() {
|
||||||
|
const std::vector<int> expect = {0, 1, 2, 3, 4};
|
||||||
|
std::vector<int> got;
|
||||||
|
for (auto x : count(5))
|
||||||
|
got.push_back(x);
|
||||||
|
assert(expect == got);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_range() {
|
||||||
|
int sum = 0;
|
||||||
|
for (auto v: range(1, 20))
|
||||||
|
sum += v;
|
||||||
|
assert(sum == 190);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_mini_generator() {
|
||||||
|
int sum = 0;
|
||||||
|
auto g = mini_count(5);
|
||||||
|
while (g.move_next()) {
|
||||||
|
sum += g.current_value();
|
||||||
|
}
|
||||||
|
assert(sum == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test_count();
|
||||||
|
test_range();
|
||||||
|
test_mini_generator();
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// UNSUPPORTED: c++98, c++03, c++11
|
||||||
|
// REQUIRES: fcoroutines-ts
|
||||||
|
|
||||||
|
// RUN: %build -fcoroutines-ts
|
||||||
|
// RUN: %run
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace std::experimental;
|
||||||
|
|
||||||
|
bool cancel = false;
|
||||||
|
|
||||||
|
struct goroutine
|
||||||
|
{
|
||||||
|
static int const N = 10;
|
||||||
|
static int count;
|
||||||
|
static coroutine_handle<> stack[N];
|
||||||
|
|
||||||
|
static void schedule(coroutine_handle<>& rh)
|
||||||
|
{
|
||||||
|
assert(count < N);
|
||||||
|
stack[count++] = rh;
|
||||||
|
rh = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~goroutine() {}
|
||||||
|
|
||||||
|
static void go(goroutine) {}
|
||||||
|
|
||||||
|
static void run_one()
|
||||||
|
{
|
||||||
|
assert(count > 0);
|
||||||
|
stack[--count]();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct promise_type
|
||||||
|
{
|
||||||
|
suspend_never initial_suspend() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
suspend_never final_suspend() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
void return_void() {}
|
||||||
|
goroutine get_return_object() {
|
||||||
|
return{};
|
||||||
|
}
|
||||||
|
void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
int goroutine::count;
|
||||||
|
coroutine_handle<> goroutine::stack[N];
|
||||||
|
|
||||||
|
coroutine_handle<goroutine::promise_type> workaround;
|
||||||
|
|
||||||
|
class channel;
|
||||||
|
|
||||||
|
struct push_awaiter {
|
||||||
|
channel* ch;
|
||||||
|
bool await_ready() {return false; }
|
||||||
|
void await_suspend(coroutine_handle<> rh);
|
||||||
|
void await_resume() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pull_awaiter {
|
||||||
|
channel * ch;
|
||||||
|
|
||||||
|
bool await_ready();
|
||||||
|
void await_suspend(coroutine_handle<> rh);
|
||||||
|
int await_resume();
|
||||||
|
};
|
||||||
|
|
||||||
|
class channel
|
||||||
|
{
|
||||||
|
using T = int;
|
||||||
|
|
||||||
|
friend struct push_awaiter;
|
||||||
|
friend struct pull_awaiter;
|
||||||
|
|
||||||
|
T const* pvalue = nullptr;
|
||||||
|
coroutine_handle<> reader = nullptr;
|
||||||
|
coroutine_handle<> writer = nullptr;
|
||||||
|
public:
|
||||||
|
push_awaiter push(T const& value)
|
||||||
|
{
|
||||||
|
assert(pvalue == nullptr);
|
||||||
|
assert(!writer);
|
||||||
|
pvalue = &value;
|
||||||
|
|
||||||
|
return { this };
|
||||||
|
}
|
||||||
|
|
||||||
|
pull_awaiter pull()
|
||||||
|
{
|
||||||
|
assert(!reader);
|
||||||
|
|
||||||
|
return { this };
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync_push(T const& value)
|
||||||
|
{
|
||||||
|
assert(!pvalue);
|
||||||
|
pvalue = &value;
|
||||||
|
assert(reader);
|
||||||
|
reader();
|
||||||
|
assert(!pvalue);
|
||||||
|
reader = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sync_pull()
|
||||||
|
{
|
||||||
|
while (!pvalue) goroutine::run_one();
|
||||||
|
auto result = *pvalue;
|
||||||
|
pvalue = nullptr;
|
||||||
|
if (writer)
|
||||||
|
{
|
||||||
|
auto wr = writer;
|
||||||
|
writer = nullptr;
|
||||||
|
wr();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void push_awaiter::await_suspend(coroutine_handle<> rh)
|
||||||
|
{
|
||||||
|
ch->writer = rh;
|
||||||
|
if (ch->reader) goroutine::schedule(ch->reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool pull_awaiter::await_ready() {
|
||||||
|
return !!ch->writer;
|
||||||
|
}
|
||||||
|
void pull_awaiter::await_suspend(coroutine_handle<> rh) {
|
||||||
|
ch->reader = rh;
|
||||||
|
}
|
||||||
|
int pull_awaiter::await_resume() {
|
||||||
|
auto result = *ch->pvalue;
|
||||||
|
ch->pvalue = nullptr;
|
||||||
|
if (ch->writer) {
|
||||||
|
//goroutine::schedule(ch->writer);
|
||||||
|
auto wr = ch->writer;
|
||||||
|
ch->writer = nullptr;
|
||||||
|
wr();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
goroutine pusher(channel& left, channel& right)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
auto val = co_await left.pull();
|
||||||
|
co_await right.push(val + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int N = 100; //100'000'000;
|
||||||
|
const int repeat = 1;
|
||||||
|
|
||||||
|
channel* c = new channel[N + 1];
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
for (int i = 0; i < N; ++i)
|
||||||
|
goroutine::go(pusher(c[i], c[i + 1]));
|
||||||
|
|
||||||
|
c[0].sync_push(0);
|
||||||
|
int result = c[N].sync_pull();
|
||||||
|
|
||||||
|
assert(result == 100);
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// UNSUPPORTED: c++98, c++03, c++11
|
||||||
|
// REQUIRES: fcoroutines-ts
|
||||||
|
|
||||||
|
// RUN: %build -fcoroutines-ts
|
||||||
|
// RUN: %run
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace std::experimental;
|
||||||
|
|
||||||
|
// This file tests, multishot, movable std::function like thing using coroutine
|
||||||
|
// for compile-time type erasure and unerasure.
|
||||||
|
template <typename R> struct func {
|
||||||
|
struct Input {R a, b;};
|
||||||
|
|
||||||
|
struct promise_type {
|
||||||
|
Input* I;
|
||||||
|
R result;
|
||||||
|
func get_return_object() { return {this}; }
|
||||||
|
suspend_always initial_suspend() { return {}; }
|
||||||
|
suspend_never final_suspend() { return {}; }
|
||||||
|
void return_void() {}
|
||||||
|
template <typename F>
|
||||||
|
suspend_always yield_value(F&& f) {
|
||||||
|
result = f(I->a, I->b);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
R operator()(Input I) {
|
||||||
|
h.promise().I = &I;
|
||||||
|
h.resume();
|
||||||
|
R result = h.promise().result;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
func() {}
|
||||||
|
func(func &&rhs) : h(rhs.h) { rhs.h = nullptr; }
|
||||||
|
func(func const &) = delete;
|
||||||
|
|
||||||
|
func &operator=(func &&rhs) {
|
||||||
|
if (this != &rhs) {
|
||||||
|
if (h)
|
||||||
|
h.destroy();
|
||||||
|
h = rhs.h;
|
||||||
|
rhs.h = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F> static func Create(F f) {
|
||||||
|
for (;;) {
|
||||||
|
co_yield f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F> func(F f) : func(Create(f)) {}
|
||||||
|
|
||||||
|
~func() {
|
||||||
|
if (h)
|
||||||
|
h.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
func(promise_type *promise)
|
||||||
|
: h(coroutine_handle<promise_type>::from_promise(*promise)) {}
|
||||||
|
coroutine_handle<promise_type> h;
|
||||||
|
};
|
||||||
|
|
||||||
|
int Do(int acc, int n, func<int> f) {
|
||||||
|
for (int i = 0; i < n; ++i)
|
||||||
|
acc = f({acc, i});
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int result = Do(1, 10, [](int a, int b) {return a + b;});
|
||||||
|
assert(result == 46);
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// UNSUPPORTED: c++98, c++03, c++11
|
||||||
|
// REQUIRES: fcoroutines-ts
|
||||||
|
|
||||||
|
// RUN: %build -fcoroutines-ts
|
||||||
|
// RUN: %run
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace std::experimental;
|
||||||
|
|
||||||
|
// This file tests, one shot, movable std::function like thing using coroutine
|
||||||
|
// for compile-time type erasure and unerasure.
|
||||||
|
|
||||||
|
template <typename R> struct func {
|
||||||
|
struct promise_type {
|
||||||
|
R result;
|
||||||
|
func get_return_object() { return {this}; }
|
||||||
|
suspend_always initial_suspend() { return {}; }
|
||||||
|
suspend_always final_suspend() { return {}; }
|
||||||
|
void return_value(R v) { result = v; }
|
||||||
|
void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
R operator()() {
|
||||||
|
h.resume();
|
||||||
|
R result = h.promise().result;
|
||||||
|
h.destroy();
|
||||||
|
h = nullptr;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
func() {}
|
||||||
|
func(func &&rhs) : h(rhs.h) { rhs.h = nullptr; }
|
||||||
|
func(func const &) = delete;
|
||||||
|
|
||||||
|
func &operator=(func &&rhs) {
|
||||||
|
if (this != &rhs) {
|
||||||
|
if (h)
|
||||||
|
h.destroy();
|
||||||
|
h = rhs.h;
|
||||||
|
rhs.h = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F> static func Create(F f) { co_return f(); }
|
||||||
|
|
||||||
|
template <typename F> func(F f) : func(Create(f)) {}
|
||||||
|
|
||||||
|
~func() {
|
||||||
|
if (h)
|
||||||
|
h.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
func(promise_type *promise)
|
||||||
|
: h(coroutine_handle<promise_type>::from_promise(*promise)) {}
|
||||||
|
coroutine_handle<promise_type> h;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<int> yielded_values = {};
|
||||||
|
int yield(int x) { yielded_values.push_back(x); return x + 1; }
|
||||||
|
float fyield(int x) { yielded_values.push_back(x); return x + 2; }
|
||||||
|
|
||||||
|
void Do1(func<int> f) { yield(f()); }
|
||||||
|
void Do2(func<double> f) { yield(f()); }
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Do1([] { return yield(43); });
|
||||||
|
assert((yielded_values == std::vector<int>{43, 44}));
|
||||||
|
|
||||||
|
yielded_values = {};
|
||||||
|
Do2([] { return fyield(44); });
|
||||||
|
assert((yielded_values == std::vector<int>{44, 46}));
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef SUPPORT_COROUTINE_TYPES_H
|
||||||
|
#define SUPPORT_COROUTINE_TYPES_H
|
||||||
|
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
|
||||||
|
template <typename Ty> struct generator {
|
||||||
|
struct promise_type {
|
||||||
|
Ty current_value;
|
||||||
|
std::experimental::suspend_always yield_value(Ty value) {
|
||||||
|
this->current_value = value;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::experimental::suspend_always initial_suspend() { return {}; }
|
||||||
|
std::experimental::suspend_always final_suspend() { return {}; }
|
||||||
|
generator get_return_object() { return generator{this}; };
|
||||||
|
void return_void() {}
|
||||||
|
void unhandled_exception() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iterator {
|
||||||
|
std::experimental::coroutine_handle<promise_type> _Coro;
|
||||||
|
bool _Done;
|
||||||
|
|
||||||
|
iterator(std::experimental::coroutine_handle<promise_type> Coro, bool Done)
|
||||||
|
: _Coro(Coro), _Done(Done) {}
|
||||||
|
|
||||||
|
iterator &operator++() {
|
||||||
|
_Coro.resume();
|
||||||
|
_Done = _Coro.done();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(iterator const &_Right) const {
|
||||||
|
return _Done == _Right._Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(iterator const &_Right) const { return !(*this == _Right); }
|
||||||
|
|
||||||
|
Ty const &operator*() const { return _Coro.promise().current_value; }
|
||||||
|
|
||||||
|
Ty const *operator->() const { return &(operator*()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() {
|
||||||
|
p.resume();
|
||||||
|
return {p, p.done()};
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() { return {p, true}; }
|
||||||
|
|
||||||
|
generator(generator &&rhs) : p(rhs.p) { rhs.p = nullptr; }
|
||||||
|
|
||||||
|
~generator() {
|
||||||
|
if (p)
|
||||||
|
p.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit generator(promise_type *p)
|
||||||
|
: p(std::experimental::coroutine_handle<promise_type>::from_promise(*p)) {}
|
||||||
|
|
||||||
|
std::experimental::coroutine_handle<promise_type> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SUPPORT_COROUTINE_TYPES_H
|
Loading…
Reference in New Issue