diff --git a/CMakeLists.txt b/CMakeLists.txt index 7450778..d72e681 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(CMAKE_CXX_FLAGS "/std:c++latest /await /EHsc") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "-std=c++2a -fcoroutines") + set(CMAKE_CXX_FLAGS "-std=c++2a -fcoroutines -pthread") endif() message(STATUS "C++ flags: ${CMAKE_CXX_FLAGS}") @@ -26,7 +26,7 @@ endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") option(OPT_USE_CONCEPT "Use conecpt instead of enable_if" ON) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - option(OPT_USE_CONCEPT "Use conecpt instead of enable_if" ON) + option(OPT_USE_CONCEPT "Use conecpt instead of enable_if" OFF) else() option(OPT_USE_CONCEPT "Use conecpt instead of enable_if" OFF) endif() diff --git a/benchmark/benchmark_async_mem.cpp b/benchmark/benchmark_async_mem.cpp index 09934f3..7777609 100644 --- a/benchmark/benchmark_async_mem.cpp +++ b/benchmark/benchmark_async_mem.cpp @@ -9,7 +9,7 @@ const size_t N = 2000000; const size_t LOOP_COUNT = 50; -volatile size_t globalValue = 0; +std::atomic globalValue{0}; void resumable_main_benchmark_mem(bool wait_key) { diff --git a/benchmark/benchmark_channel_passing_next.cpp b/benchmark/benchmark_channel_passing_next.cpp index 3e2cc76..fee5def 100644 --- a/benchmark/benchmark_channel_passing_next.cpp +++ b/benchmark/benchmark_channel_passing_next.cpp @@ -26,6 +26,20 @@ static future_t<> passing_next(channel_t rd, channel_t wr) } } +static future_t<> passing_loop_all(channel_t head, channel_t tail) +{ + for (int i = 0; i < LoopCount; ++i) + { + auto tstart = high_resolution_clock::now(); + + co_await(head << 0); + intptr_t value = co_await tail; + + auto dt = duration_cast>(high_resolution_clock::now() - tstart).count(); + std::cout << value << " cost time " << dt << "s" << std::endl; + } +} + void benchmark_main_channel_passing_next() { channel_t head{1}; @@ -39,6 +53,9 @@ void benchmark_main_channel_passing_next() in = tail; } +#if defined(__GNUC__) + go passing_loop_all(head, tail); +#else GO { for (int i = 0; i < LoopCount; ++i) @@ -52,6 +69,7 @@ void benchmark_main_channel_passing_next() std::cout << value << " cost time " << dt << "s" << std::endl; } }; +#endif this_scheduler()->run_until_notask(); } diff --git a/librf/librf.h b/librf/librf.h index 535bd5a..378567e 100644 --- a/librf/librf.h +++ b/librf/librf.h @@ -33,7 +33,7 @@ #include #include #include -#if defined(__clang__) +#if defined(__clang__) || defined(__GNUC__) #include "src/unix/coroutine.h" //编译器内建的协程函数,MSVC和clang不一样 #else #include diff --git a/librf/src/config.h b/librf/src/config.h index b5a3737..9b03415 100644 --- a/librf/src/config.h +++ b/librf/src/config.h @@ -14,9 +14,9 @@ #ifndef RESUMEF_ENABLE_CONCEPT #ifdef __cpp_lib_concepts -#define RESUMEF_ENABLE_CONCEPT 1 +/* #undef RESUMEF_ENABLE_CONCEPT */ #else -#define RESUMEF_ENABLE_CONCEPT 1 +/* #undef RESUMEF_ENABLE_CONCEPT */ #endif //#ifdef __cpp_lib_concepts #endif //#ifndef RESUMEF_ENABLE_CONCEPT diff --git a/librf/src/generator.h b/librf/src/generator.h index f3c4c05..bcb8ea0 100644 --- a/librf/src/generator.h +++ b/librf/src/generator.h @@ -160,7 +160,7 @@ namespace resumef (void)e; std::terminate(); } -#ifdef __clang__ +#if defined(__clang__) || defined(__GNUC__) void unhandled_exception() { std::terminate(); diff --git a/librf/src/mutex_v2.h b/librf/src/mutex_v2.h index aad9710..dadced0 100644 --- a/librf/src/mutex_v2.h +++ b/librf/src/mutex_v2.h @@ -25,7 +25,7 @@ namespace resumef * @brief 在析构的时候自动解锁mutex_t的辅助类。 */ template - struct [[nodiscard]] batch_unlock_t; + struct batch_unlock_t; /** * @brief 支持递归的锁。 diff --git a/librf/src/mutex_v2.inl b/librf/src/mutex_v2.inl index 6a218e4..8446700 100644 --- a/librf/src/mutex_v2.inl +++ b/librf/src/mutex_v2.inl @@ -475,7 +475,7 @@ namespace resumef for (int cnt = rand() % (1 + _mutex.size()); cnt >= 0; --cnt) { std::this_thread::yield(); //还要考虑多线程里运行的情况 - co_await ::resumef::yield(); + co_await yield_awaitor{}; } } future_t<> _ReturnValue() const; @@ -489,7 +489,7 @@ namespace resumef }; template - struct [[nodiscard]] batch_unlock_t + struct batch_unlock_t { mutex_t::_MutexAwaitAssembleT _MAA; @@ -534,13 +534,15 @@ namespace resumef co_return std::move(unlock_guard); } +#ifndef __GNUC__ template inline future_t<> mutex_t::lock(adopt_manual_unlock_t, _Mtxs&... mtxs) { mutex_t::_MutexAwaitAssembleT _MAA{ root_state(), mtxs... }; co_await detail::mutex_lock_await_lock_impl::_Lock_range(_MAA); } - +#endif + template inline future_t<> mutex_t::unlock(_Mtxs&... mtxs) { diff --git a/librf/src/promise.h b/librf/src/promise.h index e383920..0f88c37 100644 --- a/librf/src/promise.h +++ b/librf/src/promise.h @@ -30,7 +30,7 @@ namespace resumef template _Uty&& await_transform(_Uty&& _Whatever) noexcept; void set_exception(std::exception_ptr e); -#ifdef __clang__ +#if defined(__clang__) || defined(__GNUC__) void unhandled_exception(); //If the coroutine ends with an uncaught exception, it performs the following: #endif future_type get_return_object() noexcept; diff --git a/librf/src/promise.inl b/librf/src/promise.inl index e90726d..6c825d3 100644 --- a/librf/src/promise.inl +++ b/librf/src/promise.inl @@ -73,7 +73,7 @@ namespace resumef this->get_state()->set_exception(std::move(e)); } -#ifdef __clang__ +#if defined(__clang__) || defined(__GNUC__) template inline void promise_impl_t<_Ty>::unhandled_exception() { diff --git a/librf/src/scheduler.cpp b/librf/src/scheduler.cpp index aa95e78..fa16988 100644 --- a/librf/src/scheduler.cpp +++ b/librf/src/scheduler.cpp @@ -27,7 +27,7 @@ namespace resumef { if (classname) { -#if __clang__ +#if defined(__clang__) || defined(__GNUC__) #define sprintf_s sprintf #endif sprintf_s(sz_future_error_buffer, "%s, code=%s", classname, future_error_string[(size_t)(fe)]); diff --git a/librf/src/state.h b/librf/src/state.h index 3a214eb..ceb2d7f 100644 --- a/librf/src/state.h +++ b/librf/src/state.h @@ -150,7 +150,7 @@ namespace resumef //msvc认为是constexpr表达式(不写还给警告),然而,clang不这么认为。 //放弃constexpr,反正合格的编译器都会优化掉这个if判断的。 if -#ifndef __clang__ +#ifdef _MSC_VER constexpr #endif (_offset_of(state_future_t, _is_future) - _offset_of(state_future_t, _has_value) == 1) diff --git a/librf/src/unix/coroutine.h b/librf/src/unix/coroutine.h index d4ff461..e5e5241 100644 --- a/librf/src/unix/coroutine.h +++ b/librf/src/unix/coroutine.h @@ -52,7 +52,12 @@ template struct hash>; #include // for hash #include #include + +#if defined(__clang__) #include "clang_builtin.h" +#elif defined(__GNUC__) +#include "gcc_builtin.h" +#endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header @@ -61,6 +66,9 @@ template struct hash>; #ifndef _LIBCPP_HAS_NO_COROUTINES namespace std { +#if defined(__GNUC__) + inline +#endif namespace experimental { template diff --git a/librf/src/without_deadlock_assemble.inl b/librf/src/without_deadlock_assemble.inl index f976b79..7bef438 100644 --- a/librf/src/without_deadlock_assemble.inl +++ b/librf/src/without_deadlock_assemble.inl @@ -19,7 +19,8 @@ struct LOCK_ASSEMBLE_NAME(lock_impl) try { for (; _Next != _Last; ++_Next) { - if (!LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Next]))) + auto _Result__ = LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Next])); + if (!_Result__) { // try_lock failed, backout _Unlock_locks(_First, _Next, _LkN); LOCK_ASSEMBLE_RETURN(_Next); @@ -84,7 +85,8 @@ struct LOCK_ASSEMBLE_NAME(lock_impl) // attempt to lock 2 locks, by first locking _Lk0, and then trying to lock _Lk1 returns whether to try again LOCK_ASSEMBLE_AWAIT(_LkN._Lock_ref(_LkN[_Idx0])); try { - if (LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Idx1]))) + auto _Result__ = LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Idx1])); + if (_Result__) LOCK_ASSEMBLE_RETURN(false); } catch (...) { @@ -102,10 +104,20 @@ struct LOCK_ASSEMBLE_NAME(lock_impl) static auto _Lock_nonmember2(_LA& _LkN) ->decltype(_LkN._ReturnValue()) { // lock 2 locks, without deadlock, special case for better codegen and reduced metaprogramming for common case +#if defined(__GNUC__) + for (;;) + { + auto _Result__ = LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 0, 1)); + if (!_Result__) break; + _Result__ = LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 1, 0)); + if (!_Result__) break; + } +#else while (LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 0, 1)) && LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 1, 0))) { // keep trying } +#endif } template<_LockAssembleT _LA> diff --git a/tutorial/test_async_event.cpp b/tutorial/test_async_event.cpp index b4e082e..0c8fca6 100644 --- a/tutorial/test_async_event.cpp +++ b/tutorial/test_async_event.cpp @@ -22,7 +22,8 @@ static future_t<> resumable_wait_event(const event_t & e) { using namespace std::chrono; - if (co_await e.wait() == false) + auto result = co_await e.wait(); + if (result == false) std::cout << "time out!" << std::endl; else std::cout << "event signal!" << std::endl; @@ -69,7 +70,8 @@ static void test_wait_three() go[&]() -> future_t<> { - if (co_await event_t::wait_all(std::initializer_list{ evt1, evt2, evt3 })) + auto result = co_await event_t::wait_all(std::initializer_list{ evt1, evt2, evt3 }); + if (result) std::cout << "all event signal!" << std::endl; else std::cout << "time out!" << std::endl; @@ -125,7 +127,8 @@ static void test_wait_all() go[&]() -> future_t<> { - if (co_await event_t::wait_all(evts)) + auto result = co_await event_t::wait_all(evts); + if (result) std::cout << "all event signal!" << std::endl; else std::cout << "time out!" << std::endl; @@ -153,7 +156,8 @@ static void test_wait_all_timeout() go[&]() -> future_t<> { - if (co_await event_t::wait_all_for(1000ms, evts)) + auto result = co_await event_t::wait_all_for(1000ms, evts); + if (result) std::cout << "all event signal!" << std::endl; else std::cout << "time out!" << std::endl; diff --git a/tutorial/test_async_event_timeout.cpp b/tutorial/test_async_event_timeout.cpp index 5c8664e..7f1d5a0 100644 --- a/tutorial/test_async_event_timeout.cpp +++ b/tutorial/test_async_event_timeout.cpp @@ -35,7 +35,8 @@ void test_wait_timeout_one() intptr_t counter = 0; for (;;) { - if (co_await evt.wait_for(100ms)) + auto result = co_await evt.wait_for(100ms); + if (result) break; ++counter; std::cout << "."; @@ -137,7 +138,8 @@ void test_wait_timeout_all() intptr_t counter = 0; for (;;) { - if (co_await event_t::wait_all_for(1500ms, evts)) + auto result = co_await event_t::wait_all_for(1500ms, evts); + if (result) { std::cout << counter << std::endl; std::cout << "all event signal!" << std::endl; diff --git a/tutorial/test_async_event_v2.cpp b/tutorial/test_async_event_v2.cpp index b70c56f..9c48e8c 100644 --- a/tutorial/test_async_event_v2.cpp +++ b/tutorial/test_async_event_v2.cpp @@ -29,7 +29,8 @@ static std::thread async_set_event_one(event_v2::event_t e, std::chrono::millise static future_t<> resumable_wait_event(event_v2::event_t e, int idx) { - if (co_await e) + auto result = co_await e; + if (result) std::cout << "[" << idx << "]event signal!" << std::endl; else std::cout << "[" << idx << "]time out!" << std::endl; @@ -37,7 +38,8 @@ static future_t<> resumable_wait_event(event_v2::event_t e, int idx) static future_t<> resumable_wait_timeout(event_v2::event_t e, milliseconds dt, int idx) { - if (co_await e.wait_for(dt)) + auto result = co_await e.wait_for(dt); + if (result) std::cout << "[" << idx << "]event signal!" << std::endl; else std::cout << "[" << idx << "]time out!" << std::endl; diff --git a/tutorial/test_async_memory_layout.cpp b/tutorial/test_async_memory_layout.cpp index 6c87046..8c1a8dd 100644 --- a/tutorial/test_async_memory_layout.cpp +++ b/tutorial/test_async_memory_layout.cpp @@ -51,7 +51,7 @@ future_t resumeable_get_long(int64_t x, int64_t y) using promise_type = typename future_type::promise_type; using state_type = typename future_type::state_type; - void* frame_ptr = _coro_frame_ptr(); + void* frame_ptr = __builtin_coro_frame(); auto handler = coroutine_handle::from_address(frame_ptr); promise_type* promise = &handler.promise(); state_type* state = handler.promise().get_state(); @@ -59,10 +59,10 @@ future_t resumeable_get_long(int64_t x, int64_t y) std::cout << " future size=" << sizeof(future_type) << " / " << _Align_size() << std::endl; std::cout << " promise size=" << sizeof(promise_type) << " / " << _Align_size() << std::endl; std::cout << " state size=" << sizeof(state_type) << " / "<< _Align_size() << std::endl; - std::cout << " frame size=" << _coro_frame_size() << ", alloc size=" << state->get_alloc_size() << std::endl; + std::cout << " frame size=" << __builtin_coro_size() << ", alloc size=" << state->get_alloc_size() << std::endl; std::cout << " frame ptr=" << frame_ptr << "," << (void*)&frame_ptr << std::endl; - std::cout << " frame end=" << (void*)((char*)(frame_ptr)+_coro_frame_size()) << std::endl; + std::cout << " frame end=" << (void*)((char*)(frame_ptr)+__builtin_coro_size()) << std::endl; std::cout << " promise ptr=" << promise << "," << (void*)&promise << std::endl; std::cout << " handle ptr=" << handler.address() << "," << (void*)&handler << std::endl; std::cout << " state ptr=" << state << "," << (void*)&state << std::endl; @@ -90,7 +90,7 @@ future_t<> resumable_get_long_2(int64_t a, int64_t b, int64_t c) using promise_type = typename future_type::promise_type; using state_type = typename future_type::state_type; - void* frame_ptr = _coro_frame_ptr(); + void* frame_ptr = __builtin_coro_frame(); auto handler = coroutine_handle::from_address(frame_ptr); promise_type * promise = &handler.promise(); state_type * state = handler.promise().get_state(); @@ -98,10 +98,10 @@ future_t<> resumable_get_long_2(int64_t a, int64_t b, int64_t c) std::cout << " future size=" << sizeof(future_type) << " / " << _Align_size() << std::endl; std::cout << " promise size=" << sizeof(promise_type) << " / " << _Align_size() << std::endl; std::cout << " state size=" << sizeof(state_type) << " / "<< _Align_size() << std::endl; - std::cout << " frame size=" << _coro_frame_size() << ", alloc size=" << state->get_alloc_size() << std::endl; + std::cout << " frame size=" << __builtin_coro_size() << ", alloc size=" << state->get_alloc_size() << std::endl; std::cout << " frame ptr=" << frame_ptr << ","<< (void*)&frame_ptr << std::endl; - std::cout << " frame end=" << (void *)((char*)(frame_ptr) + _coro_frame_size()) << std::endl; + std::cout << " frame end=" << (void *)((char*)(frame_ptr) + __builtin_coro_size()) << std::endl; std::cout << " promise ptr=" << promise << "," << (void *)&promise << std::endl; std::cout << " handle ptr=" << handler.address() << "," << (void*)&handler << std::endl; std::cout << " state ptr=" << state << "," << (void*)&state << std::endl; diff --git a/tutorial/test_async_mutex.cpp b/tutorial/test_async_mutex.cpp index 4f48500..d7197c6 100644 --- a/tutorial/test_async_mutex.cpp +++ b/tutorial/test_async_mutex.cpp @@ -61,8 +61,12 @@ static future_t<> test_mutex_try_push(size_t idx) for (size_t i = 0; i < N; ++i) { { - while (!co_await g_lock.try_lock()) + for (;;) + { + auto result = co_await g_lock.try_lock(); + if (result) break; co_await yield(); + } ++g_counter; std::cout << "push:" << g_counter << " on " << idx << std::endl; @@ -79,8 +83,12 @@ static future_t<> test_mutex_timeout_push(size_t idx) for (size_t i = 0; i < N; ++i) { { - while (!co_await g_lock.try_lock_for(10ms)) + for (;;) + { + auto result = co_await g_lock.try_lock_for(10ms); + if (result) break; co_await yield(); + } ++g_counter; std::cout << "push:" << g_counter << " on " << idx << std::endl; diff --git a/tutorial/test_async_routine.cpp b/tutorial/test_async_routine.cpp index 1685da6..9910906 100644 --- a/tutorial/test_async_routine.cpp +++ b/tutorial/test_async_routine.cpp @@ -16,7 +16,7 @@ future_t<> test_routine_use_timer() { co_await resumef::sleep_for(100ms); std::cout << "timer after 100ms" << std::endl; - std::cout << "1:frame=" << _coro_frame_ptr() << std::endl; + std::cout << "1:frame=" << __builtin_coro_frame() << std::endl; } } @@ -25,11 +25,11 @@ future_t<> test_routine_use_timer_2() std::cout << "test_routine_use_timer_2" << std::endl; co_await test_routine_use_timer(); - std::cout << "2:frame=" << _coro_frame_ptr() << std::endl; + std::cout << "2:frame=" << __builtin_coro_frame() << std::endl; co_await test_routine_use_timer(); - std::cout << "2:frame=" << _coro_frame_ptr() << std::endl; + std::cout << "2:frame=" << __builtin_coro_frame() << std::endl; co_await test_routine_use_timer(); - std::cout << "2:frame=" << _coro_frame_ptr() << std::endl; + std::cout << "2:frame=" << __builtin_coro_frame() << std::endl; } void resumable_main_routine() diff --git a/tutorial/test_async_sleep.cpp b/tutorial/test_async_sleep.cpp index 8571019..bef06a8 100644 --- a/tutorial/test_async_sleep.cpp +++ b/tutorial/test_async_sleep.cpp @@ -38,7 +38,8 @@ void test_wait_all_events_with_signal_by_sleep() go[&]() -> future_t<> { - if (co_await event_t::wait_all(evts)) + auto result = co_await event_t::wait_all(evts); + if (result) std::cout << "all event signal!" << std::endl; else std::cout << "time out!" << std::endl; diff --git a/tutorial/test_async_switch_scheduler.cpp b/tutorial/test_async_switch_scheduler.cpp index 823e47a..778d885 100644 --- a/tutorial/test_async_switch_scheduler.cpp +++ b/tutorial/test_async_switch_scheduler.cpp @@ -88,6 +88,18 @@ static future_t<> resumable_get_long_switch_scheduler(int64_t val, channel_t resumable_main_switch_scheduler_fix_gcc_bugs(std::thread & other, channel_t c_done) +{ + co_await c_done; //第一次等待,等待run_in_thread准备好了 + + std::cout << "other thread = " << other.get_id(); + std::cout << ", sch_in_thread = " << sch_in_thread << std::endl; + + go resumable_get_long_switch_scheduler(1, c_done); //开启另外一个协程 + //co_await resumable_get_long(3, c_done); + co_await c_done; //等待新的协程运行完毕,从而保证主线程的协程不会提早退出 +} + void resumable_main_switch_scheduler() { sch_in_main = this_scheduler(); @@ -98,6 +110,9 @@ void resumable_main_switch_scheduler() channel_t c_done{ 1 }; std::thread other(&run_in_thread, std::ref(c_done)); +#if defined(__GNUC__) + go resumable_main_switch_scheduler_fix_gcc_bugs(other, c_done); +#else go[&other, c_done]()->future_t<> { co_await c_done; //第一次等待,等待run_in_thread准备好了 @@ -108,7 +123,8 @@ void resumable_main_switch_scheduler() go resumable_get_long_switch_scheduler(1, c_done); //开启另外一个协程 //co_await resumable_get_long(3, c_done); co_await c_done; //等待新的协程运行完毕,从而保证主线程的协程不会提早退出 - }; + }; //GCC: internal compiler error: in captures_temporary, at cp/coroutines.cc:2716 +#endif sch_in_main->run_until_notask();