From db82865cc3495501fc8f7c29ac2d50eb066de1fb Mon Sep 17 00:00:00 2001 From: molotkovmikhail Date: Fri, 22 Sep 2023 14:41:44 +0300 Subject: [PATCH] Added MoveOnlyFunction utility Signed-off-by: molotkovmikhail --- libpandabase/CMakeLists.txt | 1 + libpandabase/taskmanager/task.cpp | 7 +- libpandabase/taskmanager/task.h | 12 +- .../tests/move_only_function_test.cpp | 141 ++++++++++++++++++ libpandabase/utils/move_only_function.h | 103 +++++++++++++ .../mem/gc/workers/gc_workers_task_queue.cpp | 2 +- 6 files changed, 255 insertions(+), 11 deletions(-) create mode 100644 libpandabase/tests/move_only_function_test.cpp create mode 100644 libpandabase/utils/move_only_function.h diff --git a/libpandabase/CMakeLists.txt b/libpandabase/CMakeLists.txt index 8e31cdc51..dcc9f52bd 100644 --- a/libpandabase/CMakeLists.txt +++ b/libpandabase/CMakeLists.txt @@ -372,6 +372,7 @@ set(ARKBASE_TESTS_SOURCES tests/ringbuf/lock_free_ring_buffer_test.cpp tests/base_thread_test.cpp tests/mem_hooks_test.cpp + tests/move_only_function_test.cpp ) if (PANDA_TARGET_UNIX) list(APPEND ARKBASE_TESTS_SOURCES diff --git a/libpandabase/taskmanager/task.cpp b/libpandabase/taskmanager/task.cpp index 1f4e8b698..969b81ed2 100644 --- a/libpandabase/taskmanager/task.cpp +++ b/libpandabase/taskmanager/task.cpp @@ -17,13 +17,10 @@ namespace panda::taskmanager { -Task::Task(TaskProperties properties, std::function runner) - : properties_(properties), runner_(std::move(runner)) -{ -} +Task::Task(TaskProperties properties, RunnerCallback runner) : properties_(properties), runner_(std::move(runner)) {} /* static */ -Task Task::Create(TaskProperties properties, std::function runner) +Task Task::Create(TaskProperties properties, RunnerCallback runner) { Task task(properties, std::move(runner)); return task; diff --git a/libpandabase/taskmanager/task.h b/libpandabase/taskmanager/task.h index 10eb49a84..a1772a700 100644 --- a/libpandabase/taskmanager/task.h +++ b/libpandabase/taskmanager/task.h @@ -16,7 +16,7 @@ #ifndef PANDA_LIBPANDABASE_TASKMANAGER_TASK_H #define PANDA_LIBPANDABASE_TASKMANAGER_TASK_H -#include "macros.h" +#include "libpandabase/utils/move_only_function.h" #include #include @@ -61,12 +61,14 @@ public: NO_COPY_SEMANTIC(Task); DEFAULT_MOVE_SEMANTIC(Task); + using RunnerCallback = MoveOnlyFunction; + /** * @brief Tasks are created through this method with the specified arguments. * @param properties - properties of task, it contains TaskType, VMType and ExecutionMote. * @param runner - body of task, that will be executed. */ - PANDA_PUBLIC_API static Task Create(TaskProperties properties, std::function runner); + PANDA_PUBLIC_API static Task Create(TaskProperties properties, RunnerCallback runner); /// @brief Returns properties of task PANDA_PUBLIC_API TaskProperties GetTaskProperties() const; @@ -74,13 +76,13 @@ public: /// @brief Executes body of task PANDA_PUBLIC_API void RunTask(); - ~Task() = default; + PANDA_PUBLIC_API ~Task() = default; private: - Task(TaskProperties properties, std::function runner); + Task(TaskProperties properties, RunnerCallback runner); TaskProperties properties_; - std::function runner_; + RunnerCallback runner_; }; PANDA_PUBLIC_API std::ostream &operator<<(std::ostream &os, TaskType type); diff --git a/libpandabase/tests/move_only_function_test.cpp b/libpandabase/tests/move_only_function_test.cpp new file mode 100644 index 000000000..e35acc2d4 --- /dev/null +++ b/libpandabase/tests/move_only_function_test.cpp @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * 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 "libpandabase/utils/move_only_function.h" +#include +#include + +namespace panda { + +class MoveOnlyFunctionTest : public testing::Test { +public: + class DefaultClass { + std::string name_; + + public: + explicit DefaultClass(std::string name) : name_(std::move(name)) {} + ~DefaultClass() = default; + + DEFAULT_COPY_SEMANTIC(DefaultClass); + DEFAULT_MOVE_SEMANTIC(DefaultClass); + + std::string operator()() + { + return "DefaultClass" + name_; + } + }; + + class NoCopyClass { + std::string name_; + + public: + explicit NoCopyClass(std::string name) : name_(std::move(name)) {} + ~NoCopyClass() = default; + + NO_COPY_SEMANTIC(NoCopyClass); + DEFAULT_MOVE_SEMANTIC(NoCopyClass); + + std::string operator()() + { + return "NoCopyClass" + name_; + } + }; + + auto GetLambda(const std::string &name) + { + return [name]() { return "Lambda" + name; }; + } + + auto GetMoveOnlyLambda(const std::string &name) + { + auto up = std::make_unique("MoveOnlyLambda" + name); + return [up = std::move(up)]() { return *up; }; + } +}; + +TEST_F(MoveOnlyFunctionTest, MoveOnlyWFunctionWithClasses) +{ + DefaultClass default_class_inst1("1"); + std::string default_class_result1 = default_class_inst1(); + MoveOnlyFunction move_only_function_with_default_class1(default_class_inst1); + ASSERT_EQ(move_only_function_with_default_class1(), default_class_result1); + + DefaultClass default_class_inst2("2"); + std::string default_class_result2 = default_class_inst2(); + move_only_function_with_default_class1 = default_class_inst2; + ASSERT_EQ(move_only_function_with_default_class1(), default_class_result2); + + MoveOnlyFunction move_only_function_with_default_class2( + std::move(move_only_function_with_default_class1)); + ASSERT_EQ(move_only_function_with_default_class2(), default_class_result2); + + move_only_function_with_default_class1 = std::move(move_only_function_with_default_class2); + ASSERT_EQ(move_only_function_with_default_class1(), default_class_result2); + + NoCopyClass no_copy_class_inst1("1"); + std::string no_copy_class_result1 = no_copy_class_inst1(); + MoveOnlyFunction move_only_function_with_no_copy_class1(no_copy_class_inst1); + ASSERT_EQ(move_only_function_with_no_copy_class1(), no_copy_class_result1); + + NoCopyClass no_copy_class_inst2("2"); + std::string no_copy_class_result2 = no_copy_class_inst2(); + move_only_function_with_no_copy_class1 = no_copy_class_inst2; + ASSERT_EQ(move_only_function_with_no_copy_class1(), no_copy_class_result2); + + MoveOnlyFunction move_only_function_with_no_copy_class2( + std::move(move_only_function_with_no_copy_class1)); + ASSERT_EQ(move_only_function_with_no_copy_class2(), no_copy_class_result2); + + move_only_function_with_no_copy_class1 = std::move(move_only_function_with_no_copy_class2); + ASSERT_EQ(move_only_function_with_no_copy_class1(), no_copy_class_result2); +} + +TEST_F(MoveOnlyFunctionTest, MoveOnlyWFunctionWithLambda) +{ + auto lambda = GetLambda("1"); + std::string lambda_result = lambda(); + MoveOnlyFunction move_only_function_with_lambda(lambda); + ASSERT_EQ(move_only_function_with_lambda(), lambda_result); + + auto another_lambda = GetLambda("2"); + std::string another_lambda_result = another_lambda(); + move_only_function_with_lambda = another_lambda; + ASSERT_EQ(move_only_function_with_lambda(), another_lambda_result); + + MoveOnlyFunction move_only_function_with_another_lambda(std::move(move_only_function_with_lambda)); + ASSERT_EQ(move_only_function_with_another_lambda(), another_lambda_result); + + move_only_function_with_lambda = std::move(move_only_function_with_another_lambda); + ASSERT_EQ(move_only_function_with_lambda(), another_lambda_result); + + auto move_only_lambda = GetMoveOnlyLambda("1"); + std::string move_only_lambda_result = move_only_lambda(); + MoveOnlyFunction move_only_function_with_move_only_lambda(move_only_lambda); + ASSERT_EQ(move_only_function_with_move_only_lambda(), move_only_lambda_result); + + auto another_move_only_lambda = GetMoveOnlyLambda("2"); + std::string another_move_only_lambda_result = another_move_only_lambda(); + move_only_function_with_move_only_lambda = another_move_only_lambda; + ASSERT_EQ(move_only_function_with_move_only_lambda(), another_move_only_lambda_result); + + MoveOnlyFunction move_only_function_with_another_move_only_lambda( + std::move(move_only_function_with_move_only_lambda)); + ASSERT_EQ(move_only_function_with_another_move_only_lambda(), another_move_only_lambda_result); + + move_only_function_with_move_only_lambda = std::move(move_only_function_with_another_move_only_lambda); + ASSERT_EQ(move_only_function_with_move_only_lambda(), another_move_only_lambda_result); +} + +} // namespace panda \ No newline at end of file diff --git a/libpandabase/utils/move_only_function.h b/libpandabase/utils/move_only_function.h new file mode 100644 index 000000000..2601a08ee --- /dev/null +++ b/libpandabase/utils/move_only_function.h @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2023 Huawei Device Co., Ltd. + * 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 LIBPANDABASE_UTILS_MOVE_ONLY_FUNCTION_H +#define LIBPANDABASE_UTILS_MOVE_ONLY_FUNCTION_H + +#include "libpandabase/macros.h" +#include +#include +#include + +namespace panda { + +/** + * MoveOnlyFunction is derived class with std::function base class. It allows you to store objects that have only + * move semantic. + */ +template +class MoveOnlyFunction : public std::function { + using BaseClass = std::function; + +public: + MoveOnlyFunction() noexcept = default; + explicit MoveOnlyFunction(std::nullptr_t np) noexcept : BaseClass(np) {} + MoveOnlyFunction &operator=(std::nullptr_t np) noexcept + { + BaseClass::operator=(np); + return *this; + } + + NO_COPY_SEMANTIC(MoveOnlyFunction); + DEFAULT_MOVE_SEMANTIC(MoveOnlyFunction); + + template ::value>> + MoveOnlyFunction(Func &&f) // NOLINT(google-explicit-constructor) + : BaseClass(InternalRepresentation(std::forward(f))) + { + } + + template ::value>> + MoveOnlyFunction &operator=(Func &&f) + { + BaseClass::operator=(InternalRepresentation(std::forward(f))); + return *this; + } + + ~MoveOnlyFunction() = default; + +private: + template + struct InternalRepresentation { + explicit InternalRepresentation(Func &&f) : func(std::forward(f)) + { + static_assert(std::is_move_constructible::value); + } + + DEFAULT_MOVE_SEMANTIC(InternalRepresentation); + + /** + * This constructor should be never called. It's exist to face the std::function requirement: Contained type + * should be copyable. + */ + InternalRepresentation(const InternalRepresentation &other) : func(const_cast(other.func)) + { + UNREACHABLE(); + } + + /** + * This constructor should be never called. It's exist to face the std::function requirement: Contained type + * should be copyable. + */ + InternalRepresentation &operator=([[maybe_unused]] const InternalRepresentation &other) + { + UNREACHABLE(); + } + + template + auto operator()(Args &&...args) + { + return func(std::forward(args)...); + } + + ~InternalRepresentation() = default; + + Func func; // NOLINT(misc-non-private-member-variables-in-classes) + }; +}; + +} // namespace panda + +#endif // LIBPANDABASE_UTILS_MOVE_ONLY_FUNCTION_H \ No newline at end of file diff --git a/runtime/mem/gc/workers/gc_workers_task_queue.cpp b/runtime/mem/gc/workers/gc_workers_task_queue.cpp index fa3698ecf..7aba7f266 100644 --- a/runtime/mem/gc/workers/gc_workers_task_queue.cpp +++ b/runtime/mem/gc/workers/gc_workers_task_queue.cpp @@ -36,7 +36,7 @@ bool GCWorkersTaskQueue::TryAddTask(GCWorkersTask &&task) auto gc_task_runner = [this, gc_worker_task = std::move(task)]() mutable { // NOLINT(performance-move-const-arg) this->RunGCWorkersTask(&gc_worker_task); }; - auto gc_task = taskmanager::Task::Create(GC_TASK_PROPERTIES, gc_task_runner); + auto gc_task = taskmanager::Task::Create(GC_TASK_PROPERTIES, std::move(gc_task_runner)); gc_task_queue_->AddTask(std::move(gc_task)); return true; } -- Gitee