Skip to content

Commit

Permalink
feat: restore MoveWrapper to maintain forward compatibility; add sche…
Browse files Browse the repository at this point in the history
…dule_move_only interface
  • Loading branch information
chloro-pn committed Dec 21, 2023
1 parent 2afd463 commit aa9c08f
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
9 changes: 9 additions & 0 deletions async_simple/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
#include <functional>
#include <string>
#include <thread>
#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.
Expand Down Expand Up @@ -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<void()> func) {
MoveWrapper<decltype(func)> 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");
Expand Down
50 changes: 50 additions & 0 deletions async_simple/MoveWrapper.h
Original file line number Diff line number Diff line change
@@ -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 <exception>
#include "async_simple/Common.h"

namespace async_simple {

// std::function requre copyConstructable, hence we provide MoveWrapper perform
// copy as move.
template <typename T>
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
59 changes: 59 additions & 0 deletions async_simple/executors/test/SimpleExecutorTest.cpp
Original file line number Diff line number Diff line change
@@ -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 <gtest/gtest.h>

#include "async_simple/executors/SimpleExecutor.h"
#include "async_simple/test/unittest.h"

#include <condition_variable>
#include <memory>
#include <mutex>
#include <type_traits>

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<std::mutex> guard(mut);
done_count += 1;
sum += 10;
cv.notify_one();
});

auto tmp = std::make_unique<int>(20);
auto move_only_functor = [&sum, &done_count, &mut, &cv,
tmp = std::move(tmp)] {
std::unique_lock<std::mutex> guard(mut);
sum += *tmp;
done_count += 1;
cv.notify_one();
};

EXPECT_FALSE(
std::is_copy_constructible<decltype(move_only_functor)>::value);
ex.schedule_move_only(
async_simple::util::move_only_function(std::move(move_only_functor)));
std::unique_lock<std::mutex> guard(mut);
cv.wait(guard, [&]() { return done_count == 2; });
EXPECT_EQ(sum, 30);
}
2 changes: 2 additions & 0 deletions docs/docs.cn/Executor.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public:
using Context = void *;
virtual bool schedule(Func func) = 0;
bool schedule_move_only(util::move_only_function<void()> func);
virtual bool currentThreadInExecutor() const = 0;
virtual ExecutorStat stat() const = 0;
virtual Context checkout();
Expand All @@ -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<void()> 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。
Expand Down
2 changes: 2 additions & 0 deletions docs/docs.en/Executor.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public:
using Context = void *;
virtual bool schedule(Func func) = 0;
bool schedule_move_only(util::move_only_function<void()> func);
virtual bool currentThreadInExecutor() const = 0;
virtual ExecutorStat stat() const = 0;
virtual Context checkout();
Expand All @@ -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<void()> 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`.
Expand Down

0 comments on commit aa9c08f

Please sign in to comment.