diff --git a/CMakeLists.txt b/CMakeLists.txt index 007d75a07..3b6e9a7a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,11 @@ else() -D__STDC_LIMIT_MACROS -g ) + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + list(APPEND CXX_FLAGS -Wno-uninitialized) + else() + list(APPEND CXX_FLAGS -Wno-maybe-uninitialized) + endif() endif() set(IS_ACC OFF CACHE INTERNAL "Whether the current compiler is ACC") diff --git a/async_simple/FutureState.h b/async_simple/FutureState.h index 9cac785fa..7f4958399 100644 --- a/async_simple/FutureState.h +++ b/async_simple/FutureState.h @@ -27,8 +27,8 @@ #include #include "async_simple/Common.h" #include "async_simple/Executor.h" -#include "async_simple/MoveWrapper.h" #include "async_simple/Try.h" +#include "async_simple/util/move_only_function.h" namespace async_simple { @@ -62,7 +62,7 @@ constexpr State operator&(State lhs, State rhs) { template class FutureState { private: - using Continuation = std::function&& value)>; + using Continuation = util::move_only_function&& value)>; private: // A helper to help FutureState to count the references to guarantee @@ -228,11 +228,10 @@ class FutureState { void setContinuation(F&& func) { logicAssert(!hasContinuation(), "FutureState already has a continuation"); - MoveWrapper lambdaFunc(std::move(func)); - new (&_continuation) Continuation([lambdaFunc](Try&& v) mutable { - auto& lambda = lambdaFunc.get(); - lambda(std::forward>(v)); - }); + new (&_continuation) + Continuation([func = std::move(func)](Try&& v) mutable { + func(std::forward>(v)); + }); auto state = _state.load(std::memory_order_acquire); switch (state) { diff --git a/async_simple/LocalState.h b/async_simple/LocalState.h index f205a045f..297de380c 100644 --- a/async_simple/LocalState.h +++ b/async_simple/LocalState.h @@ -27,7 +27,6 @@ #include #include "async_simple/Common.h" #include "async_simple/Executor.h" -#include "async_simple/MoveWrapper.h" #include "async_simple/Try.h" namespace async_simple { diff --git a/async_simple/MoveWrapper.h b/async_simple/MoveWrapper.h deleted file mode 100644 index 698039a3d..000000000 --- a/async_simple/MoveWrapper.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 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/util/move_only_function.h b/async_simple/util/move_only_function.h new file mode 100644 index 000000000..a0f51dd33 --- /dev/null +++ b/async_simple/util/move_only_function.h @@ -0,0 +1,387 @@ +/* + * 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. + */ +/* This file implements a simple condition variable. This is used as a + * low level component in async_simple. Users shouldn't use this directly. + */ +#ifndef ASYNC_SIMPLE_UTIL_MOVE_ONLY_FUNCTION_H +#define ASYNC_SIMPLE_UTIL_MOVE_ONLY_FUNCTION_H + +#include +#include +#include +#include + +namespace async_simple::util { + +template ::value, typename = void> +struct RetTypeCheck : std::false_type {}; + +template +struct RetTypeCheck> : std::true_type {}; + +// check the implicit conversion to R. +template +struct RetTypeCheck> : std::true_type { +private: + static typename ResultType::type _S_get(); + + template + static void _S_conv(T); + + template (_S_get()))> + static std::true_type _S_test(int); + + template + static std::false_type _S_test(...); + +public: + using type = decltype(_S_test(1)); +}; + +class _undefined_class; + +union _no_copy_types { + void* _m_object; + const void* _m_const_object; + void (*_m_function_pointer)(); + void (_undefined_class::*_m_member_pointer)(); +}; + +union [[gnu::may_alias]] _any_data { + void* _m_access() { return &_m_pod_data[0]; } + + const void* _m_access() const { return &_m_pod_data[0]; } + + template + T& _m_access() { + return *static_cast(_m_access()); + } + + template + const T& _m_access() const { + return *static_cast(_m_access()); + } + + _no_copy_types _m_unused; + char _m_pod_data[sizeof(_no_copy_types)]; +}; + +enum class _manager_operation : uint8_t { + _destroy_functor, +}; + +template +class move_only_function; + +class _function_base { +public: + static constexpr size_t _m_max_size = sizeof(_no_copy_types); + static constexpr size_t _m_max_align = alignof(_no_copy_types); + + template + class _base_manager { + protected: + static const bool _stored_locally = + std::is_trivially_copyable_v && + sizeof(Functor) <= _m_max_size && + alignof(Functor) <= _m_max_align && + (_m_max_align % alignof(Functor) == 0); + + typedef std::integral_constant _local_storage; + + static Functor* _m_get_pointer(const _any_data& _source) { + if constexpr (_stored_locally) { + const Functor& f = _source._m_access(); + return const_cast(std::addressof(f)); + } + return _source._m_access(); + } + + static void _m_destroy(_any_data& _victim, std::true_type) { + _victim._m_access().~Functor(); + } + + static void _m_destroy(_any_data& _victim, std::false_type) { + delete _victim._m_access(); + } + + public: + static void _m_manager(_any_data& _dest, const _any_data& _source, + _manager_operation _op) { + switch (_op) { + case _manager_operation::_destroy_functor: + _m_destroy(_dest, _local_storage()); + break; + } + } + + static void _m_init_functor(_any_data& _functor, Functor&& _f) { + _m_init_functor(_functor, std::move(_f), _local_storage()); + } + + static void _m_init_functor(_any_data& _functor, const Functor& _f) { + _m_init_functor(_functor, _f, _local_storage()); + } + + template + static bool _m_not_empty_function( + const move_only_function& f) { + return static_cast(f); + } + + template + static bool _m_not_empty_function(T* fp) { + return fp != nullptr; + } + + template + static bool _m_not_empty_function(T Class::*mp) { + return mp != nullptr; + } + + template + static bool _m_not_empty_function(const T&) { + return true; + } + + private: + static void _m_init_functor(_any_data& functor, Functor&& f, + std::true_type) { + ::new (functor._m_access()) Functor(std::move(f)); + } + + static void _m_init_functor(_any_data& functor, Functor&& f, + std::false_type) { + functor._m_access() = new Functor(std::move(f)); + } + + static void _m_init_functor(_any_data& functor, const Functor& f, + std::true_type) { + ::new (functor._m_access()) Functor(f); + } + + static void _m_init_functor(_any_data& functor, const Functor& f, + std::false_type) { + functor._m_access() = new Functor(f); + } + }; + + _function_base() : _m_manager(nullptr) {} + + ~_function_base() { + if (_m_manager) { + _m_manager(_m_functor, _m_functor, + _manager_operation::_destroy_functor); + } + } + + bool _m_empty() const { return !_m_manager; } + + using manager_type = void (*)(_any_data&, const _any_data&, + _manager_operation); + + _any_data _m_functor; + manager_type _m_manager; +}; + +template +class FunctionHandler; + +template +class FunctionHandler + : public _function_base::_base_manager { + using BaseType = _function_base::_base_manager; + +public: + static void _m_manager(_any_data& _dest, const _any_data& _source, + _manager_operation _op) { + return BaseType::_m_manager(_dest, _source, _op); + } + + static Res _m_invoke(const _any_data& _functor, ArgTypes&&... _args) { + if constexpr (std::is_same_v) { + std::invoke(*BaseType::_m_get_pointer(_functor), + std::forward(_args)...); + } else { + return std::invoke(*BaseType::_m_get_pointer(_functor), + std::forward(_args)...); + } + } +}; + +// Specialization for invalid types +template <> +class FunctionHandler { +public: + static void _m_manager(_any_data&, const _any_data&, _manager_operation) { + return; + } +}; + +template +class move_only_function : private _function_base { + template > + struct Callable : public RetTypeCheck::type {}; + + template + struct Callable : public std::false_type {}; + + template + struct IsCStyleFunction : public std::false_type {}; + + template + struct IsCStyleFunction : public std::true_type {}; + + template + using Requires = typename std::enable_if::type; + +public: + using result_type = RetType; + + // Default construct creates an empty function call wrapper. + move_only_function() noexcept : _function_base() {} + + move_only_function(std::nullptr_t) noexcept : _function_base() {} + + // delete copy constructor + move_only_function(const move_only_function&) = delete; + + move_only_function(move_only_function&& other) noexcept : _function_base() { + other.swap(*this); + } + + template < + typename Functor, + typename = + Requires, move_only_function>>, + void>, + typename = Requires>, void>, + typename = Requires, void>> + move_only_function(Functor&& f) : _function_base() { + using MyHandler = FunctionHandler>; + if (MyHandler::_m_not_empty_function(f)) { + MyHandler::_m_init_functor(_m_functor, std::forward(f)); + _m_invoker = &MyHandler::_m_invoke; + _m_manager = &MyHandler::_m_manager; + } + } + + // fix error: invalid application of 'sizeof' to a function type + // [-Werror=pointer-arith] + template + move_only_function(Res (&f)(Args...)) : _function_base() { + using MyHandler = + FunctionHandler; + if (MyHandler::_m_not_empty_function(&f)) { + MyHandler::_m_init_functor(_m_functor, &f); + _m_invoker = &MyHandler::_m_invoke; + _m_manager = &MyHandler::_m_manager; + } + } + + move_only_function& operator=(const move_only_function&) = delete; + + move_only_function& operator=(move_only_function&& other) noexcept { + move_only_function(std::move(other)).swap(*this); + return *this; + } + + move_only_function& operator=(std::nullptr_t) noexcept { + if (_m_manager) { + _m_manager(_m_functor, _m_functor, + _manager_operation::_destroy_functor); + _m_manager = nullptr; + _m_invoker = nullptr; + } + return *this; + } + + template + Requires::type>, move_only_function&> + operator=(Functor&& f) { + move_only_function(std::forward(f)).swap(*this); + return *this; + } + + void swap(move_only_function& other) noexcept { + std::swap(_m_functor, other._m_functor); + std::swap(_m_manager, other._m_manager); + std::swap(_m_invoker, other._m_invoker); + } + + explicit operator bool() const noexcept { return !_m_empty(); } + + RetType operator()(ArgTypes... _args) const { + if (_m_empty()) { + throw std::bad_function_call(); + } + return _m_invoker(_m_functor, std::forward(_args)...); + } + +private: + using InvokerType = RetType (*)(const _any_data&, ArgTypes&&...); + InvokerType _m_invoker; +}; + +template +struct _move_only_function_guide_helper {}; + +template +struct _move_only_function_guide_helper { + using type = Res(Args...); +}; + +template +struct _move_only_function_guide_helper { + using type = Res(Args...); +}; + +template +struct _move_only_function_guide_helper { + using type = Res(Args...); +}; + +template +struct _move_only_function_guide_helper { + using type = Res(Args...); +}; + +template ::type> +move_only_function(Functor) -> move_only_function; + +template +inline void swap(move_only_function& _x, + move_only_function& _y) noexcept { + _x.swap(_y); +} + +template +inline bool operator==(const move_only_function& f, + std::nullptr_t) noexcept { + return !static_cast(f); +} + +} // namespace async_simple::util + +#endif diff --git a/async_simple/util/test/MoveOnlyFunctionTest.cpp b/async_simple/util/test/MoveOnlyFunctionTest.cpp new file mode 100644 index 000000000..0f4232127 --- /dev/null +++ b/async_simple/util/test/MoveOnlyFunctionTest.cpp @@ -0,0 +1,180 @@ +/* + * 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 + +#include "async_simple/util/move_only_function.h" + +namespace async_simple { + +namespace util { + +TEST(move_only_function, empty) { + move_only_function f; + EXPECT_THROW(f(2), std::bad_function_call); +} + +int test_for_move_only(int i) { return i; } + +int test_for_move_only_2(int i) { return i + 1; } + +TEST(move_only_function, function) { + move_only_function f(test_for_move_only); + EXPECT_EQ(f(2), 2); + f = move_only_function(test_for_move_only_2); + EXPECT_EQ(f(2), 3); + f = nullptr; + EXPECT_EQ(f.operator bool(), false); + EXPECT_THROW(f(2), std::bad_function_call); + move_only_function f3(&test_for_move_only); + EXPECT_EQ(f3(2), 2); +} + +struct TestForMoveOnlyFunction { + TestForMoveOnlyFunction(int i) : i_(i) {} + + TestForMoveOnlyFunction(const TestForMoveOnlyFunction& other) { + i_ = other.i_; + copy_constructor_count += 1; + } + + TestForMoveOnlyFunction(TestForMoveOnlyFunction&& other) { + i_ = other.i_; + move_cosntructor_count += 1; + } + + TestForMoveOnlyFunction& operator=(const TestForMoveOnlyFunction& other) { + i_ = other.i_; + copy_assign_count += 1; + return *this; + } + + TestForMoveOnlyFunction& operator=(TestForMoveOnlyFunction&& other) { + i_ = other.i_; + move_assign_count += 1; + return *this; + } + + ~TestForMoveOnlyFunction() { destructor_count += 1; } + + int i_ = 0; + + int operator()() { return i_; } + + static int copy_constructor_count; + static int copy_assign_count; + + static int move_cosntructor_count; + static int move_assign_count; + static int destructor_count; + + static void reset_count() { + copy_constructor_count = 0; + copy_assign_count = 0; + move_cosntructor_count = 0; + move_assign_count = 0; + destructor_count = 0; + } +}; + +int TestForMoveOnlyFunction::copy_constructor_count = 0; +int TestForMoveOnlyFunction::copy_assign_count = 0; +int TestForMoveOnlyFunction::move_cosntructor_count = 0; +int TestForMoveOnlyFunction::move_assign_count = 0; +int TestForMoveOnlyFunction::destructor_count = 0; + +TEST(move_only_function, functor) { + move_only_function f(TestForMoveOnlyFunction{10}); + EXPECT_EQ(TestForMoveOnlyFunction::copy_assign_count, 0); + EXPECT_EQ(TestForMoveOnlyFunction::copy_constructor_count, 0); + EXPECT_EQ(TestForMoveOnlyFunction::move_assign_count, 0); + EXPECT_EQ(TestForMoveOnlyFunction::move_cosntructor_count, 1); + EXPECT_EQ(f(), 10); + TestForMoveOnlyFunction::reset_count(); + + TestForMoveOnlyFunction tmp{20}; + move_only_function f2(tmp); + EXPECT_EQ(TestForMoveOnlyFunction::copy_assign_count, 0); + EXPECT_EQ(TestForMoveOnlyFunction::copy_constructor_count, 1); + EXPECT_EQ(TestForMoveOnlyFunction::move_assign_count, 0); + EXPECT_EQ(TestForMoveOnlyFunction::move_cosntructor_count, 0); + EXPECT_EQ(f2(), 20); + TestForMoveOnlyFunction::reset_count(); + + move_only_function f3(std::move(tmp)); + EXPECT_EQ(TestForMoveOnlyFunction::copy_assign_count, 0); + EXPECT_EQ(TestForMoveOnlyFunction::copy_constructor_count, 0); + EXPECT_EQ(TestForMoveOnlyFunction::move_assign_count, 0); + EXPECT_EQ(TestForMoveOnlyFunction::move_cosntructor_count, 1); + EXPECT_EQ(f3(), 20); + TestForMoveOnlyFunction::reset_count(); + f3 = nullptr; + EXPECT_EQ(TestForMoveOnlyFunction::destructor_count, 1); +} + +TEST(move_only_function, lambda) { + std::unique_ptr num = std::make_unique(10); + auto lambda = [num = std::move(num)]() -> int { return *num; }; + move_only_function f(std::move(lambda)); + EXPECT_EQ(f(), 10); + + std::unique_ptr num2(new int(20)); + auto lambda2 = [num2 = std::move(num2)]() -> int { return *num2; }; + move_only_function f2; + f2 = std::move(lambda2); + EXPECT_EQ(f2(), 20); +} + +TEST(move_only_function, std_function) { + std::function f = [](int i) { return i + i; }; + move_only_function f2(f); + EXPECT_EQ(f2(2), 4); + f2 = std::function([](int i) { return i * 3; }); + EXPECT_EQ(f2(2), 6); +} + +struct TestForMemFnCalledByMoveOnlyFunction { + int add(int i) const { return i + 10; } +}; + +TEST(move_only_function, mem_fn) { + move_only_function f( + &TestForMemFnCalledByMoveOnlyFunction::add); + TestForMemFnCalledByMoveOnlyFunction obj; + EXPECT_EQ(f(obj, 10), 20); +} + +TEST(move_only_function, ret_type_check) { + move_only_function f(test_for_move_only); + EXPECT_NO_THROW(f(10)); +} + +TEST(move_only_function, equal_check) { + move_only_function f; + EXPECT_EQ(f, nullptr); + EXPECT_EQ(nullptr, f); + move_only_function f2(nullptr); + EXPECT_EQ(f2, nullptr); + f = []() {}; + EXPECT_NE(f, nullptr); + EXPECT_NE(nullptr, f); +} + +} // namespace util + +} // namespace async_simple