Commit a5802280 authored by Lewis Baker's avatar Lewis Baker Committed by Facebook Github Bot

Add folly::coro::co_schedule

Summary:
Adds a utility that allows a coroutine to reschedule itself back onto its associated executor.

This allows coroutines to cooperatively schedule themselves.

It also allows a coroutine to force itself to be asynchronous by inserting a `co_await folly::coro::co_schedule;` as the first statement in the coroutine.

Reviewed By: andriigrynenko

Differential Revision: D15076362

fbshipit-source-id: 4a4a2978519e0709d28a3e1aa8745ee30ddc7751
parent ad5d6ff5
......@@ -27,7 +27,7 @@ T&& getValueOrUnit(Try<T>&& value) {
return std::move(value).value();
}
Unit getValueOrUnit(Try<void>&& value) {
inline Unit getValueOrUnit(Try<void>&& value) {
value.throwIfFailed();
return Unit{};
}
......
/*
* Copyright 2019-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/Executor.h>
#include <folly/io/async/Request.h>
#include <experimental/coroutine>
namespace folly {
namespace coro {
namespace detail {
class co_schedule_t {
class Awaiter {
folly::Executor::KeepAlive<> executor_;
public:
explicit Awaiter(folly::Executor::KeepAlive<> executor) noexcept
: executor_(std::move(executor)) {}
bool await_ready() {
return false;
}
void await_suspend(std::experimental::coroutine_handle<> coro) {
executor_->add([coro, ctx = RequestContext::saveContext()]() mutable {
RequestContextScopeGuard contextScope{std::move(ctx)};
coro.resume();
});
}
void await_resume() {}
};
friend Awaiter co_viaIfAsync(
folly::Executor::KeepAlive<> executor,
co_schedule_t) {
return Awaiter{std::move(executor)};
}
};
} // namespace detail
// A SemiAwaitable object that allows you to reschedule the current coroutine
// onto the currently associated executor.
//
// This can be used as a form of cooperative multi-tasking for coroutines that
// wish to provide fair access to the execution resources. eg. to periodically
// give up their current execution slot to allow other tasks to run.
//
// Example:
// folly::coro::Task<void> doCpuIntensiveWorkFairly() {
// for (int i = 0; i < 1'000'000; ++i) {
// // Periodically reschedule to the executor.
// if ((i % 1024) == 1023) {
// co_await folly::coro::co_schedule;
// }
// doSomeWork(i);
// }
// }
inline constexpr detail::co_schedule_t co_schedule;
} // namespace coro
} // namespace folly
/*
* Copyright 2019-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/Portability.h>
#if FOLLY_HAS_COROUTINES
#include <folly/experimental/coro/BlockingWait.h>
#include <folly/experimental/coro/Collect.h>
#include <folly/experimental/coro/Schedule.h>
#include <folly/experimental/coro/Task.h>
#include <folly/portability/GTest.h>
TEST(Task, CoSchedule) {
std::vector<int> results;
folly::coro::blockingWait(folly::coro::collectAll(
folly::coro::co_invoke([&]() -> folly::coro::Task<void> {
for (int i = 0; i <= 10; i += 2) {
if (i == 6) {
co_await folly::coro::co_schedule;
}
results.push_back(i);
}
}),
folly::coro::co_invoke([&]() -> folly::coro::Task<void> {
for (int i = 1; i < 10; i += 2) {
if (i == 7) {
co_await folly::coro::co_schedule;
}
results.push_back(i);
}
})));
CHECK_EQ(11, results.size());
const int expected[11] = {0, 2, 4, 1, 3, 5, 6, 8, 10, 7, 9};
for (int i = 0; i < 11; ++i) {
CHECK_EQ(expected[i], results[i]);
}
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment