diff --git a/async_simple/coro/Lazy.h b/async_simple/coro/Lazy.h index aec997794..13cd008af 100644 --- a/async_simple/coro/Lazy.h +++ b/async_simple/coro/Lazy.h @@ -507,9 +507,8 @@ class [[nodiscard]] CORO_ONLY_DESTROY_WHEN_DONE ELIDEABLE_AFTER_AWAIT Lazy // Bind an executor only. Don't re-schedule. // - // Users shouldn't use `setEx` directly. `setEx` is designed - // for internal purpose only. See uthread/Await.h/await for details. - Lazy setEx(Executor* ex) && { + // This function is deprecated, please use start(cb,ex) instead of setEx. + [[deprecated]] Lazy setEx(Executor* ex) && { logicAssert(this->_coro.operator bool(), "Lazy do not have a coroutine_handle " "Maybe the allocation failed or you're using a used Lazy"); @@ -517,6 +516,16 @@ class [[nodiscard]] CORO_ONLY_DESTROY_WHEN_DONE ELIDEABLE_AFTER_AWAIT Lazy return Lazy(std::exchange(this->_coro, nullptr)); } + using Base::start; + + // Bind an executor and start coroutine without scheduling immediately. + template + void directlyStart(F&& callback, Executor* executor) requires( + std::is_invocable_v>) { + this->_coro.promise()._executor = executor; + return start(std::forward(callback)); + } + auto coAwait(Executor* ex) { logicAssert(this->_coro.operator bool(), "Lazy do not have a coroutine_handle " @@ -531,6 +540,8 @@ class [[nodiscard]] CORO_ONLY_DESTROY_WHEN_DONE ELIDEABLE_AFTER_AWAIT Lazy friend class RescheduleLazy; }; +// dispatch a lazy to executor, dont reschedule immediately + // A RescheduleLazy is a Lazy with an executor. The executor of a RescheduleLazy // wouldn't/shouldn't be nullptr. So we needn't check it. // diff --git a/async_simple/coro/test/LazyTest.cpp b/async_simple/coro/test/LazyTest.cpp index fe8c913fb..b975c3529 100644 --- a/async_simple/coro/test/LazyTest.cpp +++ b/async_simple/coro/test/LazyTest.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,8 +25,10 @@ #include #include +#include "async_simple/Executor.h" #include "async_simple/coro/Collect.h" #include "async_simple/coro/Lazy.h" +#include "async_simple/coro/Sleep.h" #include "async_simple/coro/SyncAwait.h" #include "async_simple/coro/test/Time.h" #include "async_simple/executors/SimpleExecutor.h" @@ -256,6 +259,20 @@ TEST_F(LazyTest, testSimpleAsync2) { ASSERT_EQ(101, syncAwait(test(), &_executor)); } +TEST_F(LazyTest, testStartWithExecutor) { + auto test = [this]() -> Lazy { + auto executor = co_await CurrentExecutor(); + EXPECT_TRUE(executor == &_executor); + EXPECT_TRUE(executor->currentThreadInExecutor() == false); + co_await async_simple::coro::sleep(10ms); + CHECK_EXECUTOR(&_executor); + co_return; + }; + std::promise f; + test().directlyStart([&f](auto&&) { f.set_value(); }, &_executor); + f.get_future().wait(); +} + TEST_F(LazyTest, testVia) { auto test = [this]() -> Lazy { auto tid = std::this_thread::get_id(); diff --git a/async_simple/uthread/Await.h b/async_simple/uthread/Await.h index 2b4a8730c..27dc126a6 100644 --- a/async_simple/uthread/Await.h +++ b/async_simple/uthread/Await.h @@ -98,8 +98,7 @@ decltype(auto) await(Executor* ex, Fn&& fn, Args&&... args) requires co_return; }; lazy(std::forward(fn), std::forward(args)...) - .setEx(ex) - .start([](auto&&) {}); + .directlyStart([](auto&&) {}, ex); return await(std::move(f)); } diff --git a/docs/docs.cn/Executor.md b/docs/docs.cn/Executor.md index 922fa4aca..bc5100aa5 100644 --- a/docs/docs.cn/Executor.md +++ b/docs/docs.cn/Executor.md @@ -4,14 +4,16 @@ Executor是调度协程的关键组件。绝大多数开源协程框架提供内 ## 使用Executor -让协程运行在指定的调度器中非常简单,只需要创建协程时传递Executor给协程即可。在Lazy中通过`via()/setEx()`可以传递Executor;在Uthread中设置`async()`的Executor参数传递。 +让协程运行在指定的调度器中非常简单,只需要创建协程时传递Executor给协程即可。在Lazy中通过`via()`可以传递Executor。或者可以使用`directlyStart(callback, executor)`接口启动协程并惰性调度,也可以在Uthread中设置`async()`的Executor参数传递。 ```cpp Executor e; // lazy -co_await f().via(&e).start([](){}); -co_await f().setEx(&e); +// 协程绑定在对应的调度器上,立即调度执行 +f().via(&e).start([](auto&&){}); +// 协程绑定在对应的调度器上,延迟调度执行 +f().start([](auto&&){},&e); // uthread uthread::async(, &e); diff --git a/docs/docs.cn/Lazy.md b/docs/docs.cn/Lazy.md index 2cd131b18..6f2843cdf 100644 --- a/docs/docs.cn/Lazy.md +++ b/docs/docs.cn/Lazy.md @@ -30,7 +30,7 @@ Lazy task2(int x) { ## 启动方式 -一个 Lazy 应该以 `co_await`、 `syncAwait` 以及 `.start(callback)` 方式启动。 +一个 Lazy 应该以 `co_await`、 `syncAwait`, `.start(callback)` 以及 `directlyStart(callback, executor)`方式启动。 ### co_await 启动 @@ -93,6 +93,28 @@ void func() { task().start([](auto&&){}); ``` +### directlyStart(callback, executor) 启动 + +和`start`类似,但是提供了调度器接口,用于在启动协程时绑定调度器。需要注意的是,`directlyStart`不会在启动时立即调度协程。 + +```cpp +Lazy<> task() { + + auto e = co_await currentExecutor{}; + // 已经成功绑定调度器 + assert(e!=nullptr); + // 惰性调度,此时任务还未被提交给调度器运行 + assert(e.currentThreadInExecutor()==false); + co_await coro::Sleep(1s); + // Sleep函数需要使用调度器,因此任务已被提交给调度器运行。 + assert(e.currentThreadInExecutor()==true); +} +void func() { + auto executor=std::make_shared(1); + task().directlyStart([executor](Try Result){},executor.get()); +} +``` + ### syncAwait 启动 例如: diff --git a/docs/docs.en/Executor.md b/docs/docs.en/Executor.md index 9b4111aae..4cdb68cc4 100644 --- a/docs/docs.en/Executor.md +++ b/docs/docs.en/Executor.md @@ -4,14 +4,16 @@ Executor is the key component for shceduling coroutine. A lot of the open-source ### Use Executor -It is easy to assign a coroutine instance to an executor. The user need to pass the executor to coroutine only. The users could assign an executor to a Lazy by `via/setEx`. They could use `async` to pass Executor to Uthread. +It is easy to assign a coroutine instance to an executor. The user need to pass the executor to coroutine only. The users could assign an executor to a Lazy then schedule immediately by `via`. User could also use `directlyStart(callback, executor)` to assign an executor wihtout schedul immediately. They could use `async` to pass Executor to Uthread. ```cpp Executor e; // lazy -co_await f().via(&e).start([](){}); -co_await f().setEx(&e); +// schedule to executor immediately +f().via(&e).start([](auto&&){}); +// binding executor but dont schedule immediately +f().start([](auto&&){},&e); // uthread uthread::async(, &e); diff --git a/docs/docs.en/Lazy.md b/docs/docs.en/Lazy.md index fa7a23775..48d2cad45 100644 --- a/docs/docs.en/Lazy.md +++ b/docs/docs.en/Lazy.md @@ -30,7 +30,7 @@ of async_simple itself, we requrie the alignment of `T` in `Lazy` can exceed ## Start Lazy -We could start a Lazy by `co_await`, `syncAwait` and `.start(callback)`. +We could start a Lazy by `co_await`, `syncAwait`, `.start(callback)` or `directlyStart(callback, executor)`. ### co_await @@ -93,6 +93,28 @@ In case the `callback` isn't needed, we could write: task().start([](auto&&){}); ``` + +### directlyStart(callback, executor) + +Similar to `start`, but provides a paramter for binding a scheduler when starting a coroutine. It is important to note that `directlyStart` does not immediately schedule the task when coroutine start. + +```cpp +Lazy<> task() { + auto e = co_await currentExecutor{}; + // binding executor successfully. + assert(e!=nullptr); + // lazy schedule, work doesn't run in executor. + assert(e->currentThreadInExecutor()==false); + co_await coro::Sleep(1s); + // Sleep function need executor schedule, now work runs in executor. + assert(e->currentThreadInExecutor()==true); +} +void func() { + auto executor=std::make_shared(1); + task().directlyStart([executor](Try Result){},executor.get()); +} +``` + ### syncAwait For example: diff --git a/modules_test/_deps/googletest-src b/modules_test/_deps/googletest-src new file mode 160000 index 000000000..e2239ee60 --- /dev/null +++ b/modules_test/_deps/googletest-src @@ -0,0 +1 @@ +Subproject commit e2239ee6043f73722e7aa812a459f54a28552929