diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a0a5ae53..adea304fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ else() list(APPEND CXX_FLAGS -Wno-uninitialized) else() list(APPEND CXX_FLAGS -Wno-maybe-uninitialized) + list(APPEND CXX_FLAGS -Wno-uninitialized) endif() endif() diff --git a/async_simple/Executor.h b/async_simple/Executor.h index 14ede1cf5..d1edfedf8 100644 --- a/async_simple/Executor.h +++ b/async_simple/Executor.h @@ -20,7 +20,9 @@ #include #include #include +#include "async_simple/MoveWrapper.h" #include "async_simple/experimental/coroutine.h" +#include "async_simple/util/move_only_function.h" namespace async_simple { // Stat information for an executor. @@ -86,6 +88,13 @@ class Executor { // func will not be executed. In case schedule return true, the executor // should guarantee that the func would be executed. virtual bool schedule(Func func) = 0; + + // Schedule a move only functor + bool schedule_move_only(util::move_only_function func) { + MoveWrapper tmp(std::move(func)); + return schedule([func = tmp]() { func.get()(); }); + } + // Return true if caller runs in the executor. virtual bool currentThreadInExecutor() const { throw std::logic_error("Not implemented"); diff --git a/async_simple/MoveWrapper.h b/async_simple/MoveWrapper.h new file mode 100644 index 000000000..d75c9c1f1 --- /dev/null +++ b/async_simple/MoveWrapper.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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. + */ + +#ifndef ASYNC_SIMPLE_MOVEWRAPPER_H +#define ASYNC_SIMPLE_MOVEWRAPPER_H + +#include +#include "async_simple/Common.h" + +namespace async_simple { + +// std::function requre copyConstructable, hence we provide MoveWrapper perform +// copy as move. +template +class [[deprecated]] MoveWrapper { +public: + MoveWrapper() = default; + MoveWrapper(T&& value) : _value(std::move(value)) {} + + MoveWrapper(const MoveWrapper& other) : _value(std::move(other._value)) {} + MoveWrapper(MoveWrapper&& other) : _value(std::move(other._value)) {} + + MoveWrapper& operator=(const MoveWrapper&) = delete; + MoveWrapper& operator=(MoveWrapper&&) = delete; + + T& get() { return _value; } + const T& get() const { return _value; } + + ~MoveWrapper() {} + +private: + mutable T _value; +}; + +} // namespace async_simple + +#endif // ASYNC_SIMPLE_MOVEWRAPPER_H diff --git a/async_simple/executors/test/SimpleExecutorTest.cpp b/async_simple/executors/test/SimpleExecutorTest.cpp new file mode 100644 index 000000000..1e593706a --- /dev/null +++ b/async_simple/executors/test/SimpleExecutorTest.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022, Alibaba Group Holding Limited; + * + * 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 + +#include "async_simple/executors/SimpleExecutor.h" +#include "async_simple/test/unittest.h" + +#include +#include +#include +#include + +TEST(SimpleExecutorTest, testNormal) { + using namespace async_simple::executors; + SimpleExecutor ex(2); + + std::condition_variable cv; + std::mutex mut; + size_t done_count = 0; + size_t sum = 0; + + ex.schedule([&sum, &done_count, &mut, &cv]() { + std::unique_lock guard(mut); + done_count += 1; + sum += 10; + cv.notify_one(); + }); + + auto tmp = std::make_unique(20); + auto move_only_functor = [&sum, &done_count, &mut, &cv, + tmp = std::move(tmp)] { + std::unique_lock guard(mut); + sum += *tmp; + done_count += 1; + cv.notify_one(); + }; + + EXPECT_FALSE( + std::is_copy_constructible::value); + ex.schedule_move_only( + async_simple::util::move_only_function(std::move(move_only_functor))); + std::unique_lock guard(mut); + cv.wait(guard, [&]() { return done_count == 2; }); + EXPECT_EQ(sum, 30); +} diff --git a/docs/docs.cn/Executor.md b/docs/docs.cn/Executor.md index 6676ed8dd..922fa4aca 100644 --- a/docs/docs.cn/Executor.md +++ b/docs/docs.cn/Executor.md @@ -32,6 +32,7 @@ public: using Context = void *; virtual bool schedule(Func func) = 0; + bool schedule_move_only(util::move_only_function func); virtual bool currentThreadInExecutor() const = 0; virtual ExecutorStat stat() const = 0; virtual Context checkout(); @@ -40,6 +41,7 @@ public: ``` - `virtual bool schedule(Func func) = 0;` 接口接收一个lambda函数进行调度执行。实现时将该lambda调度到任意一个线程执行即可。当 `schedule(Func)` 返回 `true` 时,该调度器实现需要保证 func 一定会被执行。否则被提交到调度器的 Lazy 任务可能会导致程序出现内存泄漏问题。 +- `bool schedule_move_only(util::move_only_function func);` 接口可以接受一个move_only/copyable functor进行调度执行,这个接口是为了弥补std::function不能接收move_only functor的缺陷。 - `virtual bool currentThreadInExecutor() const = 0;` 接口用于检查调用者是否运行在当前Executor中。实现时判断当前线程是否隶属于Executor。 - `virtual ExecutorStat stat() const = 0;` 接口获取当前Executor状态信息。实现时可以继承ExecutorStat,添加更多状态信息方便用户调试和监控Executor。 - `virtual Context checkout();` 接口获取当前执行上下文信息。实现时一般可以直接返回当前线程在Executor中唯一标识id。 diff --git a/docs/docs.en/Executor.md b/docs/docs.en/Executor.md index 478bda89f..9b4111aae 100644 --- a/docs/docs.en/Executor.md +++ b/docs/docs.en/Executor.md @@ -31,6 +31,7 @@ public: using Context = void *; virtual bool schedule(Func func) = 0; + bool schedule_move_only(util::move_only_function func); virtual bool currentThreadInExecutor() const = 0; virtual ExecutorStat stat() const = 0; virtual Context checkout(); @@ -39,6 +40,7 @@ public: ``` - `Virtual bool schedule(Func func) = 0;`. It would schedule a lambda function to execute. When `scheudle(Func)` returns true, the implementation is required to schedule the lambda to any threads to execute. Otherwise, the unfinished Lazy tasks may result memory leaks. +- `bool schedule_move_only(util::move_only_function func);`. It would schedule a move_only or copyable functor to execute, the interface is designed to compensate for the limitation that std:: function cannot receive move_only functor. - `virtual bool currentThreadInExecutor() const = 0;`. It would check if the current thread are in the executor. - `virtual ExecutorStat stat() const = 0;`. It would return the state information of the executor. - `virtual Context checkout();` It would be called in case the user (Lazy, Uthread and Future/Promise) want to leave the current thread. The return value of `checkout` is the identity of the current thread. User who want to schedule back to the original thread should use the identity returned from `checkout`.