├── .gitignore ├── 09.http ├── conanfile.txt ├── coroutine_common.h ├── CMakeLists.txt ├── DispatchAwaiter.h ├── SleepAwaiter.h ├── FutureAwaiter.h ├── main.cpp ├── Result.h ├── TaskAwaiter.h ├── io_utils.h ├── io_utils.cpp ├── ChannelAwaiter.h ├── CommonAwaiter.h ├── Task.h ├── Executor.h ├── Scheduler.h ├── Channel.h └── TaskPromise.h ├── 04.task ├── CMakeLists.txt ├── coroutine_common.h ├── Result.h ├── io_utils.h ├── TaskAwaiter.h ├── io_utils.cpp ├── main.cpp ├── Task.h └── TaskPromise.h ├── 06.sleep ├── CMakeLists.txt ├── coroutine_common.h ├── Result.h ├── DispatchAwaiter.h ├── SleepAwaiter.h ├── io_utils.h ├── io_utils.cpp ├── TaskAwaiter.h ├── Task.h ├── main.cpp ├── TaskPromise.h ├── Executor.h └── Scheduler.h ├── 07.channel ├── CMakeLists.txt ├── coroutine_common.h ├── DispatchAwaiter.h ├── SleepAwaiter.h ├── Result.h ├── io_utils.h ├── io_utils.cpp ├── TaskAwaiter.h ├── main.cpp ├── ChannelAwaiter.h ├── Task.h ├── Executor.h ├── Scheduler.h ├── Channel.h └── TaskPromise.h ├── 08.awaiter ├── CMakeLists.txt ├── coroutine_common.h ├── DispatchAwaiter.h ├── SleepAwaiter.h ├── FutureAwaiter.h ├── Result.h ├── io_utils.h ├── io_utils.cpp ├── TaskAwaiter.h ├── ChannelAwaiter.h ├── CommonAwaiter.h ├── Task.h ├── Executor.h ├── main.cpp ├── Scheduler.h ├── Channel.h └── TaskPromise.h ├── 05.dispatcher ├── CMakeLists.txt ├── coroutine_common.h ├── Result.h ├── DispatchAwaiter.h ├── io_utils.h ├── io_utils.cpp ├── TaskAwaiter.h ├── Task.h ├── main.cpp ├── TaskPromise.h ├── Scheduler.h └── Executor.h ├── CMakeLists.txt ├── README.md ├── io.h ├── 01.intro.cpp ├── 02.sequence.cpp ├── 02.sequence_2.cpp └── 03.functional.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build* 2 | *.iml 3 | .idea -------------------------------------------------------------------------------- /09.http/conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | cpp-httplib/0.10.4 3 | openssl/3.0.2 4 | nlohmann_json/3.10.5 5 | 6 | [generators] 7 | cmake -------------------------------------------------------------------------------- /04.task/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(CppCoroutines-Tasks) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_executable("coroutine-task" 7 | main.cpp 8 | io_utils.cpp) -------------------------------------------------------------------------------- /06.sleep/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(CppCoroutines-Tasks) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_executable("coroutine-task" 7 | main.cpp 8 | io_utils.cpp) -------------------------------------------------------------------------------- /07.channel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(CppCoroutines-Tasks) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_executable("coroutine-task" 7 | main.cpp 8 | io_utils.cpp) -------------------------------------------------------------------------------- /08.awaiter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(CppCoroutines-Tasks) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_executable("coroutine-task" 7 | main.cpp 8 | io_utils.cpp) -------------------------------------------------------------------------------- /05.dispatcher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(CppCoroutines-Tasks) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_executable("coroutine-task" 7 | main.cpp 8 | io_utils.cpp) -------------------------------------------------------------------------------- /04.task/coroutine_common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 7 | 8 | #define __cpp_lib_coroutine 9 | #include 10 | 11 | #endif //CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 12 | -------------------------------------------------------------------------------- /09.http/coroutine_common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 7 | 8 | #define __cpp_lib_coroutine 9 | #include 10 | 11 | #endif //CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 12 | -------------------------------------------------------------------------------- /06.sleep/coroutine_common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 7 | 8 | #define __cpp_lib_coroutine 9 | #include 10 | 11 | #endif //CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 12 | -------------------------------------------------------------------------------- /07.channel/coroutine_common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 7 | 8 | #define __cpp_lib_coroutine 9 | #include 10 | 11 | #endif //CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 12 | -------------------------------------------------------------------------------- /08.awaiter/coroutine_common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 7 | 8 | #define __cpp_lib_coroutine 9 | #include 10 | 11 | #endif //CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 12 | -------------------------------------------------------------------------------- /05.dispatcher/coroutine_common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 7 | 8 | #define __cpp_lib_coroutine 9 | #include 10 | 11 | #endif //CPPCOROUTINES_TASKS_04_TASK_COROUTINE_COMMON_H_ 12 | -------------------------------------------------------------------------------- /09.http/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(CppCoroutines-Tasks) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 7 | conan_basic_setup() 8 | 9 | add_executable(httpdemo 10 | main.cpp 11 | io_utils.cpp) 12 | 13 | target_link_libraries(httpdemo ${CONAN_LIBS}) 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(CppCoroutines) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | # list all c files in current dir recursively. 7 | file(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") 8 | foreach(file ${files}) 9 | get_filename_component(name ${file} NAME) 10 | add_executable(${name} ${file}) 11 | endforeach() -------------------------------------------------------------------------------- /04.task/Result.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_RESULT_H_ 6 | #define CPPCOROUTINES_04_TASK_RESULT_H_ 7 | 8 | #include 9 | 10 | template 11 | struct Result { 12 | 13 | explicit Result() = default; 14 | 15 | explicit Result(T &&value) : _value(value) {} 16 | 17 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 18 | 19 | T get_or_throw() { 20 | if (_exception_ptr) { 21 | std::rethrow_exception(_exception_ptr); 22 | } 23 | return _value; 24 | } 25 | 26 | private: 27 | T _value{}; 28 | std::exception_ptr _exception_ptr; 29 | }; 30 | 31 | #endif //CPPCOROUTINES_04_TASK_RESULT_H_ 32 | -------------------------------------------------------------------------------- /06.sleep/Result.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_RESULT_H_ 6 | #define CPPCOROUTINES_04_TASK_RESULT_H_ 7 | 8 | #include 9 | 10 | template 11 | struct Result { 12 | 13 | explicit Result() = default; 14 | 15 | explicit Result(T &&value) : _value(value) {} 16 | 17 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 18 | 19 | T get_or_throw() { 20 | if (_exception_ptr) { 21 | std::rethrow_exception(_exception_ptr); 22 | } 23 | return _value; 24 | } 25 | 26 | private: 27 | T _value{}; 28 | std::exception_ptr _exception_ptr; 29 | }; 30 | 31 | #endif //CPPCOROUTINES_04_TASK_RESULT_H_ 32 | -------------------------------------------------------------------------------- /05.dispatcher/Result.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_RESULT_H_ 6 | #define CPPCOROUTINES_04_TASK_RESULT_H_ 7 | 8 | #include 9 | 10 | template 11 | struct Result { 12 | 13 | explicit Result() = default; 14 | 15 | explicit Result(T &&value) : _value(value) {} 16 | 17 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 18 | 19 | T get_or_throw() { 20 | if (_exception_ptr) { 21 | std::rethrow_exception(_exception_ptr); 22 | } 23 | return _value; 24 | } 25 | 26 | private: 27 | T _value{}; 28 | std::exception_ptr _exception_ptr; 29 | }; 30 | 31 | #endif //CPPCOROUTINES_04_TASK_RESULT_H_ 32 | -------------------------------------------------------------------------------- /06.sleep/DispatchAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | 11 | struct DispatchAwaiter { 12 | 13 | explicit DispatchAwaiter(AbstractExecutor *executor) noexcept 14 | : _executor(executor) {} 15 | 16 | bool await_ready() const { return false; } 17 | 18 | void await_suspend(std::coroutine_handle<> handle) const { 19 | _executor->execute([handle]() { 20 | handle.resume(); 21 | }); 22 | } 23 | 24 | void await_resume() {} 25 | 26 | private: 27 | AbstractExecutor *_executor; 28 | }; 29 | 30 | #endif //CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 31 | -------------------------------------------------------------------------------- /09.http/DispatchAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | 11 | struct DispatchAwaiter { 12 | 13 | explicit DispatchAwaiter(AbstractExecutor *executor) noexcept 14 | : _executor(executor) {} 15 | 16 | bool await_ready() const { return false; } 17 | 18 | void await_suspend(std::coroutine_handle<> handle) const { 19 | _executor->execute([handle]() { 20 | handle.resume(); 21 | }); 22 | } 23 | 24 | void await_resume() {} 25 | 26 | private: 27 | AbstractExecutor *_executor; 28 | }; 29 | 30 | #endif //CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 31 | -------------------------------------------------------------------------------- /05.dispatcher/DispatchAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | 11 | struct DispatchAwaiter { 12 | 13 | explicit DispatchAwaiter(AbstractExecutor *executor) noexcept 14 | : _executor(executor) {} 15 | 16 | bool await_ready() const { return false; } 17 | 18 | void await_suspend(std::coroutine_handle<> handle) const { 19 | _executor->execute([handle]() { 20 | handle.resume(); 21 | }); 22 | } 23 | 24 | void await_resume() {} 25 | 26 | private: 27 | AbstractExecutor *_executor; 28 | }; 29 | 30 | #endif //CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 31 | -------------------------------------------------------------------------------- /07.channel/DispatchAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | 11 | struct DispatchAwaiter { 12 | 13 | explicit DispatchAwaiter(AbstractExecutor *executor) noexcept 14 | : _executor(executor) {} 15 | 16 | bool await_ready() const { return false; } 17 | 18 | void await_suspend(std::coroutine_handle<> handle) const { 19 | _executor->execute([handle]() { 20 | handle.resume(); 21 | }); 22 | } 23 | 24 | void await_resume() {} 25 | 26 | private: 27 | AbstractExecutor *_executor; 28 | }; 29 | 30 | #endif //CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 31 | -------------------------------------------------------------------------------- /08.awaiter/DispatchAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | 11 | struct DispatchAwaiter { 12 | 13 | explicit DispatchAwaiter(AbstractExecutor *executor) noexcept 14 | : _executor(executor) {} 15 | 16 | bool await_ready() const { return false; } 17 | 18 | void await_suspend(std::coroutine_handle<> handle) const { 19 | _executor->execute([handle]() { 20 | handle.resume(); 21 | }); 22 | } 23 | 24 | void await_resume() {} 25 | 26 | private: 27 | AbstractExecutor *_executor; 28 | }; 29 | 30 | #endif //CPPCOROUTINES_04_TASK_DISPATCHAWAITER_H_ 31 | -------------------------------------------------------------------------------- /04.task/io_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | 5 | #ifndef CPPCOROUTINES__IO_H_ 6 | #define CPPCOROUTINES__IO_H_ 7 | 8 | #include 9 | 10 | const char *file_name(const char *path); 11 | 12 | void PrintTime(); 13 | 14 | void PrintThread(); 15 | 16 | template 17 | void Println(U... u) { 18 | using namespace std; 19 | 20 | int i = 0; 21 | auto printer = [&i](Arg arg) { 22 | if (sizeof...(U) == ++i) cout << arg << endl; 23 | else cout << arg << " "; 24 | }; 25 | (printer(u), ...); 26 | 27 | std::cout.flush(); 28 | } 29 | 30 | #define debug(...) \ 31 | PrintTime(); \ 32 | PrintThread(); \ 33 | printf("(%s:%d) %s: ", file_name(__FILE__), __LINE__, __func__); \ 34 | Println(__VA_ARGS__); 35 | 36 | #endif //CPPCOROUTINES__IO_H_ 37 | -------------------------------------------------------------------------------- /05.dispatcher/io_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | 5 | #ifndef CPPCOROUTINES__IO_H_ 6 | #define CPPCOROUTINES__IO_H_ 7 | 8 | #include 9 | 10 | const char *file_name(const char *path); 11 | 12 | void PrintTime(); 13 | 14 | void PrintThread(); 15 | 16 | template 17 | void Println(U... u) { 18 | using namespace std; 19 | 20 | int i = 0; 21 | auto printer = [&i](Arg arg) { 22 | if (sizeof...(U) == ++i) cout << arg << endl; 23 | else cout << arg << " "; 24 | }; 25 | (printer(u), ...); 26 | 27 | std::cout.flush(); 28 | } 29 | 30 | #define debug(...) \ 31 | PrintTime(); \ 32 | PrintThread(); \ 33 | printf("(%s:%d) %s: ", file_name(__FILE__), __LINE__, __func__); \ 34 | Println(__VA_ARGS__); 35 | 36 | #endif //CPPCOROUTINES__IO_H_ 37 | -------------------------------------------------------------------------------- /09.http/SleepAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/20. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 7 | 8 | #include "Executor.h" 9 | #include "Scheduler.h" 10 | #include "coroutine_common.h" 11 | #include "CommonAwaiter.h" 12 | 13 | struct SleepAwaiter : Awaiter { 14 | 15 | explicit SleepAwaiter(long long duration) noexcept 16 | : _duration(duration) {} 17 | 18 | template 19 | explicit SleepAwaiter(std::chrono::duration<_Rep, _Period> &&duration) noexcept 20 | : _duration(std::chrono::duration_cast(duration).count()) {} 21 | 22 | void after_suspend() override { 23 | static Scheduler scheduler; 24 | scheduler.execute([this] { resume(); }, _duration); 25 | } 26 | 27 | private: 28 | long long _duration; 29 | }; 30 | 31 | #endif //CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 32 | -------------------------------------------------------------------------------- /08.awaiter/SleepAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/20. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 7 | 8 | #include "Executor.h" 9 | #include "Scheduler.h" 10 | #include "coroutine_common.h" 11 | #include "CommonAwaiter.h" 12 | 13 | struct SleepAwaiter : Awaiter { 14 | 15 | explicit SleepAwaiter(long long duration) noexcept 16 | : _duration(duration) {} 17 | 18 | template 19 | explicit SleepAwaiter(std::chrono::duration<_Rep, _Period> &&duration) noexcept 20 | : _duration(std::chrono::duration_cast(duration).count()) {} 21 | 22 | void after_suspend() override { 23 | static Scheduler scheduler; 24 | scheduler.execute([this] { resume(); }, _duration); 25 | } 26 | 27 | private: 28 | long long _duration; 29 | }; 30 | 31 | #endif //CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 32 | -------------------------------------------------------------------------------- /06.sleep/SleepAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/20. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 7 | 8 | #include "Executor.h" 9 | #include "Scheduler.h" 10 | #include "coroutine_common.h" 11 | 12 | struct SleepAwaiter { 13 | 14 | explicit SleepAwaiter(AbstractExecutor *executor, long long duration) noexcept 15 | : _executor(executor), _duration(duration) {} 16 | 17 | bool await_ready() const { return false; } 18 | 19 | void await_suspend(std::coroutine_handle<> handle) const { 20 | static Scheduler scheduler; 21 | 22 | scheduler.execute([this, handle]() { 23 | _executor->execute([handle]() { 24 | handle.resume(); 25 | }); 26 | }, _duration); 27 | } 28 | 29 | void await_resume() {} 30 | 31 | private: 32 | AbstractExecutor *_executor; 33 | long long _duration; 34 | }; 35 | 36 | #endif //CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 37 | -------------------------------------------------------------------------------- /07.channel/SleepAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/20. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 7 | 8 | #include "Executor.h" 9 | #include "Scheduler.h" 10 | #include "coroutine_common.h" 11 | 12 | struct SleepAwaiter { 13 | 14 | explicit SleepAwaiter(AbstractExecutor *executor, long long duration) noexcept 15 | : _executor(executor), _duration(duration) {} 16 | 17 | bool await_ready() const { return false; } 18 | 19 | void await_suspend(std::coroutine_handle<> handle) const { 20 | static Scheduler scheduler; 21 | 22 | scheduler.execute([this, handle]() { 23 | _executor->execute([handle]() { 24 | handle.resume(); 25 | }); 26 | }, _duration); 27 | } 28 | 29 | void await_resume() {} 30 | 31 | private: 32 | AbstractExecutor *_executor; 33 | long long _duration; 34 | }; 35 | 36 | #endif //CPPCOROUTINES_TASKS_06_SLEEP_SLEEPAWAITER_H_ 37 | -------------------------------------------------------------------------------- /08.awaiter/FutureAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_FUTUREAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_FUTUREAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | #include "CommonAwaiter.h" 11 | #include 12 | #include 13 | 14 | template 15 | struct FutureAwaiter : public Awaiter { 16 | explicit FutureAwaiter(std::future &&future) noexcept 17 | : _future(std::move(future)) {} 18 | 19 | FutureAwaiter(FutureAwaiter &&awaiter) noexcept 20 | : Awaiter(awaiter), _future(std::move(awaiter._future)) {} 21 | 22 | FutureAwaiter(FutureAwaiter &) = delete; 23 | 24 | FutureAwaiter &operator=(FutureAwaiter &) = delete; 25 | 26 | protected: 27 | void after_suspend() override { 28 | std::thread([this](){ 29 | this->resume(this->_future.get()); 30 | }).detach(); 31 | } 32 | 33 | private: 34 | std::future _future; 35 | }; 36 | 37 | #endif //CPPCOROUTINES_04_TASK_FUTUREAWAITER_H_ 38 | -------------------------------------------------------------------------------- /09.http/FutureAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_FUTUREAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_FUTUREAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | #include "CommonAwaiter.h" 11 | #include 12 | #include 13 | 14 | template 15 | struct FutureAwaiter : public Awaiter { 16 | explicit FutureAwaiter(std::future &&future) noexcept 17 | : _future(std::move(future)) {} 18 | 19 | FutureAwaiter(FutureAwaiter &&awaiter) noexcept 20 | : Awaiter(awaiter), _future(std::move(awaiter._future)) {} 21 | 22 | FutureAwaiter(FutureAwaiter &) = delete; 23 | 24 | FutureAwaiter &operator=(FutureAwaiter &) = delete; 25 | 26 | protected: 27 | void after_suspend() override { 28 | std::thread([this](){ 29 | this->resume(this->_future.get()); 30 | }).detach(); 31 | } 32 | 33 | private: 34 | std::future _future; 35 | }; 36 | 37 | #endif //CPPCOROUTINES_04_TASK_FUTUREAWAITER_H_ 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 渡劫 C++ 协程 2 | 3 | 这是[渡劫 C++ 协程](https://www.bennyhuo.com/book/cpp-coroutines/)系列文章的源码,按照编号基本对应于文章的标题。 4 | 5 | 文章目录: 6 | 7 | * [0. 前言](https://www.bennyhuo.com/book/cpp-coroutines/00-foreword.html) 8 | * [1. C++ 协程概览](https://www.bennyhuo.com/book/cpp-coroutines/01-intro.html) 9 | * [2. 实现一个序列生成器](https://www.bennyhuo.com/book/cpp-coroutines/02-generator.html) 10 | * [3. 序列生成器的泛化和函数式变换](https://www.bennyhuo.com/book/cpp-coroutines/03-functional.html) 11 | * [4. 通用异步任务 Task](https://www.bennyhuo.com/book/cpp-coroutines/04-task.html) 12 | * [5. 协程的调度器](https://www.bennyhuo.com/book/cpp-coroutines/05-dispatcher.html) 13 | * [6. 基于协程的挂起实现无阻塞的 sleep](https://www.bennyhuo.com/book/cpp-coroutines/06-sleep.html) 14 | * [7. 用于协程之间消息传递的 Channel](https://www.bennyhuo.com/book/cpp-coroutines/07-channel.html) 15 | * [8. 通用 Awaiter](https://www.bennyhuo.com/book/cpp-coroutines/08-awaiter.html) 16 | * [9. 一个简单的示例](https://www.bennyhuo.com/book/cpp-coroutines/09-http.html) 17 | * [10. 后记](https://www.bennyhuo.com/book/cpp-coroutines/10-postscript.html) -------------------------------------------------------------------------------- /09.http/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | #define CPPHTTPLIB_OPENSSL_SUPPORT 5 | 6 | #include 7 | #include 8 | 9 | #include "Executor.h" 10 | #include "Task.h" 11 | #include "io_utils.h" 12 | 13 | Task http_get(std::string host, std::string path) { 14 | httplib::Client cli(host); 15 | 16 | auto res = cli.Get(path.c_str()); 17 | 18 | if (res) { 19 | co_return res->body; 20 | } else { 21 | co_return httplib::to_string(res.error()); 22 | } 23 | } 24 | 25 | Task test_http() { 26 | try { 27 | debug("send request..."); 28 | auto result = co_await http_get("https://api.github.com", "/users/bennyhuo"); 29 | debug("done."); 30 | auto json = nlohmann::json::parse(result); 31 | debug(json.dump(2)); 32 | debug(json["login"]); 33 | debug(json["url"]); 34 | debug(json["bio"]); 35 | } catch (std::exception &e) { 36 | debug(e.what()); 37 | } 38 | } 39 | 40 | int main() { 41 | test_http().get_result(); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /04.task/TaskAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | 10 | template 11 | struct Task; 12 | 13 | template 14 | struct TaskAwaiter { 15 | explicit TaskAwaiter(Task &&task) noexcept 16 | : task(std::move(task)) {} 17 | 18 | TaskAwaiter(TaskAwaiter &&completion) noexcept 19 | : task(std::exchange(completion.task, {})) {} 20 | 21 | TaskAwaiter(TaskAwaiter &) = delete; 22 | 23 | TaskAwaiter &operator=(TaskAwaiter &) = delete; 24 | 25 | constexpr bool await_ready() const noexcept { 26 | return false; 27 | } 28 | 29 | void await_suspend(std::coroutine_handle<> handle) noexcept { 30 | task.finally([handle]() { 31 | handle.resume(); 32 | }); 33 | } 34 | 35 | Result await_resume() noexcept { 36 | return task.get_result(); 37 | } 38 | 39 | private: 40 | Task task; 41 | }; 42 | 43 | #endif //CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 44 | -------------------------------------------------------------------------------- /09.http/Result.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_RESULT_H_ 6 | #define CPPCOROUTINES_04_TASK_RESULT_H_ 7 | 8 | #include 9 | 10 | template 11 | struct Result { 12 | 13 | explicit Result() = default; 14 | 15 | explicit Result(T &&value) : _value(value) {} 16 | 17 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 18 | 19 | T get_or_throw() { 20 | if (_exception_ptr) { 21 | std::rethrow_exception(_exception_ptr); 22 | } 23 | return _value; 24 | } 25 | 26 | private: 27 | T _value{}; 28 | std::exception_ptr _exception_ptr; 29 | }; 30 | 31 | template<> 32 | struct Result { 33 | 34 | explicit Result() = default; 35 | 36 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 37 | 38 | void get_or_throw() { 39 | if (_exception_ptr) { 40 | std::rethrow_exception(_exception_ptr); 41 | } 42 | } 43 | 44 | private: 45 | std::exception_ptr _exception_ptr; 46 | }; 47 | 48 | #endif //CPPCOROUTINES_04_TASK_RESULT_H_ 49 | -------------------------------------------------------------------------------- /07.channel/Result.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_RESULT_H_ 6 | #define CPPCOROUTINES_04_TASK_RESULT_H_ 7 | 8 | #include 9 | 10 | template 11 | struct Result { 12 | 13 | explicit Result() = default; 14 | 15 | explicit Result(T &&value) : _value(value) {} 16 | 17 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 18 | 19 | T get_or_throw() { 20 | if (_exception_ptr) { 21 | std::rethrow_exception(_exception_ptr); 22 | } 23 | return _value; 24 | } 25 | 26 | private: 27 | T _value{}; 28 | std::exception_ptr _exception_ptr; 29 | }; 30 | 31 | template<> 32 | struct Result { 33 | 34 | explicit Result() = default; 35 | 36 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 37 | 38 | void get_or_throw() { 39 | if (_exception_ptr) { 40 | std::rethrow_exception(_exception_ptr); 41 | } 42 | } 43 | 44 | private: 45 | std::exception_ptr _exception_ptr; 46 | }; 47 | 48 | #endif //CPPCOROUTINES_04_TASK_RESULT_H_ 49 | -------------------------------------------------------------------------------- /08.awaiter/Result.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_RESULT_H_ 6 | #define CPPCOROUTINES_04_TASK_RESULT_H_ 7 | 8 | #include 9 | 10 | template 11 | struct Result { 12 | 13 | explicit Result() = default; 14 | 15 | explicit Result(T &&value) : _value(value) {} 16 | 17 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 18 | 19 | T get_or_throw() { 20 | if (_exception_ptr) { 21 | std::rethrow_exception(_exception_ptr); 22 | } 23 | return _value; 24 | } 25 | 26 | private: 27 | T _value{}; 28 | std::exception_ptr _exception_ptr; 29 | }; 30 | 31 | template<> 32 | struct Result { 33 | 34 | explicit Result() = default; 35 | 36 | explicit Result(std::exception_ptr &&exception_ptr) : _exception_ptr(exception_ptr) {} 37 | 38 | void get_or_throw() { 39 | if (_exception_ptr) { 40 | std::rethrow_exception(_exception_ptr); 41 | } 42 | } 43 | 44 | private: 45 | std::exception_ptr _exception_ptr; 46 | }; 47 | 48 | #endif //CPPCOROUTINES_04_TASK_RESULT_H_ 49 | -------------------------------------------------------------------------------- /07.channel/io_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | 5 | #ifndef CPPCOROUTINES__IO_H_ 6 | #define CPPCOROUTINES__IO_H_ 7 | 8 | #include 9 | #include 10 | 11 | const char *file_name(const char *path); 12 | 13 | std::stringstream &PrintTime(std::stringstream &ss); 14 | 15 | std::stringstream &PrintThread(std::stringstream &ss); 16 | 17 | template 18 | void Println(std::stringstream &ss, U... u) { 19 | int i = 0; 20 | auto printer = [&ss, &i](Arg arg) { 21 | if (sizeof...(U) == ++i) ss << arg << std::endl; 22 | else ss << arg << " "; 23 | }; 24 | (printer(u), ...); 25 | 26 | std::cout << ss.str(); 27 | std::cout.flush(); 28 | } 29 | 30 | #define debug(...) \ 31 | std::stringstream ss;\ 32 | PrintTime(ss); \ 33 | PrintThread(ss); \ 34 | char buf[100]; \ 35 | size_t len = snprintf(buf, 100, "(%s:%d) %s: ", file_name(__FILE__), __LINE__, __func__); \ 36 | std::string s(buf, buf + len - 1); \ 37 | Println(ss, s, __VA_ARGS__); 38 | 39 | #endif //CPPCOROUTINES__IO_H_ 40 | -------------------------------------------------------------------------------- /09.http/TaskAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | #include "CommonAwaiter.h" 11 | 12 | template 13 | struct Task; 14 | 15 | template 16 | struct TaskAwaiter : public Awaiter { 17 | explicit TaskAwaiter(Task &&task) noexcept 18 | : task(std::move(task)) {} 19 | 20 | TaskAwaiter(TaskAwaiter &&awaiter) noexcept 21 | : Awaiter(awaiter), task(std::move(awaiter.task)) {} 22 | 23 | TaskAwaiter(TaskAwaiter &) = delete; 24 | 25 | TaskAwaiter &operator=(TaskAwaiter &) = delete; 26 | 27 | protected: 28 | void after_suspend() override { 29 | task.finally([this]() { 30 | this->resume_unsafe(); 31 | }); 32 | } 33 | 34 | void before_resume() override { 35 | this->_result = Result(task.get_result()); 36 | } 37 | 38 | private: 39 | Task task; 40 | }; 41 | 42 | #endif //CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 43 | -------------------------------------------------------------------------------- /04.task/io_utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | inline char separator() { 11 | #ifdef _WIN32 12 | return '\\'; 13 | #else 14 | return '/'; 15 | #endif 16 | } 17 | 18 | const char *file_name(const char *path) { 19 | const char *file = path; 20 | while (*path) { 21 | if (*path++ == separator()) { 22 | file = path; 23 | } 24 | } 25 | return file; 26 | } 27 | 28 | void PrintTime() { 29 | using namespace std; 30 | using namespace std::chrono; 31 | 32 | auto now = system_clock::now(); 33 | auto in_time_t = system_clock::to_time_t(now); 34 | 35 | // get number of milliseconds for the current second 36 | // (remainder after division into seconds) 37 | auto ms = duration_cast(now.time_since_epoch()) % 1000; 38 | 39 | cout << std::put_time(std::localtime(&in_time_t), "%T") 40 | << '.' << std::setfill('0') << std::setw(3) << ms.count(); 41 | } 42 | 43 | void PrintThread() { 44 | using namespace std; 45 | cout << " [Thread-" << this_thread::get_id() << "] "; 46 | } -------------------------------------------------------------------------------- /05.dispatcher/io_utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | inline char separator() { 11 | #ifdef _WIN32 12 | return '\\'; 13 | #else 14 | return '/'; 15 | #endif 16 | } 17 | 18 | const char *file_name(const char *path) { 19 | const char *file = path; 20 | while (*path) { 21 | if (*path++ == separator()) { 22 | file = path; 23 | } 24 | } 25 | return file; 26 | } 27 | 28 | void PrintTime() { 29 | using namespace std; 30 | using namespace std::chrono; 31 | 32 | auto now = system_clock::now(); 33 | auto in_time_t = system_clock::to_time_t(now); 34 | 35 | // get number of milliseconds for the current second 36 | // (remainder after division into seconds) 37 | auto ms = duration_cast(now.time_since_epoch()) % 1000; 38 | 39 | cout << std::put_time(std::localtime(&in_time_t), "%T") 40 | << '.' << std::setfill('0') << std::setw(3) << ms.count(); 41 | } 42 | 43 | void PrintThread() { 44 | using namespace std; 45 | cout << " [Thread-" << this_thread::get_id() << "] "; 46 | } -------------------------------------------------------------------------------- /06.sleep/io_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | 5 | #ifndef CPPCOROUTINES__IO_H_ 6 | #define CPPCOROUTINES__IO_H_ 7 | 8 | #include 9 | #include 10 | 11 | const char *file_name(const char *path); 12 | 13 | std::stringstream &PrintTime(std::stringstream &ss); 14 | 15 | std::stringstream &PrintThread(std::stringstream &ss); 16 | 17 | template 18 | void Println(std::stringstream &ss, U... u) { 19 | int i = 0; 20 | auto printer = [&ss, &i](Arg arg) { 21 | if (sizeof...(U) == ++i) ss << arg << std::endl; 22 | else ss << arg << " "; 23 | }; 24 | (printer(u), ...); 25 | 26 | std::cout << ss.str(); 27 | std::cout.flush(); 28 | } 29 | 30 | #define debug(...) \ 31 | do { \ 32 | std::stringstream ss;\ 33 | PrintTime(ss); \ 34 | PrintThread(ss); \ 35 | char buf[100]; \ 36 | size_t len = snprintf(buf, 100, "(%s:%d) %s: ", file_name(__FILE__), __LINE__, __func__); \ 37 | std::string s(buf, buf + len - 1); \ 38 | Println(ss, s, __VA_ARGS__); \ 39 | } while(0) 40 | 41 | #endif //CPPCOROUTINES__IO_H_ 42 | -------------------------------------------------------------------------------- /09.http/io_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | 5 | #ifndef CPPCOROUTINES__IO_H_ 6 | #define CPPCOROUTINES__IO_H_ 7 | 8 | #include 9 | #include 10 | 11 | const char *file_name(const char *path); 12 | 13 | std::stringstream &PrintTime(std::stringstream &ss); 14 | 15 | std::stringstream &PrintThread(std::stringstream &ss); 16 | 17 | template 18 | void Println(std::stringstream &ss, U... u) { 19 | int i = 0; 20 | auto printer = [&ss, &i](Arg arg) { 21 | if (sizeof...(U) == ++i) ss << arg << std::endl; 22 | else ss << arg << " "; 23 | }; 24 | (printer(u), ...); 25 | 26 | std::cout << ss.str(); 27 | std::cout.flush(); 28 | } 29 | 30 | #define debug(...) \ 31 | do { \ 32 | std::stringstream ss;\ 33 | PrintTime(ss); \ 34 | PrintThread(ss); \ 35 | char buf[100]; \ 36 | size_t len = snprintf(buf, 100, "(%s:%d) %s: ", file_name(__FILE__), __LINE__, __func__); \ 37 | std::string s(buf, buf + len - 1); \ 38 | Println(ss, s, __VA_ARGS__); \ 39 | } while(0) 40 | 41 | #endif //CPPCOROUTINES__IO_H_ 42 | -------------------------------------------------------------------------------- /08.awaiter/io_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | 5 | #ifndef CPPCOROUTINES__IO_H_ 6 | #define CPPCOROUTINES__IO_H_ 7 | 8 | #include 9 | #include 10 | 11 | const char *file_name(const char *path); 12 | 13 | std::stringstream &PrintTime(std::stringstream &ss); 14 | 15 | std::stringstream &PrintThread(std::stringstream &ss); 16 | 17 | template 18 | void Println(std::stringstream &ss, U... u) { 19 | int i = 0; 20 | auto printer = [&ss, &i](Arg arg) { 21 | if (sizeof...(U) == ++i) ss << arg << std::endl; 22 | else ss << arg << " "; 23 | }; 24 | (printer(u), ...); 25 | 26 | std::cout << ss.str(); 27 | std::cout.flush(); 28 | } 29 | 30 | #define debug(...) \ 31 | do { \ 32 | std::stringstream ss;\ 33 | PrintTime(ss); \ 34 | PrintThread(ss); \ 35 | char buf[100]; \ 36 | size_t len = snprintf(buf, 100, "(%s:%d) %s: ", file_name(__FILE__), __LINE__, __func__); \ 37 | std::string s(buf, buf + len - 1); \ 38 | Println(ss, s, __VA_ARGS__); \ 39 | } while(0) 40 | 41 | #endif //CPPCOROUTINES__IO_H_ 42 | -------------------------------------------------------------------------------- /09.http/io_utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | inline char separator() { 11 | #ifdef _WIN32 12 | return '\\'; 13 | #else 14 | return '/'; 15 | #endif 16 | } 17 | 18 | const char *file_name(const char *path) { 19 | const char *file = path; 20 | while (*path) { 21 | if (*path++ == separator()) { 22 | file = path; 23 | } 24 | } 25 | return file; 26 | } 27 | 28 | std::stringstream &PrintTime(std::stringstream &ss) { 29 | using namespace std; 30 | using namespace std::chrono; 31 | 32 | auto now = system_clock::now(); 33 | auto in_time_t = system_clock::to_time_t(now); 34 | 35 | // get number of milliseconds for the current second 36 | // (remainder after division into seconds) 37 | auto ms = duration_cast(now.time_since_epoch()) % 1000; 38 | 39 | ss << std::put_time(std::localtime(&in_time_t), "%R") 40 | << '.' << std::setfill('0') << std::setw(3) << ms.count(); 41 | return ss; 42 | } 43 | 44 | std::stringstream &PrintThread(std::stringstream &ss) { 45 | using namespace std; 46 | ss << " [Thread-" << setw(5) << this_thread::get_id() << "] "; 47 | return ss; 48 | } -------------------------------------------------------------------------------- /07.channel/io_utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | inline char separator() { 11 | #ifdef _WIN32 12 | return '\\'; 13 | #else 14 | return '/'; 15 | #endif 16 | } 17 | 18 | const char *file_name(const char *path) { 19 | const char *file = path; 20 | while (*path) { 21 | if (*path++ == separator()) { 22 | file = path; 23 | } 24 | } 25 | return file; 26 | } 27 | 28 | std::stringstream &PrintTime(std::stringstream &ss) { 29 | using namespace std; 30 | using namespace std::chrono; 31 | 32 | auto now = system_clock::now(); 33 | auto in_time_t = system_clock::to_time_t(now); 34 | 35 | // get number of milliseconds for the current second 36 | // (remainder after division into seconds) 37 | auto ms = duration_cast(now.time_since_epoch()) % 1000; 38 | 39 | ss << std::put_time(std::localtime(&in_time_t), "%T") 40 | << '.' << std::setfill('0') << std::setw(3) << ms.count(); 41 | return ss; 42 | } 43 | 44 | std::stringstream &PrintThread(std::stringstream &ss) { 45 | using namespace std; 46 | ss << " [Thread-" << setw(5) << this_thread::get_id() << "] "; 47 | return ss; 48 | } -------------------------------------------------------------------------------- /06.sleep/io_utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | inline char separator() { 12 | #ifdef _WIN32 13 | return '\\'; 14 | #else 15 | return '/'; 16 | #endif 17 | } 18 | 19 | const char *file_name(const char *path) { 20 | const char *file = path; 21 | while (*path) { 22 | if (*path++ == separator()) { 23 | file = path; 24 | } 25 | } 26 | return file; 27 | } 28 | 29 | std::stringstream &PrintTime(std::stringstream &ss) { 30 | using namespace std; 31 | using namespace std::chrono; 32 | 33 | auto now = system_clock::now(); 34 | auto in_time_t = system_clock::to_time_t(now); 35 | 36 | // get number of milliseconds for the current second 37 | // (remainder after division into seconds) 38 | auto ms = duration_cast(now.time_since_epoch()) % 1000; 39 | 40 | ss << std::put_time(std::localtime(&in_time_t), "%T") 41 | << '.' << std::setfill('0') << std::setw(3) << ms.count(); 42 | return ss; 43 | } 44 | 45 | std::stringstream &PrintThread(std::stringstream &ss) { 46 | using namespace std; 47 | ss << " [Thread-" << setw(5) << this_thread::get_id() << "] "; 48 | return ss; 49 | } -------------------------------------------------------------------------------- /04.task/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | #include "Task.h" 5 | #include "io_utils.h" 6 | 7 | Task simple_task2() { 8 | debug("task 2 start ..."); 9 | using namespace std::chrono_literals; 10 | std::this_thread::sleep_for(1s); 11 | debug("task 2 returns after 1s."); 12 | co_return 2; 13 | } 14 | 15 | Task simple_task3() { 16 | debug("in task 3 start ..."); 17 | using namespace std::chrono_literals; 18 | std::this_thread::sleep_for(2s); 19 | debug("task 3 returns after 2s."); 20 | co_return 3; 21 | } 22 | 23 | Task simple_task() { 24 | debug("task start ..."); 25 | auto result2 = co_await simple_task2(); 26 | debug("returns from task2: ", result2); 27 | auto result3 = co_await simple_task3(); 28 | debug("returns from task3: ", result3); 29 | co_return 1 + result2 + result3; 30 | } 31 | 32 | int main() { 33 | auto simpleTask = simple_task(); 34 | simpleTask.then([](int i) { 35 | debug("simple task end: ", i); 36 | }).catching([](std::exception &e) { 37 | debug("error occurred", e.what()); 38 | }); 39 | try { 40 | auto i = simpleTask.get_result(); 41 | debug("simple task end from get: ", i); 42 | } catch (std::exception &e) { 43 | debug("error: ", e.what()); 44 | } 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /08.awaiter/io_utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | inline char separator() { 12 | #ifdef _WIN32 13 | return '\\'; 14 | #else 15 | return '/'; 16 | #endif 17 | } 18 | 19 | const char *file_name(const char *path) { 20 | const char *file = path; 21 | while (*path) { 22 | if (*path++ == separator()) { 23 | file = path; 24 | } 25 | } 26 | return file; 27 | } 28 | 29 | std::stringstream &PrintTime(std::stringstream &ss) { 30 | using namespace std; 31 | using namespace std::chrono; 32 | 33 | auto now = system_clock::now(); 34 | auto in_time_t = system_clock::to_time_t(now); 35 | 36 | // get number of milliseconds for the current second 37 | // (remainder after division into seconds) 38 | auto ms = duration_cast(now.time_since_epoch()) % 1000; 39 | 40 | ss << std::put_time(std::localtime(&in_time_t), "%R") 41 | << '.' << std::setfill('0') << std::setw(3) << ms.count(); 42 | return ss; 43 | } 44 | 45 | std::stringstream &PrintThread(std::stringstream &ss) { 46 | using namespace std; 47 | ss << " [Thread-" << setw(5) << this_thread::get_id() << "] "; 48 | return ss; 49 | } -------------------------------------------------------------------------------- /06.sleep/TaskAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | 11 | template 12 | struct Task; 13 | 14 | template 15 | struct TaskAwaiter { 16 | explicit TaskAwaiter(AbstractExecutor *executor, Task &&task) noexcept 17 | : _executor(executor), task(std::move(task)) {} 18 | 19 | TaskAwaiter(TaskAwaiter &&completion) noexcept 20 | : _executor(completion._executor), task(std::exchange(completion.task, {})) {} 21 | 22 | TaskAwaiter(TaskAwaiter &) = delete; 23 | 24 | TaskAwaiter &operator=(TaskAwaiter &) = delete; 25 | 26 | constexpr bool await_ready() const noexcept { 27 | return false; 28 | } 29 | 30 | void await_suspend(std::coroutine_handle<> handle) noexcept { 31 | task.finally([handle, this]() { 32 | _executor->execute([handle]() { 33 | handle.resume(); 34 | }); 35 | }); 36 | } 37 | 38 | Result await_resume() noexcept { 39 | return task.get_result(); 40 | } 41 | 42 | private: 43 | Task task; 44 | AbstractExecutor *_executor; 45 | 46 | }; 47 | 48 | #endif //CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 49 | -------------------------------------------------------------------------------- /07.channel/TaskAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | 11 | template 12 | struct Task; 13 | 14 | template 15 | struct TaskAwaiter { 16 | explicit TaskAwaiter(AbstractExecutor *executor, Task &&task) noexcept 17 | : _executor(executor), task(std::move(task)) {} 18 | 19 | TaskAwaiter(TaskAwaiter &&completion) noexcept 20 | : _executor(completion._executor), task(std::exchange(completion.task, {})) {} 21 | 22 | TaskAwaiter(TaskAwaiter &) = delete; 23 | 24 | TaskAwaiter &operator=(TaskAwaiter &) = delete; 25 | 26 | constexpr bool await_ready() const noexcept { 27 | return false; 28 | } 29 | 30 | void await_suspend(std::coroutine_handle<> handle) noexcept { 31 | task.finally([handle, this]() { 32 | _executor->execute([handle]() { 33 | handle.resume(); 34 | }); 35 | }); 36 | } 37 | 38 | Result await_resume() noexcept { 39 | return task.get_result(); 40 | } 41 | 42 | private: 43 | Task task; 44 | AbstractExecutor *_executor; 45 | 46 | }; 47 | 48 | #endif //CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 49 | -------------------------------------------------------------------------------- /05.dispatcher/TaskAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | 11 | template 12 | struct Task; 13 | 14 | template 15 | struct TaskAwaiter { 16 | explicit TaskAwaiter(AbstractExecutor *executor, Task &&task) noexcept 17 | : _executor(executor), task(std::move(task)) {} 18 | 19 | TaskAwaiter(TaskAwaiter &&completion) noexcept 20 | : _executor(completion._executor), task(std::exchange(completion.task, {})) {} 21 | 22 | TaskAwaiter(TaskAwaiter &) = delete; 23 | 24 | TaskAwaiter &operator=(TaskAwaiter &) = delete; 25 | 26 | constexpr bool await_ready() const noexcept { 27 | return false; 28 | } 29 | 30 | void await_suspend(std::coroutine_handle<> handle) noexcept { 31 | task.finally([handle, this]() { 32 | _executor->execute([handle]() { 33 | handle.resume(); 34 | }); 35 | }); 36 | } 37 | 38 | Result await_resume() noexcept { 39 | return task.get_result(); 40 | } 41 | 42 | private: 43 | Task task; 44 | AbstractExecutor *_executor; 45 | 46 | }; 47 | 48 | #endif //CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 49 | -------------------------------------------------------------------------------- /04.task/Task.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASK_H_ 6 | #define CPPCOROUTINES_04_TASK_TASK_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "TaskPromise.h" 10 | 11 | template 12 | struct Task { 13 | 14 | using promise_type = TaskPromise; 15 | 16 | ResultType get_result() { 17 | return handle.promise().get_result(); 18 | } 19 | 20 | Task &then(std::function &&func) { 21 | handle.promise().on_completed([func](auto result) { 22 | try { 23 | func(result.get_or_throw()); 24 | } catch (std::exception &e) { 25 | // ignore. 26 | } 27 | }); 28 | return *this; 29 | } 30 | 31 | Task &catching(std::function &&func) { 32 | handle.promise().on_completed([func](auto result) { 33 | try { 34 | result.get_or_throw(); 35 | } catch (std::exception &e) { 36 | func(e); 37 | } 38 | }); 39 | return *this; 40 | } 41 | 42 | Task &finally(std::function &&func) { 43 | handle.promise().on_completed([func](auto result) { func(); }); 44 | return *this; 45 | } 46 | 47 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 48 | 49 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 50 | 51 | Task(Task &) = delete; 52 | 53 | Task &operator=(Task &) = delete; 54 | 55 | ~Task() { 56 | if (handle) handle.destroy(); 57 | } 58 | 59 | private: 60 | std::coroutine_handle handle; 61 | }; 62 | 63 | #endif //CPPCOROUTINES_04_TASK_TASK_H_ 64 | -------------------------------------------------------------------------------- /06.sleep/Task.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASK_H_ 6 | #define CPPCOROUTINES_04_TASK_TASK_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "TaskPromise.h" 10 | 11 | template 12 | struct Task { 13 | 14 | using promise_type = TaskPromise; 15 | 16 | ResultType get_result() { 17 | return handle.promise().get_result(); 18 | } 19 | 20 | Task &then(std::function &&func) { 21 | handle.promise().on_completed([func](auto result) { 22 | try { 23 | func(result.get_or_throw()); 24 | } catch (std::exception &e) { 25 | // ignore. 26 | } 27 | }); 28 | return *this; 29 | } 30 | 31 | Task &catching(std::function &&func) { 32 | handle.promise().on_completed([func](auto result) { 33 | try { 34 | result.get_or_throw(); 35 | } catch (std::exception &e) { 36 | func(e); 37 | } 38 | }); 39 | return *this; 40 | } 41 | 42 | Task &finally(std::function &&func) { 43 | handle.promise().on_completed([func](auto result) { func(); }); 44 | return *this; 45 | } 46 | 47 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 48 | 49 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 50 | 51 | Task(Task &) = delete; 52 | 53 | Task &operator=(Task &) = delete; 54 | 55 | ~Task() { 56 | if (handle) handle.destroy(); 57 | } 58 | 59 | private: 60 | std::coroutine_handle handle; 61 | }; 62 | 63 | #endif //CPPCOROUTINES_04_TASK_TASK_H_ 64 | -------------------------------------------------------------------------------- /05.dispatcher/Task.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASK_H_ 6 | #define CPPCOROUTINES_04_TASK_TASK_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "TaskPromise.h" 10 | 11 | template 12 | struct Task { 13 | 14 | using promise_type = TaskPromise; 15 | 16 | ResultType get_result() { 17 | return handle.promise().get_result(); 18 | } 19 | 20 | Task &then(std::function &&func) { 21 | handle.promise().on_completed([func](auto result) { 22 | try { 23 | func(result.get_or_throw()); 24 | } catch (std::exception &e) { 25 | // ignore. 26 | } 27 | }); 28 | return *this; 29 | } 30 | 31 | Task &catching(std::function &&func) { 32 | handle.promise().on_completed([func](auto result) { 33 | try { 34 | result.get_or_throw(); 35 | } catch (std::exception &e) { 36 | func(e); 37 | } 38 | }); 39 | return *this; 40 | } 41 | 42 | Task &finally(std::function &&func) { 43 | handle.promise().on_completed([func](auto result) { func(); }); 44 | return *this; 45 | } 46 | 47 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 48 | 49 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 50 | 51 | Task(Task &) = delete; 52 | 53 | Task &operator=(Task &) = delete; 54 | 55 | ~Task() { 56 | if (handle) handle.destroy(); 57 | } 58 | 59 | private: 60 | std::coroutine_handle handle; 61 | }; 62 | 63 | #endif //CPPCOROUTINES_04_TASK_TASK_H_ 64 | -------------------------------------------------------------------------------- /07.channel/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | #include "Executor.h" 5 | #include "Task.h" 6 | #include "io_utils.h" 7 | #include "Scheduler.h" 8 | #include "Channel.h" 9 | 10 | using namespace std::chrono_literals; 11 | 12 | Task Producer(Channel &channel) { 13 | int i = 0; 14 | while (i < 10) { 15 | debug("send: ", i); 16 | // co_await channel.write(i++); 17 | co_await (channel << i++); 18 | // co_await 50ms; 19 | } 20 | 21 | co_await 5s; 22 | channel.close(); 23 | debug("close channel, exit."); 24 | } 25 | 26 | Task Consumer(Channel &channel) { 27 | while (channel.is_active()) { 28 | try { 29 | // auto received = co_await channel.read(); 30 | int received; 31 | co_await (channel >> received); 32 | debug("receive: ", received); 33 | //co_await 500ms; 34 | } catch (std::exception &e) { 35 | debug("exception: ", e.what()); 36 | } 37 | } 38 | 39 | debug("exit."); 40 | } 41 | 42 | Task Consumer2(Channel &channel) { 43 | while (channel.is_active()) { 44 | try { 45 | auto received = co_await channel.read(); 46 | debug("receive2: ", received); 47 | // co_await 300ms; 48 | } catch (std::exception &e) { 49 | debug("exception2: ", e.what()); 50 | } 51 | } 52 | 53 | debug("exit."); 54 | } 55 | 56 | void test_channel() { 57 | auto channel = Channel(5); 58 | auto producer = Producer(channel); 59 | auto consumer = Consumer(channel); 60 | auto consumer2 = Consumer2(channel); 61 | 62 | std::this_thread::sleep_for(10s); 63 | } 64 | 65 | int main() { 66 | test_channel(); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /io.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/10. 3 | // 4 | 5 | #ifndef CPPCOROUTINES__IO_H_ 6 | #define CPPCOROUTINES__IO_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | inline char separator() { 14 | #ifdef _WIN32 15 | return '\\'; 16 | #else 17 | return '/'; 18 | #endif 19 | } 20 | 21 | constexpr const char *file_name(const char *path) { 22 | const char *file = path; 23 | while (*path) { 24 | if (*path++ == separator()) { 25 | file = path; 26 | } 27 | } 28 | return file; 29 | } 30 | 31 | void PrintTime() { 32 | using namespace std; 33 | using namespace std::chrono; 34 | 35 | auto now = system_clock::now(); 36 | auto in_time_t = system_clock::to_time_t(now); 37 | 38 | // get number of milliseconds for the current second 39 | // (remainder after division into seconds) 40 | auto ms = duration_cast(now.time_since_epoch()) % 1000; 41 | 42 | cout << std::put_time(std::localtime(&in_time_t), "%T") 43 | << '.' << std::setfill('0') << std::setw(3) << ms.count(); 44 | } 45 | 46 | void PrintThread() { 47 | using namespace std; 48 | cout << " [Thread-" << this_thread::get_id() << "] "; 49 | } 50 | 51 | template 52 | void Println(U... u) { 53 | using namespace std; 54 | 55 | int i = 0; 56 | auto printer = [&i](Arg arg) { 57 | if (sizeof...(U) == ++i) cout << arg << endl; 58 | else cout << arg << ", "; 59 | }; 60 | (printer(u), ...); 61 | 62 | std::cout.flush(); 63 | } 64 | 65 | #define debug(...) \ 66 | PrintTime(); \ 67 | PrintThread(); \ 68 | printf("(%s:%d) %s: ", file_name(__FILE__), __LINE__, __func__); \ 69 | Println(__VA_ARGS__); 70 | 71 | #endif //CPPCOROUTINES__IO_H_ 72 | -------------------------------------------------------------------------------- /01.intro.cpp: -------------------------------------------------------------------------------- 1 | #define __cpp_lib_coroutine 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std::chrono_literals; 10 | 11 | void Fun() { 12 | std::cout << 1 << std::endl; 13 | std::cout << 2 << std::endl; 14 | std::cout << 3 << std::endl; 15 | std::cout << 4 << std::endl; 16 | } 17 | 18 | struct Result { 19 | struct promise_type { 20 | std::suspend_never initial_suspend() { 21 | return {}; 22 | } 23 | 24 | std::suspend_never final_suspend() noexcept { 25 | return {}; 26 | } 27 | 28 | Result get_return_object() { 29 | return {}; 30 | } 31 | 32 | void return_void() { 33 | 34 | } 35 | 36 | // void return_value(int value) { 37 | // 38 | // } 39 | 40 | void unhandled_exception() { 41 | 42 | } 43 | }; 44 | }; 45 | 46 | struct Awaiter { 47 | int value; 48 | 49 | bool await_ready() { 50 | return false; 51 | } 52 | 53 | void await_suspend(std::coroutine_handle<> coroutine_handle) { 54 | std::async([=](){ 55 | std::this_thread::sleep_for(1s); 56 | coroutine_handle.resume(); 57 | }); 58 | } 59 | 60 | int await_resume() { 61 | return value; 62 | } 63 | }; 64 | 65 | Result Coroutine() { 66 | std::cout << 1 << std::endl; 67 | std::cout << co_await Awaiter{.value = 1000} << std::endl; 68 | std::cout << 2 << std::endl; 69 | std::cout << 3 << std::endl; 70 | co_await std::suspend_always{}; 71 | std::cout << 4 << std::endl; 72 | 73 | co_return; 74 | }; 75 | 76 | //Result Coroutine(int start_value) { 77 | // std::cout << start_value << std::endl; 78 | // co_await std::suspend_always{}; 79 | // std::cout << start_value + 1 << std::endl; 80 | // 81 | // co_return 100; 82 | //}; 83 | 84 | int main() { 85 | Coroutine(); 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /08.awaiter/TaskAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 6 | #define CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "Executor.h" 10 | #include "CommonAwaiter.h" 11 | 12 | template 13 | struct Task; 14 | 15 | template 16 | struct TaskAwaiter : public Awaiter { 17 | explicit TaskAwaiter(Task &&task) noexcept 18 | : task(std::move(task)) {} 19 | 20 | TaskAwaiter(TaskAwaiter &&awaiter) noexcept 21 | : Awaiter(awaiter), task(std::move(awaiter.task)) {} 22 | 23 | TaskAwaiter(TaskAwaiter &) = delete; 24 | 25 | TaskAwaiter &operator=(TaskAwaiter &) = delete; 26 | 27 | protected: 28 | void after_suspend() override { 29 | task.finally([this]() { 30 | this->resume_unsafe(); 31 | }); 32 | } 33 | 34 | void before_resume() override { 35 | this->_result = Result(task.get_result()); 36 | } 37 | 38 | private: 39 | Task task; 40 | }; 41 | 42 | template 43 | struct TaskAwaiter : public Awaiter { 44 | explicit TaskAwaiter(Task &&task) noexcept 45 | : task(std::move(task)) {} 46 | 47 | TaskAwaiter(TaskAwaiter &&awaiter) noexcept 48 | : Awaiter(awaiter), task(std::move(awaiter.task)) {} 49 | 50 | TaskAwaiter(TaskAwaiter &) = delete; 51 | 52 | TaskAwaiter &operator=(TaskAwaiter &) = delete; 53 | 54 | protected: 55 | void after_suspend() override { 56 | task.finally([this]() { 57 | this->resume_unsafe(); 58 | }); 59 | } 60 | 61 | void before_resume() override { 62 | task.get_result(); 63 | } 64 | 65 | private: 66 | Task task; 67 | }; 68 | 69 | #endif //CPPCOROUTINES_04_TASK_TASKAWAITER_H_ 70 | -------------------------------------------------------------------------------- /08.awaiter/ChannelAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/21. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "CommonAwaiter.h" 10 | 11 | template 12 | struct Channel; 13 | 14 | template 15 | struct WriterAwaiter : public Awaiter { 16 | Channel *channel; 17 | ValueType _value; 18 | 19 | WriterAwaiter(Channel *channel, ValueType value) : channel(channel), _value(value) {} 20 | 21 | WriterAwaiter(WriterAwaiter &&other) noexcept 22 | : Awaiter(other), 23 | channel(std::exchange(other.channel, nullptr)), 24 | _value(other._value) {} 25 | 26 | void after_suspend() override { 27 | channel->try_push_writer(this); 28 | } 29 | 30 | void before_resume() override { 31 | channel->check_closed(); 32 | channel = nullptr; 33 | } 34 | 35 | ~WriterAwaiter() { 36 | if (channel) channel->remove_writer(this); 37 | } 38 | }; 39 | 40 | template 41 | struct ReaderAwaiter : public Awaiter { 42 | Channel *channel; 43 | ValueType *p_value = nullptr; 44 | 45 | explicit ReaderAwaiter(Channel *channel) : Awaiter(), channel(channel) {} 46 | 47 | ReaderAwaiter(ReaderAwaiter &&other) noexcept 48 | : Awaiter(other), 49 | channel(std::exchange(other.channel, nullptr)), 50 | p_value(std::exchange(other.p_value, nullptr)) {} 51 | 52 | void after_suspend() override { 53 | channel->try_push_reader(this); 54 | } 55 | 56 | void before_resume() override { 57 | channel->check_closed(); 58 | if (p_value) { 59 | *p_value = this->_result->get_or_throw(); 60 | } 61 | channel = nullptr; 62 | } 63 | 64 | ~ReaderAwaiter() { 65 | if (channel) channel->remove_reader(this); 66 | } 67 | }; 68 | 69 | #endif //CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 70 | -------------------------------------------------------------------------------- /09.http/ChannelAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/21. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "CommonAwaiter.h" 10 | 11 | template 12 | struct Channel; 13 | 14 | template 15 | struct WriterAwaiter : public Awaiter { 16 | Channel *channel; 17 | ValueType _value; 18 | 19 | WriterAwaiter(Channel *channel, ValueType value) : channel(channel), _value(value) {} 20 | 21 | WriterAwaiter(WriterAwaiter &&other) noexcept 22 | : Awaiter(other), 23 | channel(std::exchange(other.channel, nullptr)), 24 | _value(other._value) {} 25 | 26 | void after_suspend() override { 27 | channel->try_push_writer(this); 28 | } 29 | 30 | void before_resume() override { 31 | channel->check_closed(); 32 | channel = nullptr; 33 | } 34 | 35 | ~WriterAwaiter() { 36 | if (channel) channel->remove_writer(this); 37 | } 38 | }; 39 | 40 | template 41 | struct ReaderAwaiter : public Awaiter { 42 | Channel *channel; 43 | ValueType *p_value = nullptr; 44 | 45 | explicit ReaderAwaiter(Channel *channel) : Awaiter(), channel(channel) {} 46 | 47 | ReaderAwaiter(ReaderAwaiter &&other) noexcept 48 | : Awaiter(other), 49 | channel(std::exchange(other.channel, nullptr)), 50 | p_value(std::exchange(other.p_value, nullptr)) {} 51 | 52 | void after_suspend() override { 53 | channel->try_push_reader(this); 54 | } 55 | 56 | void before_resume() override { 57 | channel->check_closed(); 58 | if (p_value) { 59 | *p_value = this->_result->get_or_throw(); 60 | } 61 | channel = nullptr; 62 | } 63 | 64 | ~ReaderAwaiter() { 65 | if (channel) channel->remove_reader(this); 66 | } 67 | }; 68 | 69 | #endif //CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 70 | -------------------------------------------------------------------------------- /06.sleep/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | #include "Executor.h" 5 | #include "Task.h" 6 | #include "io_utils.h" 7 | #include "Scheduler.h" 8 | 9 | void test_scheduler() { 10 | auto scheduler = Scheduler(); 11 | 12 | debug("start"); 13 | scheduler.execute([]() { debug("2"); }, 100); 14 | scheduler.execute([]() { debug("1"); }, 50); 15 | scheduler.execute([]() { debug("6"); }, 1000); 16 | scheduler.execute([]() { debug("5"); }, 500); 17 | scheduler.execute([]() { debug("3"); }, 200); 18 | scheduler.execute([]() { debug("4"); }, 300); 19 | 20 | scheduler.shutdown(); 21 | scheduler.join(); 22 | } 23 | 24 | Task simple_task2() { 25 | debug("task 2 start ..."); 26 | using namespace std::chrono_literals; 27 | co_await 1s; 28 | debug("task 2 returns after 1s."); 29 | co_return 2; 30 | } 31 | 32 | Task simple_task3() { 33 | debug("in task 3 start ..."); 34 | using namespace std::chrono_literals; 35 | co_await 2s; 36 | debug("task 3 returns after 2s."); 37 | co_return 3; 38 | } 39 | 40 | Task simple_task() { 41 | debug("task start ..."); 42 | using namespace std::chrono_literals; 43 | co_await 100ms; 44 | debug("after 100ms ..."); 45 | auto result2 = co_await simple_task2(); 46 | debug("returns from task2: ", result2); 47 | 48 | co_await 500ms; 49 | debug("after 500ms ..."); 50 | auto result3 = co_await simple_task3(); 51 | debug("returns from task3: ", result3); 52 | co_return 1 + result2 + result3; 53 | } 54 | 55 | void test_tasks() { 56 | auto simpleTask = simple_task(); 57 | simpleTask.then([](int i) { 58 | debug("simple task end: ", i); 59 | }).catching([](std::exception &e) { 60 | debug("error occurred", e.what()); 61 | }); 62 | try { 63 | auto i = simpleTask.get_result(); 64 | debug("simple task end from get: ", i); 65 | } catch (std::exception &e) { 66 | debug("error: ", e.what()); 67 | } 68 | } 69 | 70 | int main() { 71 | test_tasks(); 72 | // test_scheduler(); 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /05.dispatcher/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | #include "Executor.h" 5 | #include "Task.h" 6 | #include "io_utils.h" 7 | #include "Scheduler.h" 8 | 9 | void test_scheduler() { 10 | auto scheduler = Scheduler(); 11 | 12 | debug("start") 13 | scheduler.execute([]() { debug("1"); }, 50); 14 | scheduler.execute([]() { debug("2"); }, 100); 15 | scheduler.execute([]() { debug("3"); }, 200); 16 | scheduler.execute([]() { debug("4"); }, 300); 17 | scheduler.execute([]() { debug("5"); }, 500); 18 | scheduler.execute([]() { debug("6"); }, 1000); 19 | 20 | scheduler.shutdown(); 21 | } 22 | 23 | Task simple_task2() { 24 | debug("task 2 start ..."); 25 | using namespace std::chrono_literals; 26 | std::this_thread::sleep_for(1s); 27 | debug("task 2 returns after 1s."); 28 | co_return 2; 29 | } 30 | 31 | Task simple_task3() { 32 | debug("in task 3 start ..."); 33 | using namespace std::chrono_literals; 34 | std::this_thread::sleep_for(2s); 35 | debug("task 3 returns after 2s."); 36 | co_return 3; 37 | } 38 | 39 | Task simple_task() { 40 | debug("task start ..."); 41 | auto result2 = co_await simple_task2(); 42 | debug("returns from task2: ", result2); 43 | auto result3 = co_await simple_task3(); 44 | debug("returns from task3: ", result3); 45 | co_return 1 + result2 + result3; 46 | } 47 | 48 | void test_tasks() { 49 | auto simpleTask = simple_task(); 50 | simpleTask.then([](int i) { 51 | debug("simple task end: ", i); 52 | }).catching([](std::exception &e) { 53 | debug("error occurred", e.what()); 54 | }); 55 | try { 56 | auto i = simpleTask.get_result(); 57 | debug("simple task end from get: ", i); 58 | } catch (std::exception &e) { 59 | debug("error: ", e.what()); 60 | } 61 | } 62 | 63 | int main() { 64 | test_tasks(); 65 | 66 | auto looper = LooperExecutor(); 67 | 68 | using namespace std::chrono_literals; 69 | std::this_thread::sleep_for(1s); 70 | looper.shutdown(false); 71 | std::this_thread::sleep_for(1s); 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /04.task/TaskPromise.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "coroutine_common.h" 14 | #include "Result.h" 15 | #include "TaskAwaiter.h" 16 | 17 | template 18 | class Task; 19 | 20 | template 21 | struct TaskPromise { 22 | std::suspend_never initial_suspend() { return {}; } 23 | 24 | std::suspend_always final_suspend() noexcept { return {}; } 25 | 26 | Task get_return_object() { 27 | return Task{std::coroutine_handle::from_promise(*this)}; 28 | } 29 | 30 | template 31 | TaskAwaiter<_ResultType> await_transform(Task<_ResultType> &&task) { 32 | return TaskAwaiter<_ResultType>(std::move(task)); 33 | } 34 | 35 | void unhandled_exception() { 36 | std::lock_guard lock(completion_lock); 37 | result = Result(std::current_exception()); 38 | completion.notify_all(); 39 | notify_callbacks(); 40 | } 41 | 42 | void return_value(ResultType value) { 43 | std::lock_guard lock(completion_lock); 44 | result = Result(std::move(value)); 45 | completion.notify_all(); 46 | notify_callbacks(); 47 | } 48 | 49 | ResultType get_result() { 50 | // blocking for result or throw on exception 51 | std::unique_lock lock(completion_lock); 52 | if (!result.has_value()) { 53 | completion.wait(lock); 54 | } 55 | return result->get_or_throw(); 56 | } 57 | 58 | void on_completed(std::function)> &&func) { 59 | std::unique_lock lock(completion_lock); 60 | if (result.has_value()) { 61 | auto value = result.value(); 62 | lock.unlock(); 63 | func(value); 64 | } else { 65 | completion_callbacks.push_back(func); 66 | } 67 | } 68 | 69 | private: 70 | std::optional> result; 71 | 72 | std::mutex completion_lock; 73 | std::condition_variable completion; 74 | 75 | std::list)>> completion_callbacks; 76 | 77 | void notify_callbacks() { 78 | auto value = result.value(); 79 | for (auto &callback : completion_callbacks) { 80 | callback(value); 81 | } 82 | completion_callbacks.clear(); 83 | } 84 | 85 | }; 86 | 87 | #endif //CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 88 | -------------------------------------------------------------------------------- /02.sequence.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/1/31. 3 | // 4 | #define __cpp_lib_coroutine 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct Generator { 11 | 12 | class ExhaustedException: std::exception { }; 13 | 14 | struct promise_type { 15 | int value; 16 | bool is_ready = false; 17 | 18 | std::suspend_never initial_suspend() { return {}; }; 19 | 20 | std::suspend_always final_suspend() noexcept { return {}; } 21 | 22 | std::suspend_always await_transform(int value) { 23 | this->value = value; 24 | is_ready = true; 25 | return {}; 26 | } 27 | 28 | void unhandled_exception() { 29 | 30 | } 31 | 32 | Generator get_return_object() { 33 | return Generator{ std::coroutine_handle::from_promise(*this) }; 34 | } 35 | 36 | void return_void() { } 37 | }; 38 | 39 | std::coroutine_handle handle; 40 | 41 | bool has_next() { 42 | if (!handle || handle.done()) { 43 | return false; 44 | } 45 | 46 | if (!handle.promise().is_ready) { 47 | handle.resume(); 48 | } 49 | 50 | if (handle.done()) { 51 | return false; 52 | } else { 53 | return true; 54 | } 55 | } 56 | 57 | int next() { 58 | if (has_next()) { 59 | handle.promise().is_ready = false; 60 | return handle.promise().value; 61 | } 62 | throw ExhaustedException(); 63 | } 64 | 65 | explicit Generator(std::coroutine_handle handle) noexcept 66 | : handle(handle) {} 67 | 68 | Generator(Generator &&generator) noexcept 69 | : handle(std::exchange(generator.handle, {})) {} 70 | 71 | Generator(Generator &) = delete; 72 | Generator &operator=(Generator &) = delete; 73 | 74 | ~Generator() { 75 | if (handle) handle.destroy(); 76 | } 77 | }; 78 | 79 | Generator sequence() { 80 | int i = 0; 81 | while (i < 5) { 82 | co_await i++; 83 | } 84 | } 85 | 86 | Generator returns_generator() { 87 | auto g = sequence(); 88 | if (g.has_next()) { 89 | std::cout << g.next() << std::endl; 90 | } 91 | return g; 92 | } 93 | 94 | int main() { 95 | auto generator = returns_generator(); 96 | for (int i = 0; i < 15; ++i) { 97 | if (generator.has_next()) { 98 | std::cout << generator.next() << std::endl; 99 | } else { 100 | break; 101 | } 102 | } 103 | 104 | return 0; 105 | } -------------------------------------------------------------------------------- /05.dispatcher/TaskPromise.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "coroutine_common.h" 14 | #include "Result.h" 15 | #include "DispatchAwaiter.h" 16 | #include "TaskAwaiter.h" 17 | 18 | template 19 | class Task; 20 | 21 | template 22 | struct TaskPromise { 23 | DispatchAwaiter initial_suspend() { return DispatchAwaiter{&executor}; } 24 | 25 | std::suspend_always final_suspend() noexcept { return {}; } 26 | 27 | Task get_return_object() { 28 | return Task{std::coroutine_handle::from_promise(*this)}; 29 | } 30 | 31 | template 32 | TaskAwaiter<_ResultType, _Executor> await_transform(Task<_ResultType, _Executor> &&task) { 33 | return TaskAwaiter<_ResultType, _Executor>(&executor, std::move(task)); 34 | } 35 | 36 | void unhandled_exception() { 37 | std::lock_guard lock(completion_lock); 38 | result = Result(std::current_exception()); 39 | completion.notify_all(); 40 | notify_callbacks(); 41 | } 42 | 43 | void return_value(ResultType value) { 44 | std::lock_guard lock(completion_lock); 45 | result = Result(std::move(value)); 46 | completion.notify_all(); 47 | notify_callbacks(); 48 | } 49 | 50 | ResultType get_result() { 51 | // blocking for result or throw on exception 52 | std::unique_lock lock(completion_lock); 53 | if (!result.has_value()) { 54 | completion.wait(lock); 55 | } 56 | return result->get_or_throw(); 57 | } 58 | 59 | void on_completed(std::function)> &&func) { 60 | std::unique_lock lock(completion_lock); 61 | if (result.has_value()) { 62 | auto value = result.value(); 63 | lock.unlock(); 64 | func(value); 65 | } else { 66 | completion_callbacks.push_back(func); 67 | } 68 | } 69 | 70 | private: 71 | std::optional> result; 72 | 73 | std::mutex completion_lock; 74 | std::condition_variable completion; 75 | 76 | std::list)>> completion_callbacks; 77 | 78 | Executor executor; 79 | 80 | void notify_callbacks() { 81 | auto value = result.value(); 82 | for (auto &callback : completion_callbacks) { 83 | callback(value); 84 | } 85 | completion_callbacks.clear(); 86 | } 87 | 88 | }; 89 | 90 | #endif //CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 91 | -------------------------------------------------------------------------------- /02.sequence_2.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/1/31. 3 | // 4 | #define __cpp_lib_coroutine 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct Generator { 11 | 12 | class ExhaustedException : std::exception {}; 13 | 14 | struct promise_type { 15 | int value; 16 | bool is_ready = false; 17 | 18 | std::suspend_never initial_suspend() { return {}; }; 19 | 20 | std::suspend_always final_suspend() noexcept { return {}; } 21 | 22 | std::suspend_always yield_value(int value) { 23 | this->value = value; 24 | is_ready = true; 25 | return {}; 26 | } 27 | 28 | void unhandled_exception() { 29 | 30 | } 31 | 32 | Generator get_return_object() { 33 | return Generator{std::coroutine_handle::from_promise(*this)}; 34 | } 35 | 36 | void return_void() {} 37 | }; 38 | 39 | std::coroutine_handle handle; 40 | 41 | bool has_next() { 42 | if (!handle || handle.done()) { 43 | return false; 44 | } 45 | 46 | if (!handle.promise().is_ready) { 47 | handle.resume(); 48 | } 49 | 50 | if (handle.done()) { 51 | return false; 52 | } else { 53 | return true; 54 | } 55 | } 56 | 57 | int next() { 58 | if (has_next()) { 59 | handle.promise().is_ready = false; 60 | return handle.promise().value; 61 | } 62 | throw ExhaustedException(); 63 | } 64 | 65 | explicit Generator(std::coroutine_handle handle) noexcept 66 | : handle(handle) {} 67 | 68 | Generator(Generator &&generator) noexcept 69 | : handle(std::exchange(generator.handle, {})) {} 70 | 71 | Generator(Generator &) = delete; 72 | Generator &operator=(Generator &) = delete; 73 | 74 | ~Generator() { 75 | if (handle) handle.destroy(); 76 | } 77 | }; 78 | 79 | Generator sequence() { 80 | int i = 0; 81 | while (i < 5) { 82 | co_yield i++; 83 | } 84 | } 85 | 86 | Generator fibonacci() { 87 | co_yield 0; 88 | co_yield 1; 89 | 90 | int a = 0; 91 | int b = 1; 92 | while (true) { 93 | co_yield a + b; 94 | b = a + b; 95 | a = b - a; 96 | } 97 | } 98 | 99 | class Fibonacci { 100 | public: 101 | int next() { 102 | if (a == -1) { 103 | a = 0; 104 | b = 1; 105 | return 0; 106 | } 107 | 108 | int next = b; 109 | b = a + b; 110 | a = b - a; 111 | return next; 112 | } 113 | 114 | private: 115 | int a = -1; 116 | int b = 0; 117 | }; 118 | 119 | int main() { 120 | auto generator = fibonacci(); 121 | auto fib = Fibonacci(); 122 | for (int i = 0; i < 10; ++i) { 123 | if (generator.has_next()) { 124 | std::cout << generator.next() << " " << fib.next() << std::endl; 125 | } else { 126 | break; 127 | } 128 | } 129 | return 0; 130 | } -------------------------------------------------------------------------------- /06.sleep/TaskPromise.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "coroutine_common.h" 14 | #include "Result.h" 15 | #include "DispatchAwaiter.h" 16 | #include "TaskAwaiter.h" 17 | #include "SleepAwaiter.h" 18 | 19 | template 20 | class Task; 21 | 22 | template 23 | struct TaskPromise { 24 | DispatchAwaiter initial_suspend() { return DispatchAwaiter{&executor}; } 25 | 26 | std::suspend_always final_suspend() noexcept { return {}; } 27 | 28 | Task get_return_object() { 29 | return Task{std::coroutine_handle::from_promise(*this)}; 30 | } 31 | 32 | template 33 | TaskAwaiter<_ResultType, _Executor> await_transform(Task<_ResultType, _Executor> &&task) { 34 | return TaskAwaiter<_ResultType, _Executor>(&executor, std::move(task)); 35 | } 36 | 37 | template 38 | SleepAwaiter await_transform(std::chrono::duration<_Rep, _Period> &&duration) { 39 | return SleepAwaiter(&executor, std::chrono::duration_cast(duration).count()); 40 | } 41 | 42 | void unhandled_exception() { 43 | std::lock_guard lock(completion_lock); 44 | result = Result(std::current_exception()); 45 | completion.notify_all(); 46 | notify_callbacks(); 47 | } 48 | 49 | void return_value(ResultType value) { 50 | std::lock_guard lock(completion_lock); 51 | result = Result(std::move(value)); 52 | completion.notify_all(); 53 | notify_callbacks(); 54 | } 55 | 56 | ResultType get_result() { 57 | // blocking for result or throw on exception 58 | std::unique_lock lock(completion_lock); 59 | if (!result.has_value()) { 60 | completion.wait(lock); 61 | } 62 | return result->get_or_throw(); 63 | } 64 | 65 | void on_completed(std::function)> &&func) { 66 | std::unique_lock lock(completion_lock); 67 | if (result.has_value()) { 68 | auto value = result.value(); 69 | lock.unlock(); 70 | func(value); 71 | } else { 72 | completion_callbacks.push_back(func); 73 | } 74 | } 75 | 76 | private: 77 | std::optional> result; 78 | 79 | std::mutex completion_lock; 80 | std::condition_variable completion; 81 | 82 | std::list)>> completion_callbacks; 83 | 84 | Executor executor; 85 | 86 | void notify_callbacks() { 87 | auto value = result.value(); 88 | for (auto &callback : completion_callbacks) { 89 | callback(value); 90 | } 91 | completion_callbacks.clear(); 92 | } 93 | 94 | }; 95 | 96 | #endif //CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 97 | -------------------------------------------------------------------------------- /07.channel/ChannelAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/21. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 7 | 8 | #include "coroutine_common.h" 9 | 10 | template 11 | struct Channel; 12 | 13 | template 14 | struct WriterAwaiter { 15 | Channel *channel; 16 | AbstractExecutor *executor = nullptr; 17 | ValueType _value; 18 | std::coroutine_handle<> handle; 19 | 20 | WriterAwaiter(Channel *channel, ValueType value) 21 | : channel(channel), _value(value) {} 22 | 23 | WriterAwaiter(WriterAwaiter &&other) noexcept 24 | : channel(std::exchange(other.channel, nullptr)), 25 | executor(std::exchange(other.executor, nullptr)), 26 | _value(other._value), 27 | handle(other.handle) {} 28 | 29 | 30 | bool await_ready() { 31 | return false; 32 | } 33 | 34 | auto await_suspend(std::coroutine_handle<> coroutine_handle) { 35 | this->handle = coroutine_handle; 36 | channel->try_push_writer(this); 37 | } 38 | 39 | void await_resume() { 40 | channel->check_closed(); 41 | channel = nullptr; 42 | } 43 | 44 | void resume() { 45 | if (executor) { 46 | executor->execute([this]() { handle.resume(); }); 47 | } else { 48 | handle.resume(); 49 | } 50 | } 51 | 52 | ~WriterAwaiter() { 53 | if (channel) channel->remove_writer(this); 54 | } 55 | }; 56 | 57 | template 58 | struct ReaderAwaiter { 59 | Channel *channel; 60 | AbstractExecutor *executor = nullptr; 61 | ValueType _value; 62 | ValueType *p_value = nullptr; 63 | std::coroutine_handle<> handle; 64 | 65 | explicit ReaderAwaiter(Channel *channel) : channel(channel) {} 66 | 67 | ReaderAwaiter(ReaderAwaiter &&other) noexcept 68 | : channel(std::exchange(other.channel, nullptr)), 69 | executor(std::exchange(other.executor, nullptr)), 70 | _value(other._value), 71 | p_value(std::exchange(other.p_value, nullptr)), 72 | handle(other.handle) {} 73 | 74 | bool await_ready() { return false; } 75 | 76 | auto await_suspend(std::coroutine_handle<> coroutine_handle) { 77 | this->handle = coroutine_handle; 78 | channel->try_push_reader(this); 79 | } 80 | 81 | int await_resume() { 82 | auto channel = this->channel; 83 | this->channel = nullptr; 84 | channel->check_closed(); 85 | return _value; 86 | } 87 | 88 | void resume(ValueType value) { 89 | this->_value = value; 90 | if (p_value) { 91 | *p_value = value; 92 | } 93 | resume(); 94 | } 95 | 96 | void resume() { 97 | if (executor) { 98 | executor->execute([this]() { handle.resume(); }); 99 | } else { 100 | handle.resume(); 101 | } 102 | } 103 | 104 | ~ReaderAwaiter() { 105 | if (channel) channel->remove_reader(this); 106 | } 107 | }; 108 | 109 | #endif //CPPCOROUTINES_TASKS_07_CHANNEL_CHANNELAWAITER_H_ 110 | -------------------------------------------------------------------------------- /09.http/CommonAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/26. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_08_AWAITER_COMMONAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_08_AWAITER_COMMONAWAITER_H_ 7 | 8 | #include "Executor.h" 9 | #include "Result.h" 10 | #include "coroutine_common.h" 11 | 12 | template 13 | struct Awaiter { 14 | 15 | using ResultType = R; 16 | 17 | bool await_ready() const { return false; } 18 | 19 | void await_suspend(std::coroutine_handle<> handle) { 20 | this->_handle = handle; 21 | after_suspend(); 22 | } 23 | 24 | R await_resume() { 25 | before_resume(); 26 | return _result->get_or_throw(); 27 | } 28 | 29 | void resume(R value) { 30 | dispatch([this, value]() { 31 | _result = Result(static_cast(value)); 32 | _handle.resume(); 33 | }); 34 | } 35 | 36 | void resume_unsafe() { 37 | dispatch([this]() { _handle.resume(); }); 38 | } 39 | 40 | void resume_exception(std::exception_ptr &&e) { 41 | dispatch([this, e]() { 42 | _result = Result(static_cast(e)); 43 | _handle.resume(); 44 | }); 45 | } 46 | 47 | void install_executor(AbstractExecutor *executor) { 48 | _executor = executor; 49 | } 50 | 51 | protected: 52 | std::optional> _result{}; 53 | 54 | virtual void after_suspend() {} 55 | 56 | virtual void before_resume() {} 57 | private: 58 | AbstractExecutor *_executor = nullptr; 59 | std::coroutine_handle<> _handle = nullptr; 60 | 61 | void dispatch(std::function &&f) { 62 | if (_executor) { 63 | _executor->execute(std::move(f)); 64 | } else { 65 | f(); 66 | } 67 | } 68 | }; 69 | 70 | template<> 71 | struct Awaiter { 72 | 73 | using ResultType = void; 74 | 75 | bool await_ready() { return false; } 76 | 77 | void await_suspend(std::coroutine_handle<> handle) { 78 | this->_handle = handle; 79 | after_suspend(); 80 | } 81 | 82 | void await_resume() { 83 | before_resume(); 84 | _result->get_or_throw(); 85 | } 86 | 87 | void resume() { 88 | dispatch([this]() { 89 | _result = Result(); 90 | _handle.resume(); 91 | }); 92 | } 93 | 94 | void resume_exception(std::exception_ptr &&e) { 95 | dispatch([this, e]() { 96 | _result = Result(static_cast(e)); 97 | _handle.resume(); 98 | }); 99 | } 100 | 101 | void install_executor(AbstractExecutor *executor) { 102 | _executor = executor; 103 | } 104 | 105 | virtual void after_suspend() {} 106 | 107 | virtual void before_resume() {} 108 | 109 | private: 110 | std::optional> _result{}; 111 | AbstractExecutor *_executor = nullptr; 112 | std::coroutine_handle<> _handle = nullptr; 113 | 114 | void dispatch(std::function &&f) { 115 | if (_executor) { 116 | _executor->execute(std::move(f)); 117 | } else { 118 | f(); 119 | } 120 | } 121 | }; 122 | 123 | #endif //CPPCOROUTINES_TASKS_08_AWAITER_COMMONAWAITER_H_ 124 | -------------------------------------------------------------------------------- /07.channel/Task.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASK_H_ 6 | #define CPPCOROUTINES_04_TASK_TASK_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "TaskPromise.h" 10 | 11 | template 12 | struct Task { 13 | 14 | using promise_type = TaskPromise; 15 | 16 | ResultType get_result() { 17 | return handle.promise().get_result(); 18 | } 19 | 20 | Task &then(std::function &&func) { 21 | handle.promise().on_completed([func](auto result) { 22 | try { 23 | func(result.get_or_throw()); 24 | } catch (std::exception &e) { 25 | // ignore. 26 | } 27 | }); 28 | return *this; 29 | } 30 | 31 | Task &catching(std::function &&func) { 32 | handle.promise().on_completed([func](auto result) { 33 | try { 34 | result.get_or_throw(); 35 | } catch (std::exception &e) { 36 | func(e); 37 | } 38 | }); 39 | return *this; 40 | } 41 | 42 | Task &finally(std::function &&func) { 43 | handle.promise().on_completed([func](auto result) { func(); }); 44 | return *this; 45 | } 46 | 47 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 48 | 49 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 50 | 51 | Task(Task &) = delete; 52 | 53 | Task &operator=(Task &) = delete; 54 | 55 | ~Task() { 56 | if (handle) handle.destroy(); 57 | } 58 | 59 | private: 60 | std::coroutine_handle handle; 61 | }; 62 | 63 | template 64 | struct Task { 65 | 66 | using promise_type = TaskPromise; 67 | 68 | void get_result() { 69 | handle.promise().get_result(); 70 | } 71 | 72 | Task &then(std::function &&func) { 73 | handle.promise().on_completed([func](auto result) { 74 | try { 75 | result.get_or_throw(); 76 | func(); 77 | } catch (std::exception &e) { 78 | // ignore. 79 | } 80 | }); 81 | return *this; 82 | } 83 | 84 | Task &catching(std::function &&func) { 85 | handle.promise().on_completed([func](auto result) { 86 | try { 87 | result.get_or_throw(); 88 | } catch (std::exception &e) { 89 | func(e); 90 | } 91 | }); 92 | return *this; 93 | } 94 | 95 | Task &finally(std::function &&func) { 96 | handle.promise().on_completed([func](auto result) { func(); }); 97 | return *this; 98 | } 99 | 100 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 101 | 102 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 103 | 104 | Task(Task &) = delete; 105 | 106 | Task &operator=(Task &) = delete; 107 | 108 | ~Task() { 109 | if (handle) handle.destroy(); 110 | } 111 | 112 | private: 113 | std::coroutine_handle handle; 114 | }; 115 | 116 | 117 | #endif //CPPCOROUTINES_04_TASK_TASK_H_ 118 | -------------------------------------------------------------------------------- /08.awaiter/CommonAwaiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/26. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_08_AWAITER_COMMONAWAITER_H_ 6 | #define CPPCOROUTINES_TASKS_08_AWAITER_COMMONAWAITER_H_ 7 | 8 | #include "Executor.h" 9 | #include "Result.h" 10 | #include "coroutine_common.h" 11 | 12 | template 13 | struct Awaiter { 14 | 15 | using ResultType = R; 16 | 17 | bool await_ready() const { return false; } 18 | 19 | void await_suspend(std::coroutine_handle<> handle) { 20 | this->_handle = handle; 21 | after_suspend(); 22 | } 23 | 24 | R await_resume() { 25 | before_resume(); 26 | return _result->get_or_throw(); 27 | } 28 | 29 | void resume(R value) { 30 | dispatch([this, value]() { 31 | _result = Result(static_cast(value)); 32 | _handle.resume(); 33 | }); 34 | } 35 | 36 | void resume_unsafe() { 37 | dispatch([this]() { _handle.resume(); }); 38 | } 39 | 40 | void resume_exception(std::exception_ptr &&e) { 41 | dispatch([this, e]() { 42 | _result = Result(static_cast(e)); 43 | _handle.resume(); 44 | }); 45 | } 46 | 47 | void install_executor(AbstractExecutor *executor) { 48 | _executor = executor; 49 | } 50 | 51 | protected: 52 | std::optional> _result{}; 53 | 54 | virtual void after_suspend() {} 55 | 56 | virtual void before_resume() {} 57 | private: 58 | AbstractExecutor *_executor = nullptr; 59 | std::coroutine_handle<> _handle = nullptr; 60 | 61 | void dispatch(std::function &&f) { 62 | if (_executor) { 63 | _executor->execute(std::move(f)); 64 | } else { 65 | f(); 66 | } 67 | } 68 | }; 69 | 70 | template<> 71 | struct Awaiter { 72 | 73 | using ResultType = void; 74 | 75 | bool await_ready() { return false; } 76 | 77 | void await_suspend(std::coroutine_handle<> handle) { 78 | this->_handle = handle; 79 | after_suspend(); 80 | } 81 | 82 | void await_resume() { 83 | before_resume(); 84 | _result->get_or_throw(); 85 | } 86 | 87 | void resume() { 88 | dispatch([this]() { 89 | _result = Result(); 90 | _handle.resume(); 91 | }); 92 | } 93 | 94 | void resume_unsafe() { 95 | dispatch([this]() { _handle.resume(); }); 96 | } 97 | 98 | void resume_exception(std::exception_ptr &&e) { 99 | dispatch([this, e]() { 100 | _result = Result(static_cast(e)); 101 | _handle.resume(); 102 | }); 103 | } 104 | 105 | void install_executor(AbstractExecutor *executor) { 106 | _executor = executor; 107 | } 108 | 109 | virtual void after_suspend() {} 110 | 111 | virtual void before_resume() {} 112 | 113 | private: 114 | std::optional> _result{}; 115 | AbstractExecutor *_executor = nullptr; 116 | std::coroutine_handle<> _handle = nullptr; 117 | 118 | void dispatch(std::function &&f) { 119 | if (_executor) { 120 | _executor->execute(std::move(f)); 121 | } else { 122 | f(); 123 | } 124 | } 125 | }; 126 | 127 | #endif //CPPCOROUTINES_TASKS_08_AWAITER_COMMONAWAITER_H_ 128 | -------------------------------------------------------------------------------- /09.http/Task.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASK_H_ 6 | #define CPPCOROUTINES_04_TASK_TASK_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "TaskPromise.h" 10 | 11 | template 12 | struct Task { 13 | 14 | using promise_type = TaskPromise; 15 | 16 | auto as_awaiter() { 17 | return TaskAwaiter(std::move(*this)); 18 | } 19 | 20 | ResultType get_result() { 21 | return handle.promise().get_result(); 22 | } 23 | 24 | Task &then(std::function &&func) { 25 | handle.promise().on_completed([func](auto result) { 26 | try { 27 | func(result.get_or_throw()); 28 | } catch (std::exception &e) { 29 | // ignore. 30 | } 31 | }); 32 | return *this; 33 | } 34 | 35 | Task &catching(std::function &&func) { 36 | handle.promise().on_completed([func](auto result) { 37 | try { 38 | result.get_or_throw(); 39 | } catch (std::exception &e) { 40 | func(e); 41 | } 42 | }); 43 | return *this; 44 | } 45 | 46 | Task &finally(std::function &&func) { 47 | handle.promise().on_completed([func](auto result) { func(); }); 48 | return *this; 49 | } 50 | 51 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 52 | 53 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 54 | 55 | Task(Task &) = delete; 56 | 57 | Task &operator=(Task &) = delete; 58 | 59 | ~Task() { 60 | if (handle) handle.destroy(); 61 | } 62 | 63 | private: 64 | std::coroutine_handle handle; 65 | }; 66 | 67 | template 68 | struct Task { 69 | 70 | using promise_type = TaskPromise; 71 | 72 | auto as_awaiter() { 73 | return TaskAwaiter(std::move(*this)); 74 | } 75 | 76 | void get_result() { 77 | handle.promise().get_result(); 78 | } 79 | 80 | Task &then(std::function &&func) { 81 | handle.promise().on_completed([func](auto result) { 82 | try { 83 | result.get_or_throw(); 84 | func(); 85 | } catch (std::exception &e) { 86 | // ignore. 87 | } 88 | }); 89 | return *this; 90 | } 91 | 92 | Task &catching(std::function &&func) { 93 | handle.promise().on_completed([func](auto result) { 94 | try { 95 | result.get_or_throw(); 96 | } catch (std::exception &e) { 97 | func(e); 98 | } 99 | }); 100 | return *this; 101 | } 102 | 103 | Task &finally(std::function &&func) { 104 | handle.promise().on_completed([func](auto result) { func(); }); 105 | return *this; 106 | } 107 | 108 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 109 | 110 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 111 | 112 | Task(Task &) = delete; 113 | 114 | Task &operator=(Task &) = delete; 115 | 116 | ~Task() { 117 | if (handle) handle.destroy(); 118 | } 119 | 120 | private: 121 | std::coroutine_handle handle; 122 | }; 123 | 124 | 125 | #endif //CPPCOROUTINES_04_TASK_TASK_H_ 126 | -------------------------------------------------------------------------------- /08.awaiter/Task.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_TASK_H_ 6 | #define CPPCOROUTINES_04_TASK_TASK_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "TaskPromise.h" 10 | 11 | template 12 | struct Task { 13 | 14 | using promise_type = TaskPromise; 15 | 16 | auto as_awaiter() { 17 | return TaskAwaiter(std::move(*this)); 18 | } 19 | 20 | ResultType get_result() { 21 | return handle.promise().get_result(); 22 | } 23 | 24 | Task &then(std::function &&func) { 25 | handle.promise().on_completed([func](auto result) { 26 | try { 27 | func(result.get_or_throw()); 28 | } catch (std::exception &e) { 29 | // ignore. 30 | } 31 | }); 32 | return *this; 33 | } 34 | 35 | Task &catching(std::function &&func) { 36 | handle.promise().on_completed([func](auto result) { 37 | try { 38 | result.get_or_throw(); 39 | } catch (std::exception &e) { 40 | func(e); 41 | } 42 | }); 43 | return *this; 44 | } 45 | 46 | Task &finally(std::function &&func) { 47 | handle.promise().on_completed([func](auto result) { func(); }); 48 | return *this; 49 | } 50 | 51 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 52 | 53 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 54 | 55 | Task(Task &) = delete; 56 | 57 | Task &operator=(Task &) = delete; 58 | 59 | ~Task() { 60 | if (handle) handle.destroy(); 61 | } 62 | 63 | private: 64 | std::coroutine_handle handle; 65 | }; 66 | 67 | template 68 | struct Task { 69 | 70 | using promise_type = TaskPromise; 71 | 72 | auto as_awaiter() { 73 | return TaskAwaiter(std::move(*this)); 74 | } 75 | 76 | void get_result() { 77 | handle.promise().get_result(); 78 | } 79 | 80 | Task &then(std::function &&func) { 81 | handle.promise().on_completed([func](auto result) { 82 | try { 83 | result.get_or_throw(); 84 | func(); 85 | } catch (std::exception &e) { 86 | // ignore. 87 | } 88 | }); 89 | return *this; 90 | } 91 | 92 | Task &catching(std::function &&func) { 93 | handle.promise().on_completed([func](auto result) { 94 | try { 95 | result.get_or_throw(); 96 | } catch (std::exception &e) { 97 | func(e); 98 | } 99 | }); 100 | return *this; 101 | } 102 | 103 | Task &finally(std::function &&func) { 104 | handle.promise().on_completed([func](auto result) { func(); }); 105 | return *this; 106 | } 107 | 108 | explicit Task(std::coroutine_handle handle) noexcept: handle(handle) {} 109 | 110 | Task(Task &&task) noexcept: handle(std::exchange(task.handle, {})) {} 111 | 112 | Task(Task &) = delete; 113 | 114 | Task &operator=(Task &) = delete; 115 | 116 | ~Task() { 117 | if (handle) handle.destroy(); 118 | } 119 | 120 | private: 121 | std::coroutine_handle handle; 122 | }; 123 | 124 | 125 | #endif //CPPCOROUTINES_04_TASK_TASK_H_ 126 | -------------------------------------------------------------------------------- /05.dispatcher/Scheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/18. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "io_utils.h" 15 | 16 | class DelayedExecutable { 17 | public: 18 | DelayedExecutable(std::function &&func, long long delay) : func(std::move(func)) { 19 | using namespace std; 20 | using namespace std::chrono; 21 | auto now = system_clock::now(); 22 | auto current = duration_cast(now.time_since_epoch()).count(); 23 | 24 | scheduled_time = current + delay; 25 | } 26 | 27 | long long delay() const { 28 | using namespace std; 29 | using namespace std::chrono; 30 | 31 | auto now = system_clock::now(); 32 | auto current = duration_cast(now.time_since_epoch()).count(); 33 | return scheduled_time - current; 34 | } 35 | 36 | long long get_scheduled_time() const { 37 | return scheduled_time; 38 | } 39 | 40 | void operator()() { 41 | func(); 42 | } 43 | 44 | private: 45 | long long scheduled_time; 46 | std::function func; 47 | }; 48 | 49 | class DelayedExecutableCompare { 50 | public: 51 | bool operator()(DelayedExecutable &left, DelayedExecutable &right) { 52 | return left.get_scheduled_time() > right.get_scheduled_time(); 53 | } 54 | }; 55 | 56 | class Scheduler { 57 | private: 58 | std::condition_variable queue_condition; 59 | std::mutex queue_lock; 60 | std::priority_queue, DelayedExecutableCompare> executable_queue; 61 | 62 | volatile bool is_active = true; 63 | std::thread work_thread; 64 | public: 65 | 66 | Scheduler() { 67 | work_thread = std::thread([this]() { 68 | while (is_active || !executable_queue.empty()) { 69 | std::unique_lock lock(queue_lock); 70 | if (executable_queue.empty()) { 71 | queue_condition.wait(lock); 72 | } 73 | auto executable = executable_queue.top(); 74 | long long delay = executable.delay(); 75 | if (delay > 0) { 76 | auto status = queue_condition.wait_for(lock, std::chrono::milliseconds(delay)); 77 | if (status != std::cv_status::timeout) { 78 | // a new executable should be executed before. 79 | continue; 80 | } 81 | } 82 | executable_queue.pop(); 83 | lock.unlock(); 84 | executable(); 85 | } 86 | }); 87 | } 88 | 89 | ~Scheduler() { 90 | shutdown(false); 91 | } 92 | 93 | void execute(std::function &&func, long delay) { 94 | std::lock_guard lock(queue_lock); 95 | if (is_active) { 96 | executable_queue.push(DelayedExecutable(std::move(func), delay)); 97 | queue_condition.notify_one(); 98 | } 99 | } 100 | 101 | void shutdown(bool wait_for_complete = true) { 102 | is_active = false; 103 | if (wait_for_complete) { 104 | if (work_thread.joinable()) { 105 | work_thread.join(); 106 | } 107 | } else { 108 | std::lock_guard lock(queue_lock); 109 | // clear queue. 110 | decltype(executable_queue) empty_queue; 111 | std::swap(executable_queue, empty_queue); 112 | if (work_thread.joinable()) { 113 | work_thread.detach(); 114 | } 115 | } 116 | } 117 | 118 | }; 119 | 120 | #endif //CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 121 | -------------------------------------------------------------------------------- /06.sleep/Executor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_EXECUTOR_H_ 6 | #define CPPCOROUTINES_04_TASK_EXECUTOR_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "io_utils.h" 14 | 15 | class AbstractExecutor { 16 | public: 17 | virtual void execute(std::function &&func) = 0; 18 | }; 19 | 20 | class NoopExecutor : public AbstractExecutor { 21 | public: 22 | void execute(std::function &&func) override { 23 | func(); 24 | } 25 | }; 26 | 27 | class NewThreadExecutor : public AbstractExecutor { 28 | public: 29 | void execute(std::function &&func) override { 30 | std::thread(func).detach(); 31 | } 32 | }; 33 | 34 | class AsyncExecutor : public AbstractExecutor { 35 | public: 36 | void execute(std::function &&func) override { 37 | std::unique_lock lock(future_lock); 38 | auto id = nextId++; 39 | lock.unlock(); 40 | 41 | auto future = std::async([this, id, func](){ 42 | func(); 43 | 44 | std::unique_lock lock(future_lock); 45 | // move future to stack so that it will be destroyed after unlocked. 46 | auto f = std::move(this->futures[id]); 47 | this->futures.erase(id); 48 | lock.unlock(); 49 | }); 50 | 51 | lock.lock(); 52 | this->futures[id] = std::move(future); 53 | lock.unlock(); 54 | } 55 | private: 56 | std::mutex future_lock; 57 | int nextId = 0; 58 | std::map> futures{}; 59 | }; 60 | 61 | class LooperExecutor : public AbstractExecutor { 62 | private: 63 | std::condition_variable queue_condition; 64 | std::mutex queue_lock; 65 | std::queue> executable_queue; 66 | 67 | std::atomic is_active; 68 | std::thread work_thread; 69 | 70 | void run_loop() { 71 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 72 | std::unique_lock lock(queue_lock); 73 | if (executable_queue.empty()) { 74 | queue_condition.wait(lock); 75 | if (executable_queue.empty()) { 76 | continue; 77 | } 78 | } 79 | auto func = executable_queue.front(); 80 | executable_queue.pop(); 81 | lock.unlock(); 82 | 83 | func(); 84 | } 85 | debug("run_loop exit."); 86 | } 87 | 88 | public: 89 | 90 | LooperExecutor() { 91 | is_active.store(true, std::memory_order_relaxed); 92 | work_thread = std::thread(&LooperExecutor::run_loop, this); 93 | } 94 | 95 | ~LooperExecutor() { 96 | shutdown(false); 97 | if (work_thread.joinable()) { 98 | work_thread.join(); 99 | } 100 | } 101 | 102 | void execute(std::function &&func) override { 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | executable_queue.push(func); 106 | lock.unlock(); 107 | queue_condition.notify_one(); 108 | } 109 | } 110 | 111 | void shutdown(bool wait_for_complete = true) { 112 | is_active.store(false, std::memory_order_relaxed); 113 | if (!wait_for_complete) { 114 | // clear queue. 115 | std::unique_lock lock(queue_lock); 116 | decltype(executable_queue) empty_queue; 117 | std::swap(executable_queue, empty_queue); 118 | lock.unlock(); 119 | } 120 | 121 | queue_condition.notify_all(); 122 | } 123 | }; 124 | 125 | class SharedLooperExecutor : public AbstractExecutor { 126 | public: 127 | void execute(std::function &&func) override { 128 | static LooperExecutor sharedLooperExecutor; 129 | sharedLooperExecutor.execute(std::move(func)); 130 | } 131 | }; 132 | 133 | #endif //CPPCOROUTINES_04_TASK_EXECUTOR_H_ 134 | -------------------------------------------------------------------------------- /09.http/Executor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_EXECUTOR_H_ 6 | #define CPPCOROUTINES_04_TASK_EXECUTOR_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "io_utils.h" 14 | 15 | class AbstractExecutor { 16 | public: 17 | virtual void execute(std::function &&func) = 0; 18 | }; 19 | 20 | class NoopExecutor : public AbstractExecutor { 21 | public: 22 | void execute(std::function &&func) override { 23 | func(); 24 | } 25 | }; 26 | 27 | class NewThreadExecutor : public AbstractExecutor { 28 | public: 29 | void execute(std::function &&func) override { 30 | std::thread(func).detach(); 31 | } 32 | }; 33 | 34 | class AsyncExecutor : public AbstractExecutor { 35 | public: 36 | void execute(std::function &&func) override { 37 | std::unique_lock lock(future_lock); 38 | auto id = nextId++; 39 | lock.unlock(); 40 | 41 | auto future = std::async([this, id, func](){ 42 | func(); 43 | 44 | std::unique_lock lock(future_lock); 45 | // move future to stack so that it will be destroyed after unlocked. 46 | auto f = std::move(this->futures[id]); 47 | this->futures.erase(id); 48 | lock.unlock(); 49 | }); 50 | 51 | lock.lock(); 52 | this->futures[id] = std::move(future); 53 | lock.unlock(); 54 | } 55 | private: 56 | std::mutex future_lock; 57 | int nextId = 0; 58 | std::map> futures{}; 59 | }; 60 | 61 | class LooperExecutor : public AbstractExecutor { 62 | private: 63 | std::condition_variable queue_condition; 64 | std::mutex queue_lock; 65 | std::queue> executable_queue; 66 | 67 | std::atomic is_active; 68 | std::thread work_thread; 69 | 70 | void run_loop() { 71 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 72 | std::unique_lock lock(queue_lock); 73 | if (executable_queue.empty()) { 74 | queue_condition.wait(lock); 75 | if (executable_queue.empty()) { 76 | continue; 77 | } 78 | } 79 | auto func = executable_queue.front(); 80 | executable_queue.pop(); 81 | lock.unlock(); 82 | 83 | func(); 84 | } 85 | debug("run_loop exit."); 86 | } 87 | 88 | public: 89 | 90 | LooperExecutor() { 91 | is_active.store(true, std::memory_order_relaxed); 92 | work_thread = std::thread(&LooperExecutor::run_loop, this); 93 | } 94 | 95 | ~LooperExecutor() { 96 | shutdown(false); 97 | if (work_thread.joinable()) { 98 | work_thread.join(); 99 | } 100 | } 101 | 102 | void execute(std::function &&func) override { 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | executable_queue.push(func); 106 | lock.unlock(); 107 | queue_condition.notify_one(); 108 | } 109 | } 110 | 111 | void shutdown(bool wait_for_complete = true) { 112 | is_active.store(false, std::memory_order_relaxed); 113 | if (!wait_for_complete) { 114 | // clear queue. 115 | std::unique_lock lock(queue_lock); 116 | decltype(executable_queue) empty_queue; 117 | std::swap(executable_queue, empty_queue); 118 | lock.unlock(); 119 | } 120 | 121 | queue_condition.notify_all(); 122 | } 123 | }; 124 | 125 | class SharedLooperExecutor : public AbstractExecutor { 126 | public: 127 | void execute(std::function &&func) override { 128 | static LooperExecutor sharedLooperExecutor; 129 | sharedLooperExecutor.execute(std::move(func)); 130 | } 131 | }; 132 | 133 | #endif //CPPCOROUTINES_04_TASK_EXECUTOR_H_ 134 | -------------------------------------------------------------------------------- /05.dispatcher/Executor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_EXECUTOR_H_ 6 | #define CPPCOROUTINES_04_TASK_EXECUTOR_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "io_utils.h" 14 | 15 | class AbstractExecutor { 16 | public: 17 | virtual void execute(std::function &&func) = 0; 18 | }; 19 | 20 | class NoopExecutor : public AbstractExecutor { 21 | public: 22 | void execute(std::function &&func) override { 23 | func(); 24 | } 25 | }; 26 | 27 | class NewThreadExecutor : public AbstractExecutor { 28 | public: 29 | void execute(std::function &&func) override { 30 | std::thread(func).detach(); 31 | } 32 | }; 33 | 34 | class AsyncExecutor : public AbstractExecutor { 35 | public: 36 | void execute(std::function &&func) override { 37 | std::unique_lock lock(future_lock); 38 | auto id = nextId++; 39 | lock.unlock(); 40 | 41 | auto future = std::async([this, id, func](){ 42 | func(); 43 | 44 | std::unique_lock lock(future_lock); 45 | // move future to stack so that it will be destroyed after unlocked. 46 | auto f = std::move(this->futures[id]); 47 | this->futures.erase(id); 48 | lock.unlock(); 49 | }); 50 | 51 | lock.lock(); 52 | this->futures[id] = std::move(future); 53 | lock.unlock(); 54 | } 55 | private: 56 | std::mutex future_lock; 57 | int nextId = 0; 58 | std::map> futures{}; 59 | }; 60 | 61 | class LooperExecutor : public AbstractExecutor { 62 | private: 63 | std::condition_variable queue_condition; 64 | std::mutex queue_lock; 65 | std::queue> executable_queue; 66 | 67 | std::atomic is_active; 68 | std::thread work_thread; 69 | 70 | void run_loop() { 71 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 72 | std::unique_lock lock(queue_lock); 73 | if (executable_queue.empty()) { 74 | queue_condition.wait(lock); 75 | if (executable_queue.empty()) { 76 | continue; 77 | } 78 | } 79 | auto func = executable_queue.front(); 80 | executable_queue.pop(); 81 | lock.unlock(); 82 | 83 | func(); 84 | } 85 | debug("run_loop exit."); 86 | } 87 | 88 | public: 89 | 90 | LooperExecutor() { 91 | is_active.store(true, std::memory_order_relaxed); 92 | work_thread = std::thread(&LooperExecutor::run_loop, this); 93 | } 94 | 95 | ~LooperExecutor() { 96 | shutdown(false); 97 | if (work_thread.joinable()) { 98 | work_thread.join(); 99 | } 100 | } 101 | 102 | void execute(std::function &&func) override { 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | executable_queue.push(func); 106 | lock.unlock(); 107 | queue_condition.notify_one(); 108 | } 109 | } 110 | 111 | void shutdown(bool wait_for_complete = true) { 112 | is_active.store(false, std::memory_order_relaxed); 113 | if (!wait_for_complete) { 114 | // clear queue. 115 | std::unique_lock lock(queue_lock); 116 | decltype(executable_queue) empty_queue; 117 | std::swap(executable_queue, empty_queue); 118 | lock.unlock(); 119 | } 120 | 121 | queue_condition.notify_all(); 122 | } 123 | }; 124 | 125 | class SharedLooperExecutor : public AbstractExecutor { 126 | public: 127 | void execute(std::function &&func) override { 128 | static LooperExecutor sharedLooperExecutor; 129 | sharedLooperExecutor.execute(std::move(func)); 130 | } 131 | }; 132 | 133 | #endif //CPPCOROUTINES_04_TASK_EXECUTOR_H_ 134 | -------------------------------------------------------------------------------- /07.channel/Executor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_EXECUTOR_H_ 6 | #define CPPCOROUTINES_04_TASK_EXECUTOR_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "io_utils.h" 14 | 15 | class AbstractExecutor { 16 | public: 17 | virtual void execute(std::function &&func) = 0; 18 | }; 19 | 20 | class NoopExecutor : public AbstractExecutor { 21 | public: 22 | void execute(std::function &&func) override { 23 | func(); 24 | } 25 | }; 26 | 27 | class NewThreadExecutor : public AbstractExecutor { 28 | public: 29 | void execute(std::function &&func) override { 30 | std::thread(func).detach(); 31 | } 32 | }; 33 | 34 | class AsyncExecutor : public AbstractExecutor { 35 | public: 36 | void execute(std::function &&func) override { 37 | std::unique_lock lock(future_lock); 38 | auto id = nextId++; 39 | lock.unlock(); 40 | 41 | auto future = std::async([this, id, func](){ 42 | func(); 43 | 44 | std::unique_lock lock(future_lock); 45 | // move future to stack so that it will be destroyed after unlocked. 46 | auto f = std::move(this->futures[id]); 47 | this->futures.erase(id); 48 | lock.unlock(); 49 | }); 50 | 51 | lock.lock(); 52 | this->futures[id] = std::move(future); 53 | lock.unlock(); 54 | } 55 | private: 56 | std::mutex future_lock; 57 | int nextId = 0; 58 | std::map> futures{}; 59 | }; 60 | 61 | class LooperExecutor : public AbstractExecutor { 62 | private: 63 | std::condition_variable queue_condition; 64 | std::mutex queue_lock; 65 | std::queue> executable_queue; 66 | 67 | std::atomic is_active; 68 | std::thread work_thread; 69 | 70 | void run_loop() { 71 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 72 | std::unique_lock lock(queue_lock); 73 | if (executable_queue.empty()) { 74 | queue_condition.wait(lock); 75 | if (executable_queue.empty()) { 76 | continue; 77 | } 78 | } 79 | auto func = executable_queue.front(); 80 | executable_queue.pop(); 81 | lock.unlock(); 82 | 83 | func(); 84 | } 85 | debug("run_loop exit."); 86 | } 87 | 88 | public: 89 | 90 | LooperExecutor() { 91 | is_active.store(true, std::memory_order_relaxed); 92 | work_thread = std::thread(&LooperExecutor::run_loop, this); 93 | } 94 | 95 | ~LooperExecutor() { 96 | shutdown(false); 97 | if (work_thread.joinable()) { 98 | work_thread.join(); 99 | } 100 | } 101 | 102 | void execute(std::function &&func) override { 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | executable_queue.push(func); 106 | lock.unlock(); 107 | queue_condition.notify_one(); 108 | } 109 | } 110 | 111 | void shutdown(bool wait_for_complete = true) { 112 | is_active.store(false, std::memory_order_relaxed); 113 | if (!wait_for_complete) { 114 | // clear queue. 115 | std::unique_lock lock(queue_lock); 116 | decltype(executable_queue) empty_queue; 117 | std::swap(executable_queue, empty_queue); 118 | lock.unlock(); 119 | } 120 | 121 | queue_condition.notify_all(); 122 | } 123 | }; 124 | 125 | class SharedLooperExecutor : public AbstractExecutor { 126 | public: 127 | void execute(std::function &&func) override { 128 | static LooperExecutor sharedLooperExecutor; 129 | sharedLooperExecutor.execute(std::move(func)); 130 | } 131 | }; 132 | 133 | #endif //CPPCOROUTINES_04_TASK_EXECUTOR_H_ 134 | -------------------------------------------------------------------------------- /08.awaiter/Executor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_04_TASK_EXECUTOR_H_ 6 | #define CPPCOROUTINES_04_TASK_EXECUTOR_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "io_utils.h" 14 | 15 | class AbstractExecutor { 16 | public: 17 | virtual void execute(std::function &&func) = 0; 18 | }; 19 | 20 | class NoopExecutor : public AbstractExecutor { 21 | public: 22 | void execute(std::function &&func) override { 23 | func(); 24 | } 25 | }; 26 | 27 | class NewThreadExecutor : public AbstractExecutor { 28 | public: 29 | void execute(std::function &&func) override { 30 | std::thread(func).detach(); 31 | } 32 | }; 33 | 34 | class AsyncExecutor : public AbstractExecutor { 35 | public: 36 | void execute(std::function &&func) override { 37 | std::unique_lock lock(future_lock); 38 | auto id = nextId++; 39 | lock.unlock(); 40 | 41 | auto future = std::async([this, id, func](){ 42 | func(); 43 | 44 | std::unique_lock lock(future_lock); 45 | // move future to stack so that it will be destroyed after unlocked. 46 | auto f = std::move(this->futures[id]); 47 | this->futures.erase(id); 48 | lock.unlock(); 49 | }); 50 | 51 | lock.lock(); 52 | this->futures[id] = std::move(future); 53 | lock.unlock(); 54 | } 55 | private: 56 | std::mutex future_lock; 57 | int nextId = 0; 58 | std::map> futures{}; 59 | }; 60 | 61 | class LooperExecutor : public AbstractExecutor { 62 | private: 63 | std::condition_variable queue_condition; 64 | std::mutex queue_lock; 65 | std::queue> executable_queue; 66 | 67 | std::atomic is_active; 68 | std::thread work_thread; 69 | 70 | void run_loop() { 71 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 72 | std::unique_lock lock(queue_lock); 73 | if (executable_queue.empty()) { 74 | queue_condition.wait(lock); 75 | if (executable_queue.empty()) { 76 | continue; 77 | } 78 | } 79 | auto func = executable_queue.front(); 80 | executable_queue.pop(); 81 | lock.unlock(); 82 | 83 | func(); 84 | } 85 | debug("run_loop exit."); 86 | } 87 | 88 | public: 89 | 90 | LooperExecutor() { 91 | is_active.store(true, std::memory_order_relaxed); 92 | work_thread = std::thread(&LooperExecutor::run_loop, this); 93 | } 94 | 95 | ~LooperExecutor() { 96 | shutdown(false); 97 | if (work_thread.joinable()) { 98 | work_thread.join(); 99 | } 100 | } 101 | 102 | void execute(std::function &&func) override { 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | executable_queue.push(func); 106 | lock.unlock(); 107 | queue_condition.notify_one(); 108 | } 109 | } 110 | 111 | void shutdown(bool wait_for_complete = true) { 112 | is_active.store(false, std::memory_order_relaxed); 113 | if (!wait_for_complete) { 114 | // clear queue. 115 | std::unique_lock lock(queue_lock); 116 | decltype(executable_queue) empty_queue; 117 | std::swap(executable_queue, empty_queue); 118 | lock.unlock(); 119 | } 120 | 121 | queue_condition.notify_all(); 122 | } 123 | }; 124 | 125 | class SharedLooperExecutor : public AbstractExecutor { 126 | public: 127 | void execute(std::function &&func) override { 128 | static LooperExecutor sharedLooperExecutor; 129 | sharedLooperExecutor.execute(std::move(func)); 130 | } 131 | }; 132 | 133 | #endif //CPPCOROUTINES_04_TASK_EXECUTOR_H_ 134 | -------------------------------------------------------------------------------- /08.awaiter/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | #include "Executor.h" 5 | #include "Task.h" 6 | #include "io_utils.h" 7 | #include "Scheduler.h" 8 | #include "Channel.h" 9 | #include "FutureAwaiter.h" 10 | 11 | using namespace std::chrono_literals; 12 | 13 | struct FakeAwaiter { 14 | 15 | using ResultType = void; 16 | bool await_ready() { return false; } 17 | 18 | void await_suspend(std::coroutine_handle<> handle) {} 19 | 20 | void await_resume() {} 21 | 22 | void install_executor(AbstractExecutor *) {} 23 | }; 24 | 25 | Task Producer(Channel &channel) { 26 | int i = 0; 27 | while (i < 10) { 28 | debug("send: ", i); 29 | // co_await channel.write(i++); 30 | co_await (channel << i++); 31 | // co_await 300ms; 32 | co_await SleepAwaiter(300ms); 33 | } 34 | 35 | // channel.close(); 36 | // debug("close channel, exit."); 37 | } 38 | 39 | Task Consumer(Channel &channel) { 40 | while (channel.is_active()) { 41 | try { 42 | // auto received = co_await channel.read(); 43 | int received; 44 | co_await (channel >> received); 45 | debug("receive: ", received); 46 | co_await 500ms; 47 | } catch (std::exception &e) { 48 | debug("exception: ", e.what()); 49 | } 50 | } 51 | 52 | debug("exit."); 53 | } 54 | 55 | Task Consumer2(Channel &channel) { 56 | while (channel.is_active()) { 57 | try { 58 | auto received = co_await channel.read(); 59 | debug("receive2: ", received); 60 | co_await 200ms; 61 | } catch (std::exception &e) { 62 | debug("exception2: ", e.what()); 63 | } 64 | } 65 | 66 | debug("exit."); 67 | } 68 | 69 | 70 | void test_channel(Channel &channel) { 71 | auto producer = Producer(channel); 72 | auto consumer = Consumer(channel); 73 | auto consumer2 = Consumer2(channel); 74 | 75 | debug("sleep ..."); 76 | std::this_thread::sleep_for(3s); 77 | debug("after sleep ..."); 78 | } 79 | 80 | Task simple_task2() { 81 | debug("task 2 start ..."); 82 | using namespace std::chrono_literals; 83 | std::this_thread::sleep_for(1s); 84 | debug("task 2 returns after 1s."); 85 | co_return 2; 86 | } 87 | 88 | Task simple_task3() { 89 | debug("in task 3 start ..."); 90 | using namespace std::chrono_literals; 91 | std::this_thread::sleep_for(2s); 92 | debug("task 3 returns after 2s."); 93 | co_return 3; 94 | } 95 | 96 | template 97 | FutureAwaiter as_awaiter(std::future &&future) { 98 | return FutureAwaiter(std::move(future)); 99 | } 100 | 101 | Task test1() { 102 | // no matching overloaded function found 103 | // co_await FakeAwaiter(); 104 | co_return; 105 | } 106 | 107 | Task simple_task() { 108 | // co_await test1(); 109 | debug("task start ..."); 110 | auto result2 = co_await simple_task2().as_awaiter(); 111 | debug("returns from task2: ", result2); 112 | auto result3 = co_await simple_task3(); 113 | debug("returns from task3: ", result3); 114 | auto result4 = co_await FutureAwaiter(std::async([]() { 115 | std::this_thread::sleep_for(1s); 116 | return 4; 117 | })); 118 | debug("returns from task4: ", result4); 119 | co_return 1 + result2 + result3 + result4; 120 | } 121 | 122 | void test_tasks() { 123 | auto simpleTask = simple_task(); 124 | simpleTask.then([](int i) { 125 | debug("simple task end: ", i); 126 | }).catching([](std::exception &e) { 127 | debug("error occurred", e.what()); 128 | }); 129 | try { 130 | auto i = simpleTask.get_result(); 131 | debug("simple task end from get: ", i); 132 | } catch (std::exception &e) { 133 | debug("error: ", e.what()); 134 | } 135 | } 136 | 137 | int main() { 138 | auto channel = Channel(2); 139 | test_channel(channel); 140 | channel.close(); 141 | // test_tasks(); 142 | return 0; 143 | } 144 | -------------------------------------------------------------------------------- /06.sleep/Scheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/18. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "io_utils.h" 15 | 16 | class DelayedExecutable { 17 | public: 18 | DelayedExecutable(std::function &&func, long long delay) : func(std::move(func)) { 19 | using namespace std; 20 | using namespace std::chrono; 21 | auto now = system_clock::now(); 22 | auto current = duration_cast(now.time_since_epoch()).count(); 23 | 24 | scheduled_time = current + delay; 25 | } 26 | 27 | long long delay() const { 28 | using namespace std; 29 | using namespace std::chrono; 30 | 31 | auto now = system_clock::now(); 32 | auto current = duration_cast(now.time_since_epoch()).count(); 33 | return scheduled_time - current; 34 | } 35 | 36 | long long get_scheduled_time() const { 37 | return scheduled_time; 38 | } 39 | 40 | void operator()() { 41 | func(); 42 | } 43 | 44 | private: 45 | long long scheduled_time; 46 | std::function func; 47 | }; 48 | 49 | class DelayedExecutableCompare { 50 | public: 51 | bool operator()(DelayedExecutable &left, DelayedExecutable &right) { 52 | return left.get_scheduled_time() > right.get_scheduled_time(); 53 | } 54 | }; 55 | 56 | class Scheduler { 57 | private: 58 | std::condition_variable queue_condition; 59 | std::mutex queue_lock; 60 | std::priority_queue, DelayedExecutableCompare> executable_queue; 61 | 62 | std::atomic is_active; 63 | std::thread work_thread; 64 | 65 | void run_loop() { 66 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 67 | std::unique_lock lock(queue_lock); 68 | if (executable_queue.empty()) { 69 | queue_condition.wait(lock); 70 | if (executable_queue.empty()) { 71 | continue; 72 | } 73 | } 74 | auto executable = executable_queue.top(); 75 | long long delay = executable.delay(); 76 | if (delay > 0) { 77 | auto status = queue_condition.wait_for(lock, std::chrono::milliseconds(delay)); 78 | if (status != std::cv_status::timeout) { 79 | // a new executable should be executed before. 80 | continue; 81 | } 82 | } 83 | executable_queue.pop(); 84 | lock.unlock(); 85 | executable(); 86 | } 87 | debug("run_loop exit."); 88 | } 89 | public: 90 | 91 | Scheduler() { 92 | is_active.store(true, std::memory_order_relaxed); 93 | work_thread = std::thread(&Scheduler::run_loop, this); 94 | } 95 | 96 | ~Scheduler() { 97 | shutdown(false); 98 | join(); 99 | } 100 | 101 | void execute(std::function &&func, long long delay) { 102 | delay = delay < 0 ? 0 : delay; 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | bool need_notify = executable_queue.empty() || executable_queue.top().delay() > delay; 106 | executable_queue.push(DelayedExecutable(std::move(func), delay)); 107 | lock.unlock(); 108 | if (need_notify) { 109 | queue_condition.notify_one(); 110 | } 111 | } 112 | } 113 | 114 | void shutdown(bool wait_for_complete = true) { 115 | is_active.store(false, std::memory_order_relaxed); 116 | if (!wait_for_complete) { 117 | // clear queue. 118 | std::unique_lock lock(queue_lock); 119 | decltype(executable_queue) empty_queue; 120 | std::swap(executable_queue, empty_queue); 121 | lock.unlock(); 122 | } 123 | 124 | queue_condition.notify_all(); 125 | } 126 | 127 | void join() { 128 | if (work_thread.joinable()) { 129 | work_thread.join(); 130 | } 131 | } 132 | }; 133 | 134 | #endif //CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 135 | -------------------------------------------------------------------------------- /07.channel/Scheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/18. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "io_utils.h" 15 | 16 | class DelayedExecutable { 17 | public: 18 | DelayedExecutable(std::function &&func, long long delay) : func(std::move(func)) { 19 | using namespace std; 20 | using namespace std::chrono; 21 | auto now = system_clock::now(); 22 | auto current = duration_cast(now.time_since_epoch()).count(); 23 | 24 | scheduled_time = current + delay; 25 | } 26 | 27 | long long delay() const { 28 | using namespace std; 29 | using namespace std::chrono; 30 | 31 | auto now = system_clock::now(); 32 | auto current = duration_cast(now.time_since_epoch()).count(); 33 | return scheduled_time - current; 34 | } 35 | 36 | long long get_scheduled_time() const { 37 | return scheduled_time; 38 | } 39 | 40 | void operator()() { 41 | func(); 42 | } 43 | 44 | private: 45 | long long scheduled_time; 46 | std::function func; 47 | }; 48 | 49 | class DelayedExecutableCompare { 50 | public: 51 | bool operator()(DelayedExecutable &left, DelayedExecutable &right) { 52 | return left.get_scheduled_time() > right.get_scheduled_time(); 53 | } 54 | }; 55 | 56 | class Scheduler { 57 | private: 58 | std::condition_variable queue_condition; 59 | std::mutex queue_lock; 60 | std::priority_queue, DelayedExecutableCompare> executable_queue; 61 | 62 | std::atomic is_active; 63 | std::thread work_thread; 64 | 65 | void run_loop() { 66 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 67 | std::unique_lock lock(queue_lock); 68 | if (executable_queue.empty()) { 69 | queue_condition.wait(lock); 70 | if (executable_queue.empty()) { 71 | continue; 72 | } 73 | } 74 | auto executable = executable_queue.top(); 75 | long long delay = executable.delay(); 76 | if (delay > 0) { 77 | auto status = queue_condition.wait_for(lock, std::chrono::milliseconds(delay)); 78 | if (status != std::cv_status::timeout) { 79 | // a new executable should be executed before. 80 | continue; 81 | } 82 | } 83 | executable_queue.pop(); 84 | lock.unlock(); 85 | executable(); 86 | } 87 | debug("run_loop exit.") 88 | } 89 | public: 90 | 91 | Scheduler() { 92 | is_active.store(true, std::memory_order_relaxed); 93 | work_thread = std::thread(&Scheduler::run_loop, this); 94 | } 95 | 96 | ~Scheduler() { 97 | shutdown(false); 98 | join(); 99 | } 100 | 101 | void execute(std::function &&func, long long delay) { 102 | delay = delay < 0 ? 0 : delay; 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | bool need_notify = executable_queue.empty() || executable_queue.top().delay() > delay; 106 | executable_queue.push(DelayedExecutable(std::move(func), delay)); 107 | lock.unlock(); 108 | if (need_notify) { 109 | queue_condition.notify_one(); 110 | } 111 | } 112 | } 113 | 114 | void shutdown(bool wait_for_complete = true) { 115 | is_active.store(false, std::memory_order_relaxed); 116 | if (!wait_for_complete) { 117 | // clear queue. 118 | std::unique_lock lock(queue_lock); 119 | decltype(executable_queue) empty_queue; 120 | std::swap(executable_queue, empty_queue); 121 | lock.unlock(); 122 | } 123 | 124 | queue_condition.notify_all(); 125 | } 126 | 127 | void join() { 128 | if (work_thread.joinable()) { 129 | work_thread.join(); 130 | } 131 | } 132 | }; 133 | 134 | #endif //CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 135 | -------------------------------------------------------------------------------- /08.awaiter/Scheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/18. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "io_utils.h" 15 | 16 | class DelayedExecutable { 17 | public: 18 | DelayedExecutable(std::function &&func, long long delay) : func(std::move(func)) { 19 | using namespace std; 20 | using namespace std::chrono; 21 | auto now = system_clock::now(); 22 | auto current = duration_cast(now.time_since_epoch()).count(); 23 | 24 | scheduled_time = current + delay; 25 | } 26 | 27 | long long delay() const { 28 | using namespace std; 29 | using namespace std::chrono; 30 | 31 | auto now = system_clock::now(); 32 | auto current = duration_cast(now.time_since_epoch()).count(); 33 | return scheduled_time - current; 34 | } 35 | 36 | long long get_scheduled_time() const { 37 | return scheduled_time; 38 | } 39 | 40 | void operator()() { 41 | func(); 42 | } 43 | 44 | private: 45 | long long scheduled_time; 46 | std::function func; 47 | }; 48 | 49 | class DelayedExecutableCompare { 50 | public: 51 | bool operator()(DelayedExecutable &left, DelayedExecutable &right) { 52 | return left.get_scheduled_time() > right.get_scheduled_time(); 53 | } 54 | }; 55 | 56 | class Scheduler { 57 | private: 58 | std::condition_variable queue_condition; 59 | std::mutex queue_lock; 60 | std::priority_queue, DelayedExecutableCompare> executable_queue; 61 | 62 | std::atomic is_active; 63 | std::thread work_thread; 64 | 65 | void run_loop() { 66 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 67 | std::unique_lock lock(queue_lock); 68 | if (executable_queue.empty()) { 69 | queue_condition.wait(lock); 70 | if (executable_queue.empty()) { 71 | continue; 72 | } 73 | } 74 | auto executable = executable_queue.top(); 75 | long long delay = executable.delay(); 76 | if (delay > 0) { 77 | auto status = queue_condition.wait_for(lock, std::chrono::milliseconds(delay)); 78 | if (status != std::cv_status::timeout) { 79 | // a new executable should be executed before. 80 | continue; 81 | } 82 | } 83 | executable_queue.pop(); 84 | lock.unlock(); 85 | executable(); 86 | } 87 | debug("run_loop exit."); 88 | } 89 | public: 90 | 91 | Scheduler() { 92 | is_active.store(true, std::memory_order_relaxed); 93 | work_thread = std::thread(&Scheduler::run_loop, this); 94 | } 95 | 96 | ~Scheduler() { 97 | shutdown(false); 98 | join(); 99 | } 100 | 101 | void execute(std::function &&func, long long delay) { 102 | delay = delay < 0 ? 0 : delay; 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | bool need_notify = executable_queue.empty() || executable_queue.top().delay() > delay; 106 | executable_queue.push(DelayedExecutable(std::move(func), delay)); 107 | lock.unlock(); 108 | if (need_notify) { 109 | queue_condition.notify_one(); 110 | } 111 | } 112 | } 113 | 114 | void shutdown(bool wait_for_complete = true) { 115 | is_active.store(false, std::memory_order_relaxed); 116 | if (!wait_for_complete) { 117 | // clear queue. 118 | std::unique_lock lock(queue_lock); 119 | decltype(executable_queue) empty_queue; 120 | std::swap(executable_queue, empty_queue); 121 | lock.unlock(); 122 | } 123 | 124 | queue_condition.notify_all(); 125 | } 126 | 127 | void join() { 128 | if (work_thread.joinable()) { 129 | work_thread.join(); 130 | } 131 | } 132 | }; 133 | 134 | #endif //CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 135 | -------------------------------------------------------------------------------- /09.http/Scheduler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/18. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "io_utils.h" 15 | 16 | class DelayedExecutable { 17 | public: 18 | DelayedExecutable(std::function &&func, long long delay) : func(std::move(func)) { 19 | using namespace std; 20 | using namespace std::chrono; 21 | auto now = system_clock::now(); 22 | auto current = duration_cast(now.time_since_epoch()).count(); 23 | 24 | scheduled_time = current + delay; 25 | } 26 | 27 | long long delay() const { 28 | using namespace std; 29 | using namespace std::chrono; 30 | 31 | auto now = system_clock::now(); 32 | auto current = duration_cast(now.time_since_epoch()).count(); 33 | return scheduled_time - current; 34 | } 35 | 36 | long long get_scheduled_time() const { 37 | return scheduled_time; 38 | } 39 | 40 | void operator()() { 41 | func(); 42 | } 43 | 44 | private: 45 | long long scheduled_time; 46 | std::function func; 47 | }; 48 | 49 | class DelayedExecutableCompare { 50 | public: 51 | bool operator()(DelayedExecutable &left, DelayedExecutable &right) { 52 | return left.get_scheduled_time() > right.get_scheduled_time(); 53 | } 54 | }; 55 | 56 | class Scheduler { 57 | private: 58 | std::condition_variable queue_condition; 59 | std::mutex queue_lock; 60 | std::priority_queue, DelayedExecutableCompare> executable_queue; 61 | 62 | std::atomic is_active; 63 | std::thread work_thread; 64 | 65 | void run_loop() { 66 | while (is_active.load(std::memory_order_relaxed) || !executable_queue.empty()) { 67 | std::unique_lock lock(queue_lock); 68 | if (executable_queue.empty()) { 69 | queue_condition.wait(lock); 70 | if (executable_queue.empty()) { 71 | continue; 72 | } 73 | } 74 | auto executable = executable_queue.top(); 75 | long long delay = executable.delay(); 76 | if (delay > 0) { 77 | auto status = queue_condition.wait_for(lock, std::chrono::milliseconds(delay)); 78 | if (status != std::cv_status::timeout) { 79 | // a new executable should be executed before. 80 | continue; 81 | } 82 | } 83 | executable_queue.pop(); 84 | lock.unlock(); 85 | executable(); 86 | } 87 | debug("run_loop exit."); 88 | } 89 | public: 90 | 91 | Scheduler() { 92 | is_active.store(true, std::memory_order_relaxed); 93 | work_thread = std::thread(&Scheduler::run_loop, this); 94 | } 95 | 96 | ~Scheduler() { 97 | shutdown(false); 98 | join(); 99 | } 100 | 101 | void execute(std::function &&func, long long delay) { 102 | delay = delay < 0 ? 0 : delay; 103 | std::unique_lock lock(queue_lock); 104 | if (is_active.load(std::memory_order_relaxed)) { 105 | bool need_notify = executable_queue.empty() || executable_queue.top().delay() > delay; 106 | executable_queue.push(DelayedExecutable(std::move(func), delay)); 107 | lock.unlock(); 108 | if (need_notify) { 109 | queue_condition.notify_one(); 110 | } 111 | } 112 | } 113 | 114 | void shutdown(bool wait_for_complete = true) { 115 | is_active.store(false, std::memory_order_relaxed); 116 | if (!wait_for_complete) { 117 | // clear queue. 118 | std::unique_lock lock(queue_lock); 119 | decltype(executable_queue) empty_queue; 120 | std::swap(executable_queue, empty_queue); 121 | lock.unlock(); 122 | } 123 | 124 | queue_condition.notify_all(); 125 | } 126 | 127 | void join() { 128 | if (work_thread.joinable()) { 129 | work_thread.join(); 130 | } 131 | } 132 | }; 133 | 134 | #endif //CPPCOROUTINES_TASKS_04_TASK_SCHEDULER_H_ 135 | -------------------------------------------------------------------------------- /07.channel/Channel.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/21. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 6 | #define CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "ChannelAwaiter.h" 10 | #include 11 | 12 | template 13 | struct Channel { 14 | 15 | struct ChannelClosedException : std::exception { 16 | const char *what() const noexcept override { 17 | return "Channel is closed."; 18 | } 19 | }; 20 | 21 | void check_closed() { 22 | if (!_is_active.load(std::memory_order_relaxed)) { 23 | throw ChannelClosedException(); 24 | } 25 | } 26 | 27 | void try_push_reader(ReaderAwaiter *reader_awaiter) { 28 | std::unique_lock lock(channel_lock); 29 | check_closed(); 30 | 31 | if (!buffer.empty()) { 32 | auto value = buffer.front(); 33 | buffer.pop(); 34 | 35 | if (!writer_list.empty()) { 36 | auto writer = writer_list.front(); 37 | writer_list.pop_front(); 38 | buffer.push(writer->_value); 39 | lock.unlock(); 40 | 41 | writer->resume(); 42 | } else { 43 | lock.unlock(); 44 | } 45 | 46 | reader_awaiter->resume(value); 47 | return; 48 | } 49 | 50 | if (!writer_list.empty()) { 51 | auto writer = writer_list.front(); 52 | writer_list.pop_front(); 53 | lock.unlock(); 54 | 55 | reader_awaiter->resume(writer->_value); 56 | writer->resume(); 57 | return; 58 | } 59 | 60 | reader_list.push_back(reader_awaiter); 61 | } 62 | 63 | void try_push_writer(WriterAwaiter *writer_awaiter) { 64 | std::unique_lock lock(channel_lock); 65 | check_closed(); 66 | // suspended readers 67 | if (!reader_list.empty()) { 68 | auto reader = reader_list.front(); 69 | reader_list.pop_front(); 70 | lock.unlock(); 71 | 72 | reader->resume(writer_awaiter->_value); 73 | writer_awaiter->resume(); 74 | return; 75 | } 76 | 77 | // write to buffer 78 | if (buffer.size() < buffer_capacity) { 79 | buffer.push(writer_awaiter->_value); 80 | lock.unlock(); 81 | writer_awaiter->resume(); 82 | return; 83 | } 84 | 85 | // suspend writer 86 | writer_list.push_back(writer_awaiter); 87 | } 88 | 89 | void remove_writer(WriterAwaiter *writer_awaiter) { 90 | std::lock_guard lock(channel_lock); 91 | auto size = writer_list.remove(writer_awaiter); 92 | debug("remove writer ", size); 93 | } 94 | 95 | void remove_reader(ReaderAwaiter *reader_awaiter) { 96 | std::lock_guard lock(channel_lock); 97 | auto size = reader_list.remove(reader_awaiter); 98 | debug("remove reader ", size); 99 | } 100 | 101 | auto write(ValueType value) { 102 | check_closed(); 103 | return WriterAwaiter(this, value); 104 | } 105 | 106 | auto operator<<(ValueType value) { 107 | return write(value); 108 | } 109 | 110 | auto read() { 111 | check_closed(); 112 | return ReaderAwaiter(this); 113 | } 114 | 115 | auto operator>>(ValueType &value_ref) { 116 | auto awaiter = read(); 117 | awaiter.p_value = &value_ref; 118 | return awaiter; 119 | } 120 | 121 | void close() { 122 | bool expect = true; 123 | if(_is_active.compare_exchange_strong(expect, false, std::memory_order_relaxed)) { 124 | clean_up(); 125 | } 126 | } 127 | 128 | explicit Channel(int capacity = 0) : buffer_capacity(capacity) { 129 | _is_active.store(true, std::memory_order_relaxed); 130 | } 131 | 132 | bool is_active() { 133 | return _is_active.load(std::memory_order_relaxed); 134 | } 135 | 136 | Channel(Channel &&channel) = delete; 137 | 138 | Channel(Channel &) = delete; 139 | 140 | Channel &operator=(Channel &) = delete; 141 | 142 | ~Channel() { 143 | close(); 144 | } 145 | 146 | private: 147 | int buffer_capacity; 148 | std::queue buffer; 149 | std::list *> writer_list; 150 | std::list *> reader_list; 151 | 152 | std::atomic _is_active; 153 | 154 | std::mutex channel_lock; 155 | std::condition_variable channel_condition; 156 | 157 | void clean_up() { 158 | std::lock_guard lock(channel_lock); 159 | 160 | for (auto writer : writer_list) { 161 | writer->resume(); 162 | } 163 | writer_list.clear(); 164 | 165 | for (auto reader : reader_list) { 166 | reader->resume(); 167 | } 168 | reader_list.clear(); 169 | 170 | decltype(buffer) empty_buffer; 171 | std::swap(buffer, empty_buffer); 172 | } 173 | }; 174 | 175 | #endif //CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 176 | -------------------------------------------------------------------------------- /09.http/Channel.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/21. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 6 | #define CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "ChannelAwaiter.h" 10 | #include 11 | 12 | template 13 | struct Channel { 14 | 15 | struct ChannelClosedException : std::exception { 16 | const char *what() const noexcept override { 17 | return "Channel is closed."; 18 | } 19 | }; 20 | 21 | void check_closed() { 22 | if (!_is_active.load(std::memory_order_relaxed)) { 23 | throw ChannelClosedException(); 24 | } 25 | } 26 | 27 | void try_push_reader(ReaderAwaiter *reader_awaiter) { 28 | std::unique_lock lock(channel_lock); 29 | check_closed(); 30 | 31 | if (!buffer.empty()) { 32 | auto value = buffer.front(); 33 | buffer.pop(); 34 | 35 | if (!writer_list.empty()) { 36 | auto writer = writer_list.front(); 37 | writer_list.pop_front(); 38 | buffer.push(writer->_value); 39 | lock.unlock(); 40 | 41 | writer->resume(); 42 | } else { 43 | lock.unlock(); 44 | } 45 | 46 | reader_awaiter->resume(value); 47 | return; 48 | } 49 | 50 | if (!writer_list.empty()) { 51 | auto writer = writer_list.front(); 52 | writer_list.pop_front(); 53 | lock.unlock(); 54 | 55 | reader_awaiter->resume(writer->_value); 56 | writer->resume(); 57 | return; 58 | } 59 | 60 | reader_list.push_back(reader_awaiter); 61 | } 62 | 63 | void try_push_writer(WriterAwaiter *writer_awaiter) { 64 | std::unique_lock lock(channel_lock); 65 | check_closed(); 66 | // suspended readers 67 | if (!reader_list.empty()) { 68 | auto reader = reader_list.front(); 69 | reader_list.pop_front(); 70 | lock.unlock(); 71 | 72 | reader->resume(writer_awaiter->_value); 73 | writer_awaiter->resume(); 74 | return; 75 | } 76 | 77 | // write to buffer 78 | if (buffer.size() < buffer_capacity) { 79 | buffer.push(writer_awaiter->_value); 80 | lock.unlock(); 81 | writer_awaiter->resume(); 82 | return; 83 | } 84 | 85 | // suspend writer 86 | writer_list.push_back(writer_awaiter); 87 | } 88 | 89 | void remove_writer(WriterAwaiter *writer_awaiter) { 90 | std::lock_guard lock(channel_lock); 91 | auto size = writer_list.remove(writer_awaiter); 92 | debug("remove writer ", size); 93 | } 94 | 95 | void remove_reader(ReaderAwaiter *reader_awaiter) { 96 | std::lock_guard lock(channel_lock); 97 | auto size = reader_list.remove(reader_awaiter); 98 | debug("remove reader ", size); 99 | } 100 | 101 | auto write(ValueType value) { 102 | check_closed(); 103 | return WriterAwaiter{this, value}; 104 | } 105 | 106 | auto operator<<(ValueType value) { 107 | return write(value); 108 | } 109 | 110 | auto read() { 111 | check_closed(); 112 | return ReaderAwaiter{this}; 113 | } 114 | 115 | auto operator>>(ValueType &value_ref) { 116 | auto awaiter = read(); 117 | awaiter.p_value = &value_ref; 118 | return awaiter; 119 | } 120 | 121 | void close() { 122 | bool expect = true; 123 | if (_is_active.compare_exchange_strong(expect, false, std::memory_order_relaxed)) { 124 | clean_up(); 125 | } 126 | } 127 | 128 | explicit Channel(int capacity = 0) : buffer_capacity(capacity) { 129 | _is_active.store(true, std::memory_order_relaxed); 130 | } 131 | 132 | bool is_active() { 133 | return _is_active.load(std::memory_order_relaxed); 134 | } 135 | 136 | Channel(Channel &&channel) = delete; 137 | 138 | Channel(Channel &) = delete; 139 | 140 | Channel &operator=(Channel &) = delete; 141 | 142 | ~Channel() { 143 | close(); 144 | } 145 | 146 | private: 147 | int buffer_capacity; 148 | std::queue buffer; 149 | std::list *> writer_list; 150 | std::list *> reader_list; 151 | 152 | std::atomic _is_active; 153 | 154 | std::mutex channel_lock; 155 | std::condition_variable channel_condition; 156 | 157 | void clean_up() { 158 | std::lock_guard lock(channel_lock); 159 | 160 | for (auto writer : writer_list) { 161 | writer->resume(); 162 | } 163 | writer_list.clear(); 164 | 165 | for (auto reader : reader_list) { 166 | reader->resume_unsafe(); 167 | } 168 | reader_list.clear(); 169 | 170 | decltype(buffer) empty_buffer; 171 | std::swap(buffer, empty_buffer); 172 | } 173 | }; 174 | 175 | #endif //CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 176 | -------------------------------------------------------------------------------- /08.awaiter/Channel.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/21. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 6 | #define CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 7 | 8 | #include "coroutine_common.h" 9 | #include "ChannelAwaiter.h" 10 | #include 11 | 12 | template 13 | struct Channel { 14 | 15 | struct ChannelClosedException : std::exception { 16 | const char *what() const noexcept override { 17 | return "Channel is closed."; 18 | } 19 | }; 20 | 21 | void check_closed() { 22 | if (!_is_active.load(std::memory_order_relaxed)) { 23 | throw ChannelClosedException(); 24 | } 25 | } 26 | 27 | void try_push_reader(ReaderAwaiter *reader_awaiter) { 28 | std::unique_lock lock(channel_lock); 29 | check_closed(); 30 | 31 | if (!buffer.empty()) { 32 | auto value = buffer.front(); 33 | buffer.pop(); 34 | 35 | if (!writer_list.empty()) { 36 | auto writer = writer_list.front(); 37 | writer_list.pop_front(); 38 | buffer.push(writer->_value); 39 | lock.unlock(); 40 | 41 | writer->resume(); 42 | } else { 43 | lock.unlock(); 44 | } 45 | 46 | reader_awaiter->resume(value); 47 | return; 48 | } 49 | 50 | if (!writer_list.empty()) { 51 | auto writer = writer_list.front(); 52 | writer_list.pop_front(); 53 | lock.unlock(); 54 | 55 | reader_awaiter->resume(writer->_value); 56 | writer->resume(); 57 | return; 58 | } 59 | 60 | reader_list.push_back(reader_awaiter); 61 | } 62 | 63 | void try_push_writer(WriterAwaiter *writer_awaiter) { 64 | std::unique_lock lock(channel_lock); 65 | check_closed(); 66 | // suspended readers 67 | if (!reader_list.empty()) { 68 | auto reader = reader_list.front(); 69 | reader_list.pop_front(); 70 | lock.unlock(); 71 | 72 | reader->resume(writer_awaiter->_value); 73 | writer_awaiter->resume(); 74 | return; 75 | } 76 | 77 | // write to buffer 78 | if (buffer.size() < buffer_capacity) { 79 | buffer.push(writer_awaiter->_value); 80 | lock.unlock(); 81 | writer_awaiter->resume(); 82 | return; 83 | } 84 | 85 | // suspend writer 86 | writer_list.push_back(writer_awaiter); 87 | } 88 | 89 | void remove_writer(WriterAwaiter *writer_awaiter) { 90 | std::lock_guard lock(channel_lock); 91 | auto size = writer_list.remove(writer_awaiter); 92 | debug("remove writer ", size); 93 | } 94 | 95 | void remove_reader(ReaderAwaiter *reader_awaiter) { 96 | std::lock_guard lock(channel_lock); 97 | auto size = reader_list.remove(reader_awaiter); 98 | debug("remove reader ", size); 99 | } 100 | 101 | auto write(ValueType value) { 102 | check_closed(); 103 | return WriterAwaiter{this, value}; 104 | } 105 | 106 | auto operator<<(ValueType value) { 107 | return write(value); 108 | } 109 | 110 | auto read() { 111 | check_closed(); 112 | return ReaderAwaiter{this}; 113 | } 114 | 115 | auto operator>>(ValueType &value_ref) { 116 | auto awaiter = read(); 117 | awaiter.p_value = &value_ref; 118 | return awaiter; 119 | } 120 | 121 | void close() { 122 | bool expect = true; 123 | if (_is_active.compare_exchange_strong(expect, false, std::memory_order_relaxed)) { 124 | clean_up(); 125 | } 126 | } 127 | 128 | explicit Channel(int capacity = 0) : buffer_capacity(capacity) { 129 | _is_active.store(true, std::memory_order_relaxed); 130 | } 131 | 132 | bool is_active() { 133 | return _is_active.load(std::memory_order_relaxed); 134 | } 135 | 136 | Channel(Channel &&channel) = delete; 137 | 138 | Channel(Channel &) = delete; 139 | 140 | Channel &operator=(Channel &) = delete; 141 | 142 | ~Channel() { 143 | close(); 144 | } 145 | 146 | private: 147 | int buffer_capacity; 148 | std::queue buffer; 149 | std::list *> writer_list; 150 | std::list *> reader_list; 151 | 152 | std::atomic _is_active; 153 | 154 | std::mutex channel_lock; 155 | std::condition_variable channel_condition; 156 | 157 | void clean_up() { 158 | std::lock_guard lock(channel_lock); 159 | 160 | for (auto writer : writer_list) { 161 | writer->resume(); 162 | } 163 | writer_list.clear(); 164 | 165 | for (auto reader : reader_list) { 166 | reader->resume_unsafe(); 167 | } 168 | reader_list.clear(); 169 | 170 | decltype(buffer) empty_buffer; 171 | std::swap(buffer, empty_buffer); 172 | } 173 | }; 174 | 175 | #endif //CPPCOROUTINES_TASKS_07_CHANNEL_CHANNEL_H_ 176 | -------------------------------------------------------------------------------- /09.http/TaskPromise.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "coroutine_common.h" 14 | #include "Result.h" 15 | #include "DispatchAwaiter.h" 16 | #include "TaskAwaiter.h" 17 | #include "SleepAwaiter.h" 18 | #include "ChannelAwaiter.h" 19 | #include "CommonAwaiter.h" 20 | 21 | template 22 | concept AwaiterImplRestriction = std::is_base_of, AwaiterImpl>::value; 23 | 24 | template 25 | class Task; 26 | 27 | template 28 | struct TaskPromise { 29 | DispatchAwaiter initial_suspend() { return DispatchAwaiter{&executor}; } 30 | 31 | std::suspend_always final_suspend() noexcept { return {}; } 32 | 33 | Task get_return_object() { 34 | return Task{std::coroutine_handle::from_promise(*this)}; 35 | } 36 | 37 | template 38 | TaskAwaiter<_ResultType, _Executor> await_transform(Task<_ResultType, _Executor> &&task) { 39 | return await_transform(TaskAwaiter<_ResultType, _Executor>(std::move(task))); 40 | } 41 | 42 | template 43 | auto await_transform(std::chrono::duration<_Rep, _Period> &&duration) { 44 | return await_transform(SleepAwaiter(duration)); 45 | } 46 | 47 | template 48 | requires AwaiterImplRestriction 49 | AwaiterImpl await_transform(AwaiterImpl awaiter) { 50 | awaiter.install_executor(&executor); 51 | return awaiter; 52 | } 53 | 54 | void unhandled_exception() { 55 | std::lock_guard lock(completion_lock); 56 | result = Result(std::current_exception()); 57 | completion.notify_all(); 58 | notify_callbacks(); 59 | } 60 | 61 | void return_value(ResultType value) { 62 | std::lock_guard lock(completion_lock); 63 | result = Result(std::move(value)); 64 | completion.notify_all(); 65 | notify_callbacks(); 66 | } 67 | 68 | ResultType get_result() { 69 | // blocking for result or throw on exception 70 | std::unique_lock lock(completion_lock); 71 | if (!result.has_value()) { 72 | completion.wait(lock); 73 | } 74 | return result->get_or_throw(); 75 | } 76 | 77 | void on_completed(std::function)> &&func) { 78 | std::unique_lock lock(completion_lock); 79 | if (result.has_value()) { 80 | auto value = result.value(); 81 | lock.unlock(); 82 | func(value); 83 | } else { 84 | completion_callbacks.push_back(func); 85 | } 86 | } 87 | 88 | private: 89 | std::optional> result; 90 | 91 | std::mutex completion_lock; 92 | std::condition_variable completion; 93 | 94 | std::list)>> completion_callbacks; 95 | 96 | Executor executor; 97 | 98 | void notify_callbacks() { 99 | auto value = result.value(); 100 | for (auto &callback : completion_callbacks) { 101 | callback(value); 102 | } 103 | completion_callbacks.clear(); 104 | } 105 | 106 | }; 107 | 108 | template 109 | struct TaskPromise { 110 | DispatchAwaiter initial_suspend() { return DispatchAwaiter{&executor}; } 111 | 112 | std::suspend_always final_suspend() noexcept { return {}; } 113 | 114 | Task get_return_object() { 115 | return Task{std::coroutine_handle::from_promise(*this)}; 116 | } 117 | 118 | template 119 | TaskAwaiter<_ResultType, _Executor> await_transform(Task<_ResultType, _Executor> &&task) { 120 | return await_transform(TaskAwaiter<_ResultType, _Executor>(std::move(task))); 121 | } 122 | 123 | template 124 | SleepAwaiter await_transform(std::chrono::duration<_Rep, _Period> &&duration) { 125 | return await_transform(SleepAwaiter(std::chrono::duration_cast(duration).count())); 126 | } 127 | 128 | template 129 | requires AwaiterImplRestriction 130 | AwaiterImpl await_transform(AwaiterImpl &&awaiter) { 131 | awaiter.install_executor(&executor); 132 | return awaiter; 133 | } 134 | 135 | void get_result() { 136 | // blocking for result or throw on exception 137 | std::unique_lock lock(completion_lock); 138 | if (!result.has_value()) { 139 | completion.wait(lock); 140 | } 141 | result->get_or_throw(); 142 | } 143 | 144 | void unhandled_exception() { 145 | std::lock_guard lock(completion_lock); 146 | result = Result(std::current_exception()); 147 | completion.notify_all(); 148 | notify_callbacks(); 149 | } 150 | 151 | void return_void() { 152 | std::lock_guard lock(completion_lock); 153 | result = Result(); 154 | completion.notify_all(); 155 | notify_callbacks(); 156 | } 157 | 158 | void on_completed(std::function)> &&func) { 159 | std::unique_lock lock(completion_lock); 160 | if (result.has_value()) { 161 | auto value = result.value(); 162 | lock.unlock(); 163 | func(value); 164 | } else { 165 | completion_callbacks.push_back(func); 166 | } 167 | } 168 | 169 | private: 170 | std::optional> result; 171 | 172 | std::mutex completion_lock; 173 | std::condition_variable completion; 174 | 175 | std::list)>> completion_callbacks; 176 | 177 | Executor executor; 178 | 179 | void notify_callbacks() { 180 | auto value = result.value(); 181 | for (auto &callback : completion_callbacks) { 182 | callback(value); 183 | } 184 | completion_callbacks.clear(); 185 | } 186 | 187 | }; 188 | 189 | #endif //CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 190 | -------------------------------------------------------------------------------- /08.awaiter/TaskPromise.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "coroutine_common.h" 14 | #include "Result.h" 15 | #include "DispatchAwaiter.h" 16 | #include "TaskAwaiter.h" 17 | #include "SleepAwaiter.h" 18 | #include "ChannelAwaiter.h" 19 | #include "CommonAwaiter.h" 20 | 21 | template 22 | concept AwaiterImplRestriction = std::is_base_of, AwaiterImpl>::value; 23 | 24 | template 25 | class Task; 26 | 27 | template 28 | struct TaskPromise { 29 | DispatchAwaiter initial_suspend() { return DispatchAwaiter{&executor}; } 30 | 31 | std::suspend_always final_suspend() noexcept { return {}; } 32 | 33 | Task get_return_object() { 34 | return Task{std::coroutine_handle::from_promise(*this)}; 35 | } 36 | 37 | template 38 | TaskAwaiter<_ResultType, _Executor> await_transform(Task<_ResultType, _Executor> &&task) { 39 | return await_transform(TaskAwaiter<_ResultType, _Executor>(std::move(task))); 40 | } 41 | 42 | template 43 | auto await_transform(std::chrono::duration<_Rep, _Period> &&duration) { 44 | return await_transform(SleepAwaiter(duration)); 45 | } 46 | 47 | template 48 | requires AwaiterImplRestriction 49 | AwaiterImpl await_transform(AwaiterImpl awaiter) { 50 | awaiter.install_executor(&executor); 51 | return awaiter; 52 | } 53 | 54 | void unhandled_exception() { 55 | std::lock_guard lock(completion_lock); 56 | result = Result(std::current_exception()); 57 | completion.notify_all(); 58 | notify_callbacks(); 59 | } 60 | 61 | void return_value(ResultType value) { 62 | std::lock_guard lock(completion_lock); 63 | result = Result(std::move(value)); 64 | completion.notify_all(); 65 | notify_callbacks(); 66 | } 67 | 68 | ResultType get_result() { 69 | // blocking for result or throw on exception 70 | std::unique_lock lock(completion_lock); 71 | if (!result.has_value()) { 72 | completion.wait(lock); 73 | } 74 | return result->get_or_throw(); 75 | } 76 | 77 | void on_completed(std::function)> &&func) { 78 | std::unique_lock lock(completion_lock); 79 | if (result.has_value()) { 80 | auto value = result.value(); 81 | lock.unlock(); 82 | func(value); 83 | } else { 84 | completion_callbacks.push_back(func); 85 | } 86 | } 87 | 88 | private: 89 | std::optional> result; 90 | 91 | std::mutex completion_lock; 92 | std::condition_variable completion; 93 | 94 | std::list)>> completion_callbacks; 95 | 96 | Executor executor; 97 | 98 | void notify_callbacks() { 99 | auto value = result.value(); 100 | for (auto &callback : completion_callbacks) { 101 | callback(value); 102 | } 103 | completion_callbacks.clear(); 104 | } 105 | 106 | }; 107 | 108 | template 109 | struct TaskPromise { 110 | DispatchAwaiter initial_suspend() { return DispatchAwaiter{&executor}; } 111 | 112 | std::suspend_always final_suspend() noexcept { return {}; } 113 | 114 | Task get_return_object() { 115 | return Task{std::coroutine_handle::from_promise(*this)}; 116 | } 117 | 118 | template 119 | TaskAwaiter<_ResultType, _Executor> await_transform(Task<_ResultType, _Executor> &&task) { 120 | return await_transform(TaskAwaiter<_ResultType, _Executor>(std::move(task))); 121 | } 122 | 123 | template 124 | SleepAwaiter await_transform(std::chrono::duration<_Rep, _Period> &&duration) { 125 | return await_transform(SleepAwaiter(std::chrono::duration_cast(duration).count())); 126 | } 127 | 128 | template 129 | requires AwaiterImplRestriction 130 | AwaiterImpl await_transform(AwaiterImpl &&awaiter) { 131 | awaiter.install_executor(&executor); 132 | return awaiter; 133 | } 134 | 135 | void get_result() { 136 | // blocking for result or throw on exception 137 | std::unique_lock lock(completion_lock); 138 | if (!result.has_value()) { 139 | completion.wait(lock); 140 | } 141 | result->get_or_throw(); 142 | } 143 | 144 | void unhandled_exception() { 145 | std::lock_guard lock(completion_lock); 146 | result = Result(std::current_exception()); 147 | completion.notify_all(); 148 | notify_callbacks(); 149 | } 150 | 151 | void return_void() { 152 | std::lock_guard lock(completion_lock); 153 | result = Result(); 154 | completion.notify_all(); 155 | notify_callbacks(); 156 | } 157 | 158 | void on_completed(std::function)> &&func) { 159 | std::unique_lock lock(completion_lock); 160 | if (result.has_value()) { 161 | auto value = result.value(); 162 | lock.unlock(); 163 | func(value); 164 | } else { 165 | completion_callbacks.push_back(func); 166 | } 167 | } 168 | 169 | private: 170 | std::optional> result; 171 | 172 | std::mutex completion_lock; 173 | std::condition_variable completion; 174 | 175 | std::list)>> completion_callbacks; 176 | 177 | Executor executor; 178 | 179 | void notify_callbacks() { 180 | auto value = result.value(); 181 | for (auto &callback : completion_callbacks) { 182 | callback(value); 183 | } 184 | completion_callbacks.clear(); 185 | } 186 | 187 | }; 188 | 189 | #endif //CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 190 | -------------------------------------------------------------------------------- /07.channel/TaskPromise.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/3/17. 3 | // 4 | 5 | #ifndef CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 6 | #define CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "coroutine_common.h" 14 | #include "Result.h" 15 | #include "DispatchAwaiter.h" 16 | #include "TaskAwaiter.h" 17 | #include "SleepAwaiter.h" 18 | #include "ChannelAwaiter.h" 19 | 20 | template 21 | class Task; 22 | 23 | template 24 | struct TaskPromise { 25 | DispatchAwaiter initial_suspend() { return DispatchAwaiter{&executor}; } 26 | 27 | std::suspend_always final_suspend() noexcept { return {}; } 28 | 29 | Task get_return_object() { 30 | return Task{std::coroutine_handle::from_promise(*this)}; 31 | } 32 | 33 | template 34 | TaskAwaiter<_ResultType, _Executor> await_transform(Task<_ResultType, _Executor> &&task) { 35 | return TaskAwaiter<_ResultType, _Executor>(&executor, std::move(task)); 36 | } 37 | 38 | template 39 | SleepAwaiter await_transform(std::chrono::duration<_Rep, _Period> &&duration) { 40 | return SleepAwaiter(&executor, std::chrono::duration_cast(duration).count()); 41 | } 42 | 43 | template 44 | auto await_transform(ReaderAwaiter<_ValueType> reader_awaiter) { 45 | reader_awaiter.executor = &executor; 46 | return reader_awaiter; 47 | } 48 | 49 | template 50 | auto await_transform(WriterAwaiter<_ValueType> writer_awaiter) { 51 | writer_awaiter.executor = &executor; 52 | return writer_awaiter; 53 | } 54 | 55 | void unhandled_exception() { 56 | std::lock_guard lock(completion_lock); 57 | result = Result(std::current_exception()); 58 | completion.notify_all(); 59 | notify_callbacks(); 60 | } 61 | 62 | void return_value(ResultType value) { 63 | std::lock_guard lock(completion_lock); 64 | result = Result(std::move(value)); 65 | completion.notify_all(); 66 | notify_callbacks(); 67 | } 68 | 69 | ResultType get_result() { 70 | // blocking for result or throw on exception 71 | std::unique_lock lock(completion_lock); 72 | if (!result.has_value()) { 73 | completion.wait(lock); 74 | } 75 | return result->get_or_throw(); 76 | } 77 | 78 | void on_completed(std::function)> &&func) { 79 | std::unique_lock lock(completion_lock); 80 | if (result.has_value()) { 81 | auto value = result.value(); 82 | lock.unlock(); 83 | func(value); 84 | } else { 85 | completion_callbacks.push_back(func); 86 | } 87 | } 88 | 89 | private: 90 | std::optional> result; 91 | 92 | std::mutex completion_lock; 93 | std::condition_variable completion; 94 | 95 | std::list)>> completion_callbacks; 96 | 97 | Executor executor; 98 | 99 | void notify_callbacks() { 100 | auto value = result.value(); 101 | for (auto &callback : completion_callbacks) { 102 | callback(value); 103 | } 104 | completion_callbacks.clear(); 105 | } 106 | 107 | }; 108 | 109 | template 110 | struct TaskPromise { 111 | DispatchAwaiter initial_suspend() { return DispatchAwaiter{&executor}; } 112 | 113 | std::suspend_always final_suspend() noexcept { return {}; } 114 | 115 | Task get_return_object() { 116 | return Task{std::coroutine_handle::from_promise(*this)}; 117 | } 118 | 119 | template 120 | TaskAwaiter<_ResultType, _Executor> await_transform(Task<_ResultType, _Executor> &&task) { 121 | return TaskAwaiter<_ResultType, _Executor>(&executor, std::move(task)); 122 | } 123 | 124 | template 125 | SleepAwaiter await_transform(std::chrono::duration<_Rep, _Period> &&duration) { 126 | return SleepAwaiter(&executor, std::chrono::duration_cast(duration).count()); 127 | } 128 | 129 | template 130 | auto await_transform(ReaderAwaiter<_ValueType> reader_awaiter) { 131 | reader_awaiter.executor = &executor; 132 | return reader_awaiter; 133 | } 134 | 135 | template 136 | auto await_transform(WriterAwaiter<_ValueType> writer_awaiter) { 137 | writer_awaiter.executor = &executor; 138 | return writer_awaiter; 139 | } 140 | 141 | void get_result() { 142 | // blocking for result or throw on exception 143 | std::unique_lock lock(completion_lock); 144 | if (!result.has_value()) { 145 | completion.wait(lock); 146 | } 147 | result->get_or_throw(); 148 | } 149 | 150 | void unhandled_exception() { 151 | std::lock_guard lock(completion_lock); 152 | result = Result(std::current_exception()); 153 | completion.notify_all(); 154 | notify_callbacks(); 155 | } 156 | 157 | void return_void() { 158 | std::lock_guard lock(completion_lock); 159 | result = Result(); 160 | completion.notify_all(); 161 | notify_callbacks(); 162 | } 163 | 164 | void on_completed(std::function)> &&func) { 165 | std::unique_lock lock(completion_lock); 166 | if (result.has_value()) { 167 | auto value = result.value(); 168 | lock.unlock(); 169 | func(value); 170 | } else { 171 | completion_callbacks.push_back(func); 172 | } 173 | } 174 | 175 | private: 176 | std::optional> result; 177 | 178 | std::mutex completion_lock; 179 | std::condition_variable completion; 180 | 181 | std::list)>> completion_callbacks; 182 | 183 | Executor executor; 184 | 185 | void notify_callbacks() { 186 | auto value = result.value(); 187 | for (auto &callback : completion_callbacks) { 188 | callback(value); 189 | } 190 | completion_callbacks.clear(); 191 | } 192 | 193 | }; 194 | 195 | #endif //CPPCOROUTINES_TASKS_04_TASK_TASKPROMISE_H_ 196 | -------------------------------------------------------------------------------- /03.functional.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by benny on 2022/1/31. 3 | // 4 | #define __cpp_lib_coroutine 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "io.h" 13 | 14 | template 15 | struct Generator { 16 | 17 | class ExhaustedException : std::exception {}; 18 | 19 | struct promise_type { 20 | T value; 21 | bool is_ready = false; 22 | 23 | std::suspend_never initial_suspend() { return {}; }; 24 | 25 | std::suspend_always final_suspend() noexcept { return {}; } 26 | 27 | std::suspend_always yield_value(T value) { 28 | this->value = value; 29 | is_ready = true; 30 | return {}; 31 | } 32 | 33 | void unhandled_exception() { 34 | 35 | } 36 | 37 | Generator get_return_object() { 38 | return Generator{std::coroutine_handle::from_promise(*this)}; 39 | } 40 | 41 | void return_void() {} 42 | }; 43 | 44 | std::coroutine_handle handle; 45 | 46 | bool has_next() { 47 | if (!handle || handle.done()) { 48 | return false; 49 | } 50 | 51 | if (!handle.promise().is_ready) { 52 | handle.resume(); 53 | } 54 | 55 | if (handle.done()) { 56 | return false; 57 | } else { 58 | return true; 59 | } 60 | } 61 | 62 | T next() { 63 | if (has_next()) { 64 | handle.promise().is_ready = false; 65 | return handle.promise().value; 66 | } 67 | throw ExhaustedException(); 68 | } 69 | 70 | // template 71 | // Generator map(std::function f) { 72 | // auto up_stream = std::move(*this); 73 | // while (up_stream.has_next()) { 74 | // co_yield f(up_stream.next()); 75 | // } 76 | // } 77 | 78 | template 79 | Generator> map(F f) { 80 | auto up_steam = std::move(*this); 81 | while (up_steam.has_next()) { 82 | co_yield f(up_steam.next()); 83 | } 84 | } 85 | 86 | // template 87 | // Generator flat_map(std::function(T)> f) { 88 | // auto up_steam = std::move(*this); 89 | // while (up_steam.has_next()) { 90 | // auto generator = f(up_steam.next()); 91 | // while (generator.has_next()) { 92 | // co_yield generator.next(); 93 | // } 94 | // } 95 | // } 96 | 97 | template 98 | std::invoke_result_t flat_map(F f) { 99 | auto up_steam = std::move(*this); 100 | while (up_steam.has_next()) { 101 | auto generator = f(up_steam.next()); 102 | while (generator.has_next()) { 103 | co_yield generator.next(); 104 | } 105 | } 106 | } 107 | 108 | Generator take(int n) { 109 | auto up_steam = std::move(*this); 110 | int i = 0; 111 | while (i++ < n && up_steam.has_next()) { 112 | co_yield up_steam.next(); 113 | } 114 | } 115 | 116 | template 117 | Generator take_while(F f) { 118 | auto up_steam = std::move(*this); 119 | while (up_steam.has_next()) { 120 | T value = up_steam.next(); 121 | if (f(value)) { 122 | co_yield value; 123 | } else { 124 | break; 125 | } 126 | } 127 | } 128 | 129 | template 130 | Generator filter(F f) { 131 | auto up_steam = std::move(*this); 132 | while (up_steam.has_next()) { 133 | T value = up_steam.next(); 134 | if (f(value)) { 135 | co_yield value; 136 | } 137 | } 138 | } 139 | 140 | template 141 | void for_each(F f) { 142 | while (has_next()) { 143 | f(next()); 144 | } 145 | } 146 | 147 | template 148 | R fold(R initial, F f) { 149 | while (has_next()) { 150 | initial = f(initial, next()); 151 | } 152 | return initial; 153 | } 154 | 155 | T sum() { 156 | T sum = 0; 157 | while (has_next()) { 158 | sum += next(); 159 | } 160 | return sum; 161 | } 162 | 163 | Generator static from_array(T array[], int n) { 164 | for (int i = 0; i < n; ++i) { 165 | co_yield array[i]; 166 | } 167 | } 168 | 169 | Generator static from_list(std::list list) { 170 | for (auto t: list) { 171 | co_yield t; 172 | } 173 | } 174 | 175 | Generator static from(std::initializer_list args) { 176 | for (auto t: args) { 177 | co_yield t; 178 | } 179 | } 180 | 181 | template 182 | Generator static from(TArgs ...args) { 183 | (co_yield args, ...); 184 | } 185 | 186 | explicit Generator(std::coroutine_handle handle) noexcept 187 | : handle(handle) {} 188 | 189 | Generator(Generator &&generator) noexcept 190 | : handle(std::exchange(generator.handle, {})) {} 191 | 192 | Generator(Generator &) = delete; 193 | Generator &operator=(Generator &) = delete; 194 | 195 | ~Generator() { 196 | if (handle) handle.destroy(); 197 | } 198 | }; 199 | 200 | Generator fibonacci() { 201 | co_yield 0; 202 | co_yield 1; 203 | 204 | int a = 0; 205 | int b = 1; 206 | while (true) { 207 | co_yield a + b; 208 | b = a + b; 209 | a = b - a; 210 | } 211 | } 212 | 213 | int main() { 214 | Generator::from(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 215 | .filter([](auto i) { 216 | std::cout << "filter: " << i << std::endl; 217 | return i % 2 == 0; 218 | }) 219 | .map([](auto i) { 220 | std::cout << "map: " << i << std::endl; 221 | return i * 3; 222 | }) 223 | .flat_map([](auto i) -> Generator { 224 | std::cout << "flat_map: " << i << std::endl; 225 | for (int j = 0; j < i; ++j) { 226 | co_yield j; 227 | } 228 | }).take(3) 229 | .for_each([](auto i) { 230 | std::cout << "for_each: " << i << std::endl; 231 | }); 232 | 233 | int array[] = {1, 2, 3, 4}; 234 | Generator::from_array(array, 4); 235 | Generator::from_list(std::list{1, 2, 3, 4}); 236 | Generator::from({1, 2, 3, 4}); 237 | 238 | Generator::from(1, 2, 3, 4) 239 | .flat_map([](auto i) -> Generator { 240 | for (int j = 0; j < i; ++j) { 241 | co_yield j; 242 | } 243 | }) 244 | .for_each([](auto i) { 245 | if (i == 0) { 246 | std::cout << std::endl; 247 | } 248 | std::cout << "* "; 249 | }); 250 | 251 | fibonacci().take_while([](auto i) { 252 | return i < 100; 253 | }).for_each([](auto i) { 254 | std::cout << i << " "; 255 | }); 256 | 257 | std::cout << std::endl; 258 | 259 | std::cout << Generator::from(1.0, 2.0, 3.0, 4, 5, 6.0f).sum() << std::endl; 260 | std::cout << Generator::from(1.0, 2.0, 3.0, 4, 5, 6.0f).fold(1, [](auto acc, auto i) { return acc * i; }) 261 | << std::endl; 262 | 263 | auto seq = fibonacci().map([](auto i) { 264 | return std::to_string(i); 265 | }); 266 | 267 | for(int i = 0; i < 10; i++) { 268 | std::cout << seq.next() << std::endl; 269 | } 270 | 271 | Generator generator = Generator::from(1, 2, 3, 4).map([](int i) { 272 | return i * 2; 273 | }); 274 | 275 | generator.for_each([](auto i) { 276 | std::cout << i << std::endl; 277 | }); 278 | return 0; 279 | } --------------------------------------------------------------------------------