├── .gitignore ├── cf ├── sync_executor.h ├── cpp14_type_traits.h ├── common.h ├── async_queued_executor.h ├── time_watcher.h ├── async_thread_pool_executor.h └── cfuture.h ├── test ├── CMakeLists.txt └── future.cpp ├── CMakeLists.txt ├── LICENSE ├── .travis.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .build/ 2 | build 3 | *.user 4 | .vscode 5 | xcode/ 6 | *.swp 7 | -------------------------------------------------------------------------------- /cf/sync_executor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | namespace cf { 6 | class sync_executor { 7 | public: 8 | void post(detail::task_type f) { 9 | f(); 10 | } 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project("cf_test") 2 | 3 | file(GLOB CF_HEADERS "../cf/*.h") 4 | set(SRC "future.cpp") 5 | add_executable(${PROJECT_NAME} ${CF_HEADERS} ${SRC}) 6 | target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) 7 | -------------------------------------------------------------------------------- /cf/cpp14_type_traits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std { 6 | template 7 | using result_of_t = typename result_of::type; 8 | 9 | template 10 | using decay_t = typename decay::type; 11 | 12 | template 13 | using conditional_t = typename conditional::type; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project("cf") 3 | if (MSVC) 4 | set(CMAKE_CXX_FLAGS "/EHsc /bigobj") 5 | else() 6 | set(NON_MSVC_CXX_FLAGS "-std=c++1z -pedantic -Werror -Wextra -Wall") 7 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 8 | set(CMAKE_CXX_FLAGS "${NON_MSVC_CXX_FLAGS} -stdlib=libc++") 9 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") 10 | set(CMAKE_CXX_FLAGS "${NON_MSVC_CXX_FLAGS}") 11 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 12 | if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9) 13 | set(CMAKE_CXX_FLAGS "${NON_MSVC_CXX_FLAGS}") 14 | else () 15 | set(CMAKE_CXX_FLAGS "-std=c++1y") 16 | endif() 17 | endif() 18 | 19 | find_package (Threads) 20 | endif() 21 | 22 | set(INCLUDES "${CMAKE_SOURCE_DIR}") 23 | 24 | include_directories(${INCLUDES}) 25 | 26 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 27 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 28 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 29 | 30 | add_subdirectory("test") 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alexander Kulikov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cf/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cf { 6 | namespace detail { 7 | 8 | template 9 | class movable_func; 10 | 11 | template 12 | class movable_func { 13 | 14 | struct base_holder { 15 | virtual R operator() (Args... args) = 0; 16 | virtual ~base_holder() {} 17 | }; 18 | 19 | template 20 | struct holder : base_holder { 21 | holder(F f) : f_(std::move(f)) {} 22 | virtual R operator() (Args... args) override { 23 | return f_(args...); 24 | } 25 | 26 | F f_; 27 | }; 28 | 29 | public: 30 | template 31 | movable_func(F f) : held_(new holder(std::move(f))) {} 32 | movable_func() : held_(nullptr) {} 33 | movable_func(movable_func&&) = default; 34 | movable_func& operator = (std::nullptr_t) { 35 | held_.reset(); 36 | return *this; 37 | } 38 | movable_func& operator = (movable_func&&) = default; 39 | movable_func(const movable_func&) = delete; 40 | movable_func& operator = (const movable_func&) = delete; 41 | bool empty() const { return !held_; } 42 | 43 | R operator() (Args... args) const { 44 | return held_->operator()(args...); 45 | } 46 | 47 | explicit operator bool() { 48 | return (bool) held_; 49 | } 50 | 51 | private: 52 | std::unique_ptr held_; 53 | }; 54 | 55 | using task_type = movable_func; 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cf/async_queued_executor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common.h" 9 | 10 | namespace cf { 11 | class async_queued_executor { 12 | public: 13 | async_queued_executor() : need_stop_(false) { 14 | start(); 15 | } 16 | 17 | ~async_queued_executor() { 18 | stop(); 19 | } 20 | 21 | bool is_this_thread() { 22 | return std::this_thread::get_id() == thread_.get_id(); 23 | } 24 | 25 | void post(detail::task_type f) { 26 | std::lock_guard lock(mutex_); 27 | tasks_.push(std::move(f)); 28 | cond_.notify_all(); 29 | } 30 | 31 | void stop() { 32 | { 33 | std::lock_guard lock_(mutex_); 34 | need_stop_ = true; 35 | cond_.notify_all(); 36 | } 37 | if (thread_.joinable()) 38 | thread_.join(); 39 | } 40 | 41 | private: 42 | void start() { 43 | thread_ = std::thread([this] { 44 | std::unique_lock lock(mutex_); 45 | while (!need_stop_) { 46 | cond_.wait(lock, [this] { return need_stop_ || !tasks_.empty(); }); 47 | if (need_stop_) 48 | break; 49 | while (!tasks_.empty()) { 50 | auto f = std::move(tasks_.front()); 51 | tasks_.pop(); 52 | lock.unlock(); 53 | f(); 54 | lock.lock(); 55 | } 56 | } 57 | }); 58 | } 59 | 60 | private: 61 | std::queue tasks_; 62 | std::mutex mutex_; 63 | std::condition_variable cond_; 64 | std::thread thread_; 65 | bool need_stop_; 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: cpp 4 | 5 | matrix: 6 | include: 7 | - compiler: clang 8 | addons: 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | - llvm-toolchain-precise-3.8 13 | packages: 14 | - clang-3.8 15 | - libc++-dev 16 | before_install: 17 | - sudo apt-get update -qq 18 | 19 | script: 20 | - mkdir build 21 | - cd build 22 | - export CXX=clang++-3.8 23 | - export CC=clang-3.8 24 | - cmake .. && make 25 | - bin/cf_test 26 | 27 | - compiler: clang 28 | addons: 29 | apt: 30 | sources: 31 | - ubuntu-toolchain-r-test 32 | - llvm-toolchain-precise-3.7 33 | packages: 34 | - clang-3.7 35 | - libc++-dev 36 | before_install: 37 | - sudo apt-get update -qq 38 | 39 | script: 40 | - mkdir build 41 | - cd build 42 | - export CXX=clang++-3.7 43 | - export CC=clang-3.7 44 | - cmake .. && make 45 | - bin/cf_test 46 | 47 | - compiler: gcc 48 | addons: 49 | apt: 50 | sources: 51 | - ubuntu-toolchain-r-test 52 | packages: 53 | - g++-4.9 54 | before_install: 55 | - sudo apt-get update -qq 56 | 57 | script: 58 | - mkdir build 59 | - cd build 60 | - export CXX=g++-4.9 61 | - export CC=gcc-4.9 62 | - cmake .. && make 63 | - bin/cf_test 64 | 65 | - compiler: gcc 66 | addons: 67 | apt: 68 | sources: 69 | - ubuntu-toolchain-r-test 70 | packages: 71 | - g++-5 72 | before_install: 73 | - sudo apt-get update -qq 74 | 75 | script: 76 | - mkdir build 77 | - cd build 78 | - export CXX=g++-5 79 | - export CC=gcc-5 80 | - cmake .. && make 81 | - bin/cf_test 82 | 83 | - compiler: gcc 84 | script: 85 | - mkdir build 86 | - cd build 87 | - cmake .. && make 88 | - bin/cf_test 89 | 90 | -------------------------------------------------------------------------------- /cf/time_watcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "common.h" 10 | 11 | namespace cf { 12 | class time_watcher { 13 | struct record { 14 | std::chrono::time_point time; 15 | detail::task_type task; 16 | record(std::chrono::milliseconds timeout, detail::task_type task) 17 | : time(std::chrono::steady_clock::now() + timeout), 18 | task(std::move(task)) {} 19 | }; 20 | 21 | friend bool operator < (const record& lhs, const record& rhs) { 22 | return lhs.time < rhs.time; 23 | } 24 | 25 | public: 26 | time_watcher() 27 | : wakeup_time_(year_forward()) { 28 | watcher_thread_ = std::thread([this] { 29 | while (!need_stop_) { 30 | std::unique_lock lock(mutex_); 31 | if (!record_set_.empty()) 32 | wakeup_time_ = record_set_.begin()->time; 33 | while (!need_stop_ && !time_has_come()) { 34 | cond_.wait_until(lock, wakeup_time_); 35 | if (!time_has_come() && !record_set_.empty()) { 36 | wakeup_time_ = record_set_.begin()->time; 37 | } 38 | } 39 | if (need_stop_) 40 | return; 41 | while (time_has_come()) { 42 | record_set_.begin()->task(); 43 | record_set_.erase(record_set_.begin()); 44 | } 45 | wakeup_time_ = record_set_.empty() ? 46 | year_forward() : 47 | record_set_.begin()->time; 48 | } 49 | }); 50 | } 51 | 52 | ~time_watcher() { 53 | need_stop_ = true; 54 | cond_.notify_one(); 55 | if (watcher_thread_.joinable()) 56 | watcher_thread_.join(); 57 | } 58 | 59 | void add(detail::task_type task, std::chrono::milliseconds timeout) { 60 | std::lock_guard lock(mutex_); 61 | record_set_.emplace(timeout, std::move(task)); 62 | cond_.notify_one(); 63 | } 64 | 65 | private: 66 | bool time_has_come() const { 67 | if (record_set_.empty()) 68 | return false; 69 | return record_set_.cbegin()->time <= std::chrono::steady_clock::now(); 70 | } 71 | 72 | std::chrono::time_point year_forward() { 73 | return std::chrono::steady_clock::now() + std::chrono::hours(365 * 24); 74 | } 75 | 76 | private: 77 | std::condition_variable cond_; 78 | std::mutex mutex_; 79 | std::set record_set_; 80 | std::thread watcher_thread_; 81 | std::atomic need_stop_ = {false}; 82 | std::chrono::time_point wakeup_time_; 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /cf/async_thread_pool_executor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common.h" 11 | 12 | namespace cf { 13 | class async_thread_pool_executor { 14 | class worker_thread { 15 | public: 16 | worker_thread() { 17 | thread_ = std::thread([this] { 18 | std::unique_lock lock(m_); 19 | while (!need_stop_) { 20 | start_cond_.wait(lock, [this] { 21 | return (bool)task_ || need_stop_; 22 | }); 23 | if (need_stop_) 24 | return; 25 | lock.unlock(); 26 | task_(); 27 | lock.lock(); 28 | task_ = nullptr; 29 | has_task_ = false; 30 | completion_cb_(); 31 | } 32 | }); 33 | } 34 | 35 | ~worker_thread() { 36 | stop(); 37 | } 38 | 39 | void stop() { 40 | { 41 | std::lock_guard lock(m_); 42 | need_stop_ = true; 43 | start_cond_.notify_all(); 44 | } 45 | if (thread_.joinable()) 46 | thread_.join(); 47 | } 48 | 49 | bool available() const { 50 | return !has_task_; 51 | } 52 | 53 | void post( 54 | detail::task_type task, 55 | detail::task_type completion_cb) { 56 | std::unique_lock lock(m_); 57 | if (task_) 58 | throw std::logic_error("Worker already has a pending task"); 59 | start_task(std::move(task), std::move(completion_cb)); 60 | } 61 | 62 | private: 63 | void start_task( 64 | detail::task_type task, 65 | detail::task_type completion_cb) { 66 | has_task_ = true; 67 | task_ = std::move(task); 68 | completion_cb_ = std::move(completion_cb); 69 | start_cond_.notify_one(); 70 | } 71 | private: 72 | std::thread thread_; 73 | mutable std::mutex m_; 74 | bool need_stop_ = {false}; 75 | std::atomic has_task_ = {false}; 76 | std::condition_variable start_cond_; 77 | detail::task_type task_; 78 | detail::task_type completion_cb_; 79 | }; 80 | 81 | using tp_type = std::vector; 82 | 83 | public: 84 | async_thread_pool_executor(size_t size) 85 | : tp_(size), 86 | available_count_(size) { 87 | manager_thread_ = std::thread([this] { 88 | std::unique_lock lock(mutex_); 89 | while (!need_stop_) { 90 | cond_.wait(lock, [this] { 91 | return need_stop_ || (!task_queue_.empty() && 92 | available_count_ > 0); 93 | }); 94 | while (!task_queue_.empty() && available_count_ > 0) { 95 | if (need_stop_) 96 | return; 97 | auto ready_it = std::find_if(tp_.begin(), tp_.end(), 98 | [](const worker_thread& worker) { 99 | return worker.available(); 100 | }); 101 | if (ready_it == tp_.end()) 102 | break; 103 | auto task = std::move(task_queue_.front()); 104 | task_queue_.pop(); 105 | --available_count_; 106 | ready_it->post(std::move(task), [this] { 107 | ++available_count_; 108 | cond_.notify_one(); 109 | }); 110 | } 111 | } 112 | }); 113 | } 114 | 115 | ~async_thread_pool_executor() { 116 | { 117 | std::lock_guard lock(mutex_); 118 | need_stop_ = true; 119 | cond_.notify_all(); 120 | } 121 | if (manager_thread_.joinable()) 122 | manager_thread_.join(); 123 | for (auto& worker : tp_) { 124 | worker.stop(); 125 | } 126 | } 127 | 128 | size_t available() const { 129 | return (size_t)available_count_; 130 | } 131 | 132 | void post(detail::task_type task) { 133 | { 134 | std::unique_lock lock(mutex_); 135 | task_queue_.push(std::move(task)); 136 | } 137 | cond_.notify_one(); 138 | } 139 | 140 | private: 141 | tp_type tp_; 142 | mutable std::mutex mutex_; 143 | std::queue task_queue_; 144 | std::thread manager_thread_; 145 | std::atomic need_stop_ = {false}; 146 | std::atomic available_count_; 147 | std::condition_variable cond_; 148 | }; 149 | 150 | } 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/rpz80/cf.svg?branch=master)](https://travis-ci.org/rpz80/cf) 2 | # Composable futures C++ library (Cf) 3 | This is an implementation of composable, continuing c++17 like std [futures](https://en.cppreference.com/w/cpp/experimental/concurrency). 4 | Cf library has no dependencies except c++14 compliant standard library. Cf library comes with a an extensive unit test suit written using [Catch](https://github.com/philsquared/Catch) testing framework. These tests may also be used as an a source of examples. 5 | Cf has been tested on three major OS's. Minimum compiler requirements are: gcc-4.8 (gcc-4.9 or above is strongly recommended), clang-3.7, vs2015. 6 | 7 | ## Executors 8 | Unlike standard futures Cf library implements the Executor concept. Executor may be an object of virtually any type which has `post(cf::movable_func)` member function. It enables `cf::future` continuations and callables passed to the `cf::async` be executed via separate thread/process/coroutine/etc execution context. 9 | Cf comes with three executors shipped. They are: 10 | * `cf::sync_executor` - executes callable in place. 11 | * `cf::async_queued_executor` - non blocking async queued executor. 12 | * `cf::async_thread_pool_executor` - almost same as above, except posted callables may be executed on one of the free worker threads. 13 | 14 | ## Timeouts 15 | Also Cf futures support cancelation after certain timeout expired. User provided exception is stored in relevant future in this case and propagated through all subsequent `future::then`. Timeouts are handled by `cf::time_watcher`. As with executors, TimeWatcher might be an object of any type with `::add(const std::function&, std::chrono::duration timeout)` member function. 16 | 17 | ## Cf current state 18 | |Feature name|Standard library (including c++17)|Cf |Standard compliance| 19 | |------------|:--------------------------------:|:---:|----------| 20 | |[future](http://en.cppreference.com/w/cpp/experimental/future)|Yes|Yes|No share() member function. No void (use cf::unit instead) and T& specializations.| 21 | |[promise](http://en.cppreference.com/w/cpp/thread/promise)|Yes|Yes|No set_\*\*_at_thread_exit member functions. No void and T& specializations.| 22 | |[async](http://en.cppreference.com/w/cpp/thread/async)|Yes|Yes|No launch policy.| 23 | |[packaged_task](http://en.cppreference.com/w/cpp/thread/packaged_task)|Yes|No|| 24 | |[shared_future](http://en.cppreference.com/w/cpp/thread/shared_future)|Yes|No|| 25 | |[when_all](http://en.cppreference.com/w/cpp/experimental/when_all)|Yes|Yes|| 26 | |[when_any](http://en.cppreference.com/w/cpp/experimental/when_any)|Yes|Yes|| 27 | |[make_ready_future](http://en.cppreference.com/w/cpp/experimental/make_ready_future)|Yes|Yes|| 28 | |[make_exceptional_future](http://en.cppreference.com/w/cpp/experimental/make_exceptional_future)|Yes|Yes|| 29 | |executors|No|Yes|| 30 | |timeouts|No|Yes|| 31 | 32 | ## Examples 33 | For the basic future/promise/async examples please refer to http://en.cppreference.com/w/cpp/thread#Futures. 34 | 35 | ### async, then, then via executor 36 | ```c++ 37 | cf::async_queued_executor executor; 38 | try { 39 | auto f = cf::async([] { 40 | http_response resp = http_request("my-site.com")(); 41 | resp.read_headers(); // This is executed on the separate standalone thread 42 | return resp; // Result, when it's ready, is stored in cf::future. 43 | }).then([] (cf::future f) { // Which in turn is passed to the continuation. 44 | auto resp = f.get(); 45 | if (resp.code() == http::Ok) // The continuation may be executed on different contexts. 46 | resp.read_body(); // This time - on the async thread. 47 | else 48 | throw std::domain_error("Bad response"); 49 | return resp; 50 | }).then(executor, [] (cf::future f) { 51 | auto resp = f.get(); // And this time on the async_queued_executor context. 52 | process(resp.body()); 53 | return cf::unit(); // When you don't need result - use cf::unit. 54 | }).then([] (cf::future f) { 55 | f.wait(); // If f has exception inside, this line will let it out 56 | log() << "body processed" << std::endl; 57 | }).get(); 58 | } catch (const std::exception& e) { 59 | std::cerr << e.what() << std::endl; 60 | } 61 | ``` 62 | Async itself may be called with an executor. It is one of the reasons why there are no `launch::policy` in Cf. Every possible policy (async, deferred, etc) may easily be implemented as an executor. For example: 63 | 64 | ```c++ 65 | cf::sync_executor executor; 66 | cf::async(executor, [] { 67 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); // This is evaluated in place, in this case exactly like 68 | return std::string("Hello ") // std::async with the std::launch::deferred policy. 69 | }).get(); 70 | ``` 71 | You can return futures from continuations or 'plain' values. The latter will be lifted in the future by Cf implicitely. I.e. 72 | 73 | ```c++ 74 | auto f = cd::make_ready_future(42); 75 | std::is_same< 76 | decltype(f.then([](cf::future f) { // then() return type = cf::future 77 | return f.get() * 2; 78 | })), 79 | decltype(f.then([](cf::future f) { // same here (not cf::future>) 80 | return cf::async([f = std::move(f)] { 81 | return f.get() * 2; 82 | });})) 83 | >::value == true; 84 | ``` 85 | ### timeouts 86 | ```c++ 87 | cf::time_watcher tw; 88 | cf::async_thread_pool_executor executor(4); 89 | 90 | struct connect_timeout : std::domain_error { using std::domain_error::domain_error; }; 91 | struct write_timeout : std::domain_error { using std::domain_error::domain_error; }; 92 | struct read_timeout : std::domain_error { using std::domain_error::domain_error; }; 93 | 94 | try { 95 | auto client_future = cf::async([client = tcp_client()] () mutable { 96 | client.connect("mysite.com:8001"); 97 | return client; // client is moved from future to future. supposed to be a cheap operation 98 | }).timeout(std::chrono::milliseconds(500), connect_timeout("Connect timeout"), tw).then(executor, 99 | [](cf::future client_future) mutable { 100 | auto client = client_future.get(); 101 | client.write("GET /"); 102 | return client; 103 | }).timeout(std::chrono::seconds(2), write_timeout("Write timeout"), tw).then(executor, 104 | [](cf::future client_future) mutable { 105 | auto client = client_future.get(); 106 | client.read_until("/r/n/r/n"); 107 | return client; 108 | }).timeout(std::chrono::seconds(2), read_timeout("Read timeout"), tw); 109 | 110 | std::cout << client_future.get().data() << std::endl; 111 | 112 | } catch (const connect_timeout& e) { 113 | std::cerr << e.what() << std::endl; 114 | } catch (const write_timeout& e) { 115 | std::cerr << e.what() << std::endl; 116 | } catch (const read_timeout& e) { 117 | std::cerr << e.what() << std::endl; 118 | } 119 | ``` 120 | Note though, that timeout timer starts at the point of `cf::future::timeout` invocation, i.e. all timeouts in the example above are scheduled almost at the same moment. Thus when calling `cf::future::timeout` second and subsequent times consider adding up approximate duration of the previous calls when choosing timeout values. 121 | 122 | ### when_any, when_all 123 | `cf::when_any` and `cf::when_all` return `cf::future` which is ready when any or all of the input sequence futures become ready. These functions have iterator overloads and variadic overloads. Check [when_all](http://en.cppreference.com/w/cpp/experimental/when_all), [when_any](http://en.cppreference.com/w/cpp/experimental/when_any) for more details. 124 | ```c++ 125 | std::vector urls = {"url1.org", "url2.org", "url3.org"}; 126 | std::vector> response_future_vector; 127 | for (size_t i = 0; i < urls.size(); ++i) { 128 | response_future_vector.push_back(cf::async([&urls, i] { 129 | auto http_resp = http_request(urls[i])(); 130 | http_resp.read_all(); 131 | return http_resp; 132 | })); 133 | } 134 | 135 | auto result_future = cf::when_any(response_future_vector.begin(), response_future_vector.end()); 136 | auto result = result_future.get(); // blocks until one of the futures becomes ready. 137 | // result.index == ready future index 138 | // result.sequence contains original futures with sequence[index] ready 139 | 140 | ``` 141 | -------------------------------------------------------------------------------- /test/future.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include 3 | #include "catch.hh" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // aux stuff for types tests 12 | int foo(const cf::future&); 13 | double foo1(cf::future); 14 | cf::future foo3(cf::future); 15 | 16 | struct baz { 17 | baz() = default; 18 | baz(int v) : v_(v) {} 19 | int v_; 20 | }; 21 | struct test_struct { 22 | cf::unit bar1(cf::future) { return cf::unit(); } 23 | }; 24 | 25 | template 26 | void measure(const std::chrono::time_point& start_point, 27 | const char* prefix, 28 | const char* message, 29 | int expectedDuration) { 30 | auto now = std::chrono::steady_clock::now(); 31 | auto diff = std::chrono::duration_cast( 32 | now - start_point).count(); 33 | 34 | std::cout << prefix << message << diff << "ms (expected ~" 35 | << expectedDuration << "ms)" << std::endl; 36 | } 37 | 38 | // TODO: get rid of timeout dependent tests. add expectations instead. 39 | 40 | #if defined (_TIMEOUT_LEAK_TESTS) 41 | TEST_CASE("Timeout memory leak") { 42 | using namespace std::chrono_literals; 43 | SECTION("Not triggered") { 44 | cf::time_watcher tw; 45 | for (int i = 0; i < 1000; ++i) { 46 | cf::make_ready_future(10).timeout(10s, std::runtime_error("oops"), tw).get(); 47 | std::this_thread::sleep_for(100ms); 48 | } 49 | } 50 | 51 | SECTION("Triggered") { 52 | cf::time_watcher tw; 53 | for (int i = 0; i < 1000; ++i) { 54 | std::cout << "i: " << i << std::this_thread::get_id() << std::endl; 55 | try { 56 | cf::async([](){ 57 | std::this_thread::sleep_for(2s); 58 | return cf::unit(); 59 | }).timeout(500ms, std::runtime_error("oops"), tw).get(); 60 | } catch (const std::exception&){ 61 | } 62 | } 63 | } 64 | } 65 | #endif // _TIMEOUT_LEAK_TESTS 66 | 67 | TEST_CASE("Types") { 68 | SECTION("Callable return type") { 69 | test_struct ts; 70 | auto ts_bar1 = std::bind(&test_struct::bar1, &ts, std::placeholders::_1); 71 | auto lambda = [](const cf::future&) { return 0; }; 72 | lambda(cf::make_ready_future(true)); 73 | using lambda_type = decltype(lambda); 74 | 75 | REQUIRE((std::is_same>::value) == true); 77 | REQUIRE((std::is_same>::value) == true); 79 | REQUIRE((std::is_same>::value) == true); 81 | REQUIRE((std::is_same>::value) == true); 83 | REQUIRE((std::is_same, cf::detail:: 84 | then_arg_ret_type>::value) == true); 85 | } 86 | 87 | SECTION("Is future check") { 88 | REQUIRE((cf::detail::is_future>::value) == false); 90 | REQUIRE((cf::detail::is_future>::value) == true); 92 | } 93 | 94 | SECTION("Get return type for future::then") { 95 | using namespace cf::detail; 96 | using then_ret_type_for_foo = then_ret_type; 97 | REQUIRE((std::is_same>::value) == true); 99 | 100 | using then_ret_type_for_foo3 = then_ret_type; 101 | REQUIRE((std::is_same>::value) == true); 103 | } 104 | } 105 | 106 | TEST_CASE("Future") { 107 | cf::future future; 108 | cf::promise promise; 109 | 110 | SECTION("Basic") { 111 | REQUIRE(!future.valid()); 112 | 113 | SECTION("Set value") { 114 | promise.set_value(56); 115 | 116 | SECTION("Get future") { 117 | future = promise.get_future(); 118 | 119 | REQUIRE(future.valid()); 120 | REQUIRE(future.get() == 56); 121 | 122 | SECTION("Get future second time") { 123 | try { 124 | promise.get_future(); 125 | REQUIRE(false); 126 | } catch (const cf::future_error& error) { 127 | REQUIRE(error.ecode() == cf::errc::future_already_retrieved); 128 | REQUIRE(error.what() == 129 | cf::errc_string(cf::errc::future_already_retrieved)); 130 | } 131 | } 132 | 133 | SECTION("Future operator =") { 134 | cf::future future1 = std::move(future); 135 | 136 | REQUIRE(!future.valid()); 137 | REQUIRE(future1.valid()); 138 | REQUIRE_NOTHROW(future1.get()); 139 | } 140 | } 141 | 142 | SECTION("Set value second time") { 143 | REQUIRE_THROWS(promise.set_value(42)); 144 | } 145 | 146 | SECTION("Set exception second time") { 147 | REQUIRE_THROWS(promise.set_exception( 148 | std::make_exception_ptr(std::logic_error("whatever")))); 149 | } 150 | 151 | SECTION("Set value second time. Exception string.") { 152 | try { 153 | promise.set_value(42); 154 | REQUIRE(false); 155 | } catch (const cf::future_error& error) { 156 | REQUIRE(error.ecode() == cf::errc::promise_already_satisfied); 157 | REQUIRE(error.what() == 158 | cf::errc_string(cf::errc::promise_already_satisfied)); 159 | } 160 | } 161 | } // set value 162 | 163 | SECTION("Set exception") { 164 | promise.set_exception(std::make_exception_ptr(std::logic_error("test"))); 165 | 166 | SECTION("get future") { 167 | future = promise.get_future(); 168 | 169 | REQUIRE(future.valid()); 170 | try { 171 | future.get(); 172 | REQUIRE(false); 173 | } catch (const std::logic_error& error) { 174 | REQUIRE(error.what() == std::string("test")); 175 | } 176 | } 177 | 178 | SECTION("Set value second time") { 179 | REQUIRE_THROWS(promise.set_value(42)); 180 | } 181 | 182 | SECTION("Set exception second time") { 183 | REQUIRE_THROWS(promise.set_exception( 184 | std::make_exception_ptr(std::logic_error("whatever")))); 185 | } 186 | } // set exception 187 | 188 | SECTION("wait_for") { 189 | auto f = cf::async([](){ 190 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 191 | return cf::unit(); 192 | }); 193 | 194 | auto status = f.wait_for(std::chrono::milliseconds(5)); 195 | REQUIRE(status == cf::future_status::timeout); 196 | 197 | std::this_thread::sleep_for(std::chrono::milliseconds(60)); 198 | 199 | status = f.wait_for(std::chrono::milliseconds(20000)); 200 | REQUIRE(status == cf::future_status::ready); 201 | } 202 | 203 | SECTION("wait_until") { 204 | auto f = cf::async([](){ 205 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 206 | return cf::unit(); 207 | }); 208 | 209 | auto status = f.wait_until(std::chrono::steady_clock::now() + 210 | std::chrono::milliseconds(5)); 211 | REQUIRE(status == cf::future_status::timeout); 212 | 213 | std::this_thread::sleep_for(std::chrono::milliseconds(60)); 214 | 215 | status = f.wait_until(std::chrono::steady_clock::now() + 216 | std::chrono::milliseconds(1)); 217 | REQUIRE(status == cf::future_status::ready); 218 | } 219 | 220 | } 221 | 222 | const char* const executors_mix_with_async_name = "executors_mix_with_async"; 223 | 224 | SECTION(executors_mix_with_async_name) { 225 | cf::async_thread_pool_executor executor(2); 226 | auto start_point = std::chrono::steady_clock::now(); 227 | 228 | auto f = cf::async([]{ 229 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 230 | return 5; 231 | }).then(executor, [](cf::future f) { 232 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 233 | return f.get() * 5; 234 | }).then([](cf::future f) { 235 | return cf::async([f = std::move(f)]() mutable { 236 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 237 | return f.get() * 5; 238 | }); 239 | }); 240 | 241 | measure(start_point, executors_mix_with_async_name, 242 | ": async + then calls took ", 0); 243 | REQUIRE(f.get() == 125); 244 | measure(start_point, executors_mix_with_async_name, ": f.get() took ", 25); 245 | std::cout << std::endl; 246 | } 247 | 248 | SECTION("Simple several threads") { 249 | cf::promise p; 250 | auto f = p.get_future(); 251 | std::thread([p = std::move(p)] () mutable { 252 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 253 | p.set_value("Hi!"); 254 | }).detach(); 255 | REQUIRE(f.get() == "Hi!"); 256 | } 257 | } 258 | 259 | TEST_CASE("async") { 260 | SECTION("loop") { 261 | for (size_t i = 0; i < 2000; ++i) { 262 | cf::async([]{ return 1; }) 263 | .then([](cf::future){ return 2; }); 264 | } 265 | } 266 | 267 | SECTION("in a row") { 268 | cf::async_queued_executor executor; 269 | auto f = cf::async([] { 270 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 271 | return std::string("Hello "); 272 | }).then(executor, [] (cf::future f) { 273 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 274 | return f.get() + "futures "; 275 | }).then([] (cf::future f) { 276 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 277 | return f.get() + "world!"; 278 | }); 279 | REQUIRE(!f.is_ready()); 280 | REQUIRE(f.get() == "Hello futures world!"); 281 | } 282 | 283 | SECTION("async simple with args") { 284 | auto f = cf::async([](int i) { 285 | std::this_thread::sleep_for(std::chrono::milliseconds(300)); 286 | return i; 287 | }, 42); 288 | REQUIRE(f.get() == 42); 289 | } 290 | 291 | SECTION("async simple without args") { 292 | auto f = cf::async([]() { 293 | std::this_thread::sleep_for(std::chrono::milliseconds(300)); 294 | return 42; 295 | }); 296 | REQUIRE(f.get() == 42); 297 | } 298 | 299 | SECTION("simple") { 300 | auto f = cf::async([] { 301 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 302 | return std::string("Hello"); 303 | }); 304 | REQUIRE(!f.is_ready()); 305 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); 306 | REQUIRE(f.is_ready()); 307 | REQUIRE(f.get() == "Hello"); 308 | } 309 | 310 | SECTION("tp executor") { 311 | cf::async_thread_pool_executor executor(1); 312 | auto f = cf::async(executor, [] { 313 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 314 | return std::string("Hello"); 315 | }); 316 | REQUIRE(!f.is_ready()); 317 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); 318 | REQUIRE(f.is_ready()); 319 | REQUIRE(f.get() == "Hello"); 320 | } 321 | 322 | const char* const tp_executor_2_name = "async.tp_executor_2"; 323 | 324 | SECTION(tp_executor_2_name) { 325 | cf::async_thread_pool_executor executor(2); 326 | std::vector> v; 327 | 328 | auto start_time = std::chrono::steady_clock::now(); 329 | 330 | for (size_t i = 0; i < 10; ++i) { 331 | v.emplace_back(cf::async(executor, [i] { 332 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 333 | return std::string("Hello") + std::to_string(i); 334 | })); 335 | } 336 | 337 | measure(start_time, tp_executor_2_name, ": populating vector took ", 0); 338 | start_time = std::chrono::steady_clock::now(); 339 | 340 | for (size_t i = 0; i < 10; ++i) 341 | REQUIRE(v[i].get() == std::string("Hello") + std::to_string(i)); 342 | 343 | measure(start_time, tp_executor_2_name, ": get all vector futures took ", 100); 344 | } 345 | } 346 | 347 | TEST_CASE("Exceptions") { 348 | SECTION("exception in async") { 349 | SECTION("1") { 350 | auto f = cf::async([] { 351 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 352 | throw std::runtime_error("Exception"); 353 | return std::string("Hello"); 354 | }); 355 | REQUIRE(!f.is_ready()); 356 | std::this_thread::sleep_for(std::chrono::milliseconds(15)); 357 | REQUIRE(f.is_ready()); 358 | try { 359 | f.get(); 360 | REQUIRE(false); 361 | } catch (const std::exception& e) { 362 | REQUIRE(e.what() == std::string("Exception")); 363 | } 364 | } 365 | } 366 | 367 | SECTION("exception fallthrough in then") { 368 | auto f = cf::async([] { 369 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 370 | throw std::runtime_error("Exception"); 371 | return std::string("Hello"); 372 | }).then([] (cf::future) { 373 | return cf::unit(); 374 | }).then([] (cf::future f) { 375 | f.get(); 376 | REQUIRE(false); 377 | return cf::unit(); 378 | }); 379 | try { 380 | f.get(); 381 | REQUIRE(false); 382 | } catch (const std::exception& e) { 383 | REQUIRE(e.what() == std::string("Exception")); 384 | } 385 | } 386 | 387 | SECTION("exception fallthrough in then via executor") { 388 | cf::async_thread_pool_executor executor(1); 389 | auto f = cf::async(executor, [] { 390 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 391 | throw std::runtime_error("Exception"); 392 | return std::string("Hello"); 393 | }).then(executor, [] (cf::future f) { 394 | f.get(); 395 | REQUIRE(false); 396 | return cf::unit(); 397 | }).then(executor, [] (cf::future f) { 398 | f.get(); 399 | REQUIRE(false); 400 | return cf::unit(); 401 | }); 402 | REQUIRE(!f.is_ready()); 403 | try { 404 | f.get(); 405 | REQUIRE(false); 406 | } catch (const std::exception& e) { 407 | REQUIRE(e.what() == std::string("Exception")); 408 | } 409 | } 410 | } 411 | 412 | TEST_CASE("Time watcher") { 413 | cf::time_watcher tw; 414 | std::vector> tp_vec; 415 | auto start_point = std::chrono::steady_clock::now(); 416 | 417 | SECTION("1") { 418 | tw.add([&tp_vec] { 419 | tp_vec.push_back(std::chrono::steady_clock::now()); 420 | }, std::chrono::milliseconds(100)); 421 | 422 | tw.add([&tp_vec] { 423 | tp_vec.push_back(std::chrono::steady_clock::now()); 424 | }, std::chrono::milliseconds(200)); 425 | 426 | std::this_thread::sleep_for(std::chrono::milliseconds(400)); 427 | REQUIRE(tp_vec.size() == 2); 428 | REQUIRE(tp_vec[0] - start_point < std::chrono::milliseconds(110)); 429 | REQUIRE(tp_vec[0] - start_point > std::chrono::milliseconds(90)); 430 | 431 | REQUIRE(tp_vec[1] - start_point < std::chrono::milliseconds(210)); 432 | REQUIRE(tp_vec[1] - start_point > std::chrono::milliseconds(190)); 433 | } 434 | 435 | SECTION("2") { 436 | tw.add([&tp_vec] { 437 | tp_vec.push_back(std::chrono::steady_clock::now()); 438 | }, std::chrono::milliseconds(100)); 439 | 440 | tw.add([&tp_vec] { 441 | tp_vec.push_back(std::chrono::steady_clock::now()); 442 | }, std::chrono::milliseconds(100)); 443 | 444 | std::this_thread::sleep_for(std::chrono::milliseconds(150)); 445 | REQUIRE(tp_vec.size() == 2); 446 | REQUIRE(tp_vec[0] - start_point < std::chrono::milliseconds(110)); 447 | REQUIRE(tp_vec[0] - start_point > std::chrono::milliseconds(90)); 448 | 449 | REQUIRE(tp_vec[0] - start_point < std::chrono::milliseconds(110)); 450 | REQUIRE(tp_vec[0] - start_point > std::chrono::milliseconds(90)); 451 | } 452 | } 453 | 454 | TEST_CASE("Time watcher. Future timeout") { 455 | SECTION("1") { 456 | struct connect_timeout : std::runtime_error { using std::runtime_error::runtime_error; }; 457 | struct write_timeout : std::runtime_error {}; 458 | struct read_timeout : std::runtime_error {}; 459 | 460 | connect_timeout ct(""); 461 | 462 | cf::time_watcher tw; 463 | std::runtime_error timeout_error("timeout"); 464 | 465 | try { 466 | cf::async([] { 467 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 468 | return 42; 469 | }).timeout(std::chrono::milliseconds(50), timeout_error, tw).get(); 470 | REQUIRE(false); 471 | } catch (const std::exception& e) { 472 | REQUIRE(e.what() == std::string("timeout")); 473 | } 474 | } 475 | 476 | SECTION("2") { 477 | cf::time_watcher tw; 478 | cf::async_thread_pool_executor executor(4); 479 | 480 | struct connect_timeout : std::runtime_error { using std::runtime_error::runtime_error; }; 481 | struct write_timeout : std::runtime_error { using std::runtime_error::runtime_error; }; 482 | struct read_timeout : std::runtime_error { using std::runtime_error::runtime_error; }; 483 | 484 | struct tcp_client { 485 | tcp_client(std::chrono::milliseconds connect_time, 486 | std::chrono::milliseconds write_time, 487 | std::chrono::milliseconds read_time) 488 | : connect_time(connect_time), 489 | write_time(write_time), 490 | read_time(read_time) {} 491 | 492 | tcp_client() = default; 493 | tcp_client(tcp_client&& other) = default; 494 | tcp_client& operator=(tcp_client&& other) = default; 495 | 496 | void connect(const char*) { 497 | std::this_thread::sleep_for(std::chrono::milliseconds(connect_time)); 498 | } 499 | 500 | void write(const char*) { 501 | std::this_thread::sleep_for(std::chrono::milliseconds(write_time)); 502 | } 503 | 504 | void read_until(const char*) { 505 | std::this_thread::sleep_for(std::chrono::milliseconds(read_time)); 506 | } 507 | 508 | std::string data() const { 509 | return "Some data"; 510 | } 511 | 512 | std::chrono::milliseconds connect_time; 513 | std::chrono::milliseconds write_time; 514 | std::chrono::milliseconds read_time; 515 | }; 516 | 517 | SECTION("connect timeout") { 518 | tcp_client client(std::chrono::milliseconds(1000), 519 | std::chrono::milliseconds(100), 520 | std::chrono::milliseconds(200)); 521 | try { 522 | auto client_future = cf::async([client = std::move(client)] () mutable { 523 | client.connect("mysite.com:8001"); 524 | return std::move(client); 525 | }).timeout(std::chrono::milliseconds(500), connect_timeout("Connect timeout"), tw).then(executor, 526 | [](cf::future client_future) mutable { 527 | auto client = client_future.get(); 528 | client.write("GET /"); 529 | return client; 530 | }).timeout(std::chrono::seconds(2), write_timeout("Write timeout"), tw).then(executor, 531 | [](cf::future client_future) mutable { 532 | auto client = client_future.get(); 533 | client.read_until("/r/n/r/n"); 534 | return client; 535 | }).timeout(std::chrono::seconds(2), read_timeout("Read timeout"), tw); 536 | 537 | std::cout << client_future.get().data() << std::endl; 538 | REQUIRE(false); 539 | 540 | } catch (const connect_timeout& e) { 541 | REQUIRE(e.what() == std::string("Connect timeout")); 542 | } catch (const write_timeout& e) { 543 | REQUIRE(false); 544 | std::cerr << e.what() << std::endl; 545 | } catch (const read_timeout& e) { 546 | REQUIRE(false); 547 | std::cerr << e.what() << std::endl; 548 | } 549 | } 550 | 551 | SECTION("connect timeout T*") { 552 | tcp_client client(std::chrono::milliseconds(1000), 553 | std::chrono::milliseconds(100), 554 | std::chrono::milliseconds(200)); 555 | try { 556 | auto client_future = cf::async([pclient = &client] () mutable { 557 | pclient->connect("mysite.com:8001"); 558 | return pclient; 559 | }).timeout(std::chrono::milliseconds(500), connect_timeout("Connect timeout"), tw).then(executor, 560 | [](cf::future client_future) mutable { 561 | auto client = client_future.get(); 562 | client->write("GET /"); 563 | return client; 564 | }).timeout(std::chrono::seconds(2), write_timeout("Write timeout"), tw).then(executor, 565 | [](cf::future client_future) mutable { 566 | auto client = client_future.get(); 567 | client->read_until("/r/n/r/n"); 568 | return client; 569 | }).timeout(std::chrono::seconds(2), read_timeout("Read timeout"), tw); 570 | 571 | std::cout << client_future.get()->data() << std::endl; 572 | REQUIRE(false); 573 | 574 | } catch (const connect_timeout& e) { 575 | REQUIRE(e.what() == std::string("Connect timeout")); 576 | } catch (const write_timeout& e) { 577 | REQUIRE(false); 578 | std::cerr << e.what() << std::endl; 579 | } catch (const read_timeout& e) { 580 | REQUIRE(false); 581 | std::cerr << e.what() << std::endl; 582 | } 583 | } 584 | 585 | SECTION("write timeout") { 586 | tcp_client client(std::chrono::milliseconds(100), 587 | std::chrono::milliseconds(400), 588 | std::chrono::milliseconds(200)); 589 | try { 590 | auto client_future = cf::async([client = std::move(client)] () mutable { 591 | client.connect("mysite.com:8001"); 592 | return std::move(client); 593 | }).timeout(std::chrono::milliseconds(500), connect_timeout("Connect timeout"), tw).then(executor, 594 | [](cf::future client_future) mutable { 595 | auto client = client_future.get(); 596 | client.write("GET /"); 597 | return client; 598 | }).timeout(std::chrono::milliseconds(100), write_timeout("Write timeout"), tw).then(executor, 599 | [](cf::future client_future) mutable { 600 | auto client = client_future.get(); 601 | client.read_until("/r/n/r/n"); 602 | return client; 603 | }).timeout(std::chrono::seconds(2), read_timeout("Read timeout"), tw); 604 | 605 | std::cout << client_future.get().data() << std::endl; 606 | REQUIRE(false); 607 | 608 | } catch (const connect_timeout& e) { 609 | REQUIRE(false); 610 | std::cerr << e.what() << std::endl; 611 | } catch (const write_timeout& e) { 612 | REQUIRE(e.what() == std::string("Write timeout")); 613 | } catch (const read_timeout& e) { 614 | REQUIRE(false); 615 | std::cerr << e.what() << std::endl; 616 | } 617 | } 618 | 619 | SECTION("read timeout") { 620 | tcp_client client(std::chrono::milliseconds(100), 621 | std::chrono::milliseconds(100), 622 | std::chrono::milliseconds(400)); 623 | try { 624 | auto client_future = cf::async([client = std::move(client)] () mutable { 625 | client.connect("mysite.com:8001"); 626 | return std::move(client); 627 | }).timeout(std::chrono::milliseconds(500), connect_timeout("Connect timeout"), tw).then(executor, 628 | [](cf::future client_future) mutable { 629 | auto client = client_future.get(); 630 | client.write("GET /"); 631 | return client; 632 | }).timeout(std::chrono::milliseconds(500), write_timeout("Write timeout"), tw).then(executor, 633 | [](cf::future client_future) mutable { 634 | auto client = client_future.get(); 635 | client.read_until("/r/n/r/n"); 636 | return client; 637 | }).timeout(std::chrono::milliseconds(200), read_timeout("Read timeout"), tw); 638 | 639 | std::cout << client_future.get().data() << std::endl; 640 | REQUIRE(false); 641 | 642 | } catch (const connect_timeout& e) { 643 | REQUIRE(false); 644 | std::cerr << e.what() << std::endl; 645 | } catch (const write_timeout& e) { 646 | REQUIRE(false); 647 | } catch (const read_timeout& e) { 648 | REQUIRE(e.what() == std::string("Read timeout")); 649 | } 650 | } 651 | 652 | SECTION("no timeout") { 653 | tcp_client client(std::chrono::milliseconds(100), 654 | std::chrono::milliseconds(100), 655 | std::chrono::milliseconds(400)); 656 | try { 657 | auto client_future = cf::async([client = std::move(client)] () mutable { 658 | client.connect("mysite.com:8001"); 659 | return std::move(client); 660 | }).timeout(std::chrono::milliseconds(500), connect_timeout("Connect timeout"), tw).then(executor, 661 | [](cf::future client_future) mutable { 662 | auto client = client_future.get(); 663 | client.write("GET /"); 664 | return client; 665 | }).timeout(std::chrono::milliseconds(500), write_timeout("Write timeout"), tw).then(executor, 666 | [](cf::future client_future) mutable { 667 | auto client = client_future.get(); 668 | client.read_until("/r/n/r/n"); 669 | return client; 670 | }).timeout(std::chrono::milliseconds(700), read_timeout("Read timeout"), tw); 671 | 672 | auto client_data = client_future.get().data(); 673 | REQUIRE(client_data == "Some data"); 674 | std::cout << client_data << std::endl; 675 | 676 | } catch (const connect_timeout& e) { 677 | REQUIRE(false); 678 | std::cerr << e.what() << std::endl; 679 | } catch (const write_timeout& e) { 680 | REQUIRE(false); 681 | } catch (const read_timeout& e) { 682 | REQUIRE(false); 683 | } 684 | } 685 | } 686 | } 687 | 688 | TEST_CASE("Make future functions") { 689 | SECTION("Make ready") { 690 | cf::future f = cf::make_ready_future(42); 691 | REQUIRE(f.is_ready()); 692 | REQUIRE(f.valid()); 693 | REQUIRE(f.get() == 42); 694 | } 695 | 696 | SECTION("Make ready baz") { 697 | cf::future f = cf::make_ready_future(baz{1}); 698 | REQUIRE(f.is_ready()); 699 | REQUIRE(f.valid()); 700 | REQUIRE(f.get().v_ == 1); 701 | } 702 | 703 | SECTION("Make excetion") 704 | { 705 | cf::future f = cf::make_exceptional_future( 706 | std::make_exception_ptr(std::logic_error("whatever"))); 707 | REQUIRE(f.is_ready()); 708 | REQUIRE(f.valid()); 709 | REQUIRE_THROWS(f.get()); 710 | } 711 | } 712 | 713 | cf::future tfoo(cf::future f) { 714 | return cf::make_ready_future(f.get()); 715 | } 716 | 717 | TEST_CASE("Then test") { 718 | SECTION("Continuation returns future") { 719 | auto cont = [](cf::future f) -> cf::future { 720 | return cf::make_ready_future(f.get()); 721 | }; 722 | auto result = cf::make_ready_future(5).then( 723 | &tfoo 724 | ).then( 725 | cont 726 | ); 727 | REQUIRE(result.get() == (char)5.0); 728 | } 729 | 730 | SECTION("Continuation returns non future") { 731 | auto result = cf::make_ready_future(42).then( 732 | [](cf::future f) -> double { 733 | return (double)f.get(); 734 | }).then([](cf::future f) -> char { 735 | return (char)f.get(); 736 | }); 737 | REQUIRE(result.get() == 42); 738 | } 739 | 740 | SECTION("Continuation returns non future executor") { 741 | cf::sync_executor sync_executor; 742 | auto result = cf::make_ready_future(42) 743 | .then(sync_executor, [](cf::future f) { 744 | return (double)f.get(); 745 | }).then(sync_executor, [](cf::future f) { 746 | return (char)f.get(); 747 | }); 748 | REQUIRE(result.get() == 42); 749 | } 750 | } 751 | 752 | TEST_CASE("Executors") { 753 | SECTION("Async queued executor") 754 | { 755 | cf::async_queued_executor executor; 756 | int counter = 0; 757 | 758 | auto result = cf::async([&counter] { 759 | ++counter; 760 | return 42; 761 | }).then(executor, [&counter](cf::future f) { 762 | ++counter; 763 | f.get(); 764 | return std::string("Hello"); 765 | }).then(executor, [&counter](cf::future f) { 766 | ++counter; 767 | return f.get() + " world!"; 768 | }); 769 | 770 | REQUIRE(result.get() == "Hello world!"); 771 | REQUIRE(counter == 3); 772 | } 773 | 774 | SECTION("Thread pool executor") { 775 | SECTION("basic") { 776 | cf::async_thread_pool_executor executor(2); 777 | REQUIRE(executor.available() == 2); 778 | executor.post([] { 779 | std::this_thread::sleep_for(std::chrono::milliseconds(400)); 780 | }); 781 | executor.post([] { 782 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 783 | }); 784 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 785 | REQUIRE(executor.available() == 0); 786 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 787 | REQUIRE(executor.available() == 1); 788 | executor.post([] { 789 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 790 | }); 791 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 792 | REQUIRE(executor.available() == 0); 793 | std::this_thread::sleep_for(std::chrono::milliseconds(300)); 794 | REQUIRE(executor.available() == 2); 795 | } 796 | 797 | SECTION("basic wait") { 798 | cf::async_thread_pool_executor executor(2); 799 | REQUIRE(executor.available() == 2); 800 | executor.post([] { 801 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 802 | }); 803 | executor.post([] { 804 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 805 | }); 806 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 807 | REQUIRE(executor.available() == 0); 808 | executor.post([] { 809 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 810 | }); 811 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 812 | REQUIRE(executor.available() == 0); 813 | executor.post([] { 814 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 815 | }); 816 | REQUIRE(executor.available() == 0); 817 | std::this_thread::sleep_for(std::chrono::milliseconds(700)); 818 | REQUIRE(executor.available() == 2); 819 | } 820 | 821 | SECTION("future") { 822 | cf::async_thread_pool_executor executor(5); 823 | cf::future f = cf::make_ready_future(0); 824 | 825 | for (int i = 0; i < 10; ++i) { 826 | f = f.then(executor, [i](cf::future f) { 827 | std::this_thread::sleep_for(std::chrono::milliseconds(5 * (i + 3))); 828 | int val = f.get(); 829 | REQUIRE(val == i); 830 | return ++val; 831 | }); 832 | } 833 | 834 | REQUIRE(f.get() == 10); 835 | } 836 | } 837 | } 838 | 839 | template 840 | struct tuple_getter { 841 | template 842 | static auto apply(size_t i, std::tuple& t) { 843 | if (i == I) { 844 | return std::get(t); 845 | } else { 846 | return tuple_getter::apply(i, t); 847 | } 848 | } 849 | }; 850 | 851 | TEST_CASE("When all") { 852 | SECTION("Simple vector") { 853 | const size_t size = 5; 854 | std::vector> vec; 855 | 856 | SECTION("Async") { 857 | for (size_t i = 0; i < size; ++i) { 858 | vec.push_back(cf::async([i] { 859 | std::this_thread::sleep_for(std::chrono::milliseconds(i * 30)); 860 | return (int)i; 861 | })); 862 | } 863 | 864 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 865 | REQUIRE(vec[0].is_ready()); 866 | 867 | for (size_t i = 1; i < size; ++i) 868 | REQUIRE(!vec[i].is_ready()); 869 | 870 | auto when_all_result_future = cf::when_all(vec.begin(), vec.end()); 871 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 872 | 873 | REQUIRE(!when_all_result_future.is_ready()); 874 | 875 | auto when_all_result = when_all_result_future.get(); 876 | REQUIRE(when_all_result.size() == size); 877 | 878 | for (size_t i = 0; i < size; ++i) 879 | REQUIRE(when_all_result[i].get() == (int)i); 880 | } 881 | 882 | SECTION("Ready futures") { 883 | for (size_t i = 0; i < size; ++i) { 884 | vec.push_back(cf::make_ready_future((int)i)); 885 | } 886 | auto when_all_result = cf::when_all(vec.begin(), vec.end()).get(); 887 | REQUIRE(when_all_result.size() == size); 888 | for (size_t i = 0; i < size; ++i) { 889 | REQUIRE(when_all_result[i].get() == (int)i); 890 | } 891 | } 892 | 893 | SECTION("Exception in callback") { 894 | for (int i = 0; i < 1; ++i) { 895 | vec.push_back(cf::async([i] { 896 | throw std::runtime_error("Exception in callback"); 897 | return i; 898 | })); 899 | } 900 | try { 901 | auto ready_vec = cf::when_all(vec.begin(), vec.end()).get(); 902 | ready_vec[0].get(); 903 | } catch (const std::exception& e) { 904 | std::cout << e.what() << std::endl; 905 | } 906 | } 907 | } 908 | 909 | SECTION("Simple tuple") { 910 | auto when_all_result = cf::when_all( 911 | cf::async([]{ return 1; }), 912 | cf::async([]{ return cf::unit(); })).get(); 913 | REQUIRE(std::get<0>(when_all_result).get() == 1); 914 | REQUIRE(std::get<1>(when_all_result).get() == cf::unit()); 915 | } 916 | } 917 | 918 | TEST_CASE("When any") { 919 | SECTION("Simple vector") { 920 | const size_t size = 5; 921 | std::vector> vec; 922 | 923 | SECTION("Async") { 924 | for (size_t i = 0; i < size; ++i) { 925 | vec.push_back(cf::async([i] { 926 | std::this_thread::sleep_for(std::chrono::milliseconds((size - i) * 30)); 927 | return (int)i; 928 | })); 929 | } 930 | 931 | auto when_any_result= cf::when_any(vec.begin(), vec.end()).get(); 932 | REQUIRE(when_any_result.sequence.size() == size); 933 | REQUIRE(when_any_result.index == 4); 934 | REQUIRE(when_any_result.sequence[4].is_ready()); 935 | REQUIRE(when_any_result.sequence[4].get() == 4); 936 | } 937 | 938 | SECTION("Ready futures") { 939 | for (size_t i = 0; i < size; ++i) { 940 | vec.push_back(cf::make_ready_future((int)i)); 941 | } 942 | 943 | auto when_any_result= cf::when_any(vec.begin(), vec.end()).get(); 944 | REQUIRE(when_any_result.sequence.size() == size); 945 | REQUIRE(when_any_result.index == 0); 946 | REQUIRE(when_any_result.sequence[0].is_ready()); 947 | REQUIRE(when_any_result.sequence[0].get() == 0); 948 | } 949 | } 950 | 951 | SECTION("Vector") { 952 | const size_t size = 20; 953 | std::vector> vec; 954 | cf::async_thread_pool_executor executor(2); 955 | 956 | for (size_t i = 0; i < size; ++i) { 957 | vec.push_back(cf::async(executor, [i] { 958 | std::this_thread::sleep_for(std::chrono::milliseconds((i+1) * 50)); 959 | return (int)i; 960 | })); 961 | } 962 | 963 | for (size_t i = 0; i < size; ++i) { 964 | auto when_any_result = cf::when_any(vec.begin(), vec.end()).get(); 965 | REQUIRE(when_any_result.sequence[0].is_ready()); 966 | //REQUIRE(when_any_result.sequence[0].get() == i); 967 | 968 | vec.clear(); 969 | 970 | for (size_t j = 1; j < when_any_result.sequence.size(); ++j) { 971 | REQUIRE(!when_any_result.sequence[j].is_ready()); 972 | vec.push_back(std::move(when_any_result.sequence[j])); 973 | } 974 | } 975 | } 976 | 977 | SECTION("tuple async") { 978 | auto when_any_result = cf::when_any( 979 | cf::async([] { 980 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 981 | return 1; 982 | }), 983 | cf::async([] { 984 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 985 | return cf::unit(); 986 | })).get(); 987 | 988 | REQUIRE(when_any_result.index == 1); 989 | REQUIRE(std::get<1>(when_any_result.sequence).get() == cf::unit()); 990 | } 991 | 992 | SECTION("tuple ready") { 993 | auto when_any_result = cf::when_any(cf::make_ready_future(42), 994 | cf::async([] { 995 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 996 | return cf::unit(); 997 | })).get(); 998 | 999 | REQUIRE(when_any_result.index == 0); 1000 | REQUIRE(std::get<0>(when_any_result.sequence).get() == 42); 1001 | } 1002 | 1003 | SECTION("When w executors") { 1004 | cf::async_queued_executor queue_executor; 1005 | cf::async_thread_pool_executor tp_executor(1); 1006 | 1007 | auto when_any_result_future = cf::when_any( 1008 | cf::make_ready_future("Hello ").then(queue_executor, 1009 | [] (cf::future f) mutable { 1010 | std::this_thread::sleep_for(std::chrono::milliseconds(25)); 1011 | return f.get() + "composable "; 1012 | }).then(tp_executor, [] (cf::future f) mutable { 1013 | std::this_thread::sleep_for(std::chrono::milliseconds(25)); 1014 | return f.get() + "futures!"; 1015 | }), 1016 | cf::make_ready_future("Hello ").then(queue_executor, 1017 | [] (cf::future f) mutable { 1018 | std::this_thread::sleep_for(std::chrono::milliseconds(25)); 1019 | return f.get() + "composable "; 1020 | }).then(tp_executor, [] (cf::future f) mutable { 1021 | std::this_thread::sleep_for(std::chrono::milliseconds(35)); 1022 | return f.get() + "futures "; 1023 | }).then(tp_executor, [] (cf::future f) mutable { 1024 | std::this_thread::sleep_for(std::chrono::milliseconds(75)); 1025 | return f.get() + "world!"; 1026 | })); 1027 | 1028 | REQUIRE(!when_any_result_future.is_ready()); 1029 | auto when_any_result = when_any_result_future.get(); 1030 | REQUIRE(std::get<1>(when_any_result.sequence).is_ready() == false); 1031 | 1032 | REQUIRE(when_any_result.index == 0); 1033 | REQUIRE(std::get<0>(when_any_result.sequence).get() == 1034 | "Hello composable futures!"); 1035 | 1036 | auto next_when_any_result = cf::when_any( 1037 | std::move(std::get<1>(when_any_result.sequence))).get(); 1038 | 1039 | REQUIRE(next_when_any_result.index == 0); 1040 | REQUIRE(std::get<0>(next_when_any_result.sequence).get() == 1041 | "Hello composable futures world!"); 1042 | } 1043 | } 1044 | -------------------------------------------------------------------------------- /cf/cfuture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "common.h" 18 | 19 | #if !defined(__clang__) && defined (__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ <= 8) 20 | #include "cpp14_type_traits.h" 21 | #endif 22 | 23 | namespace cf { 24 | 25 | // Future and promise are explicitly forbidden. 26 | // Use cf::unit instead. 27 | struct unit {}; 28 | inline bool operator == (unit, unit) { return true; } 29 | inline bool operator != (unit, unit) { return false; } 30 | 31 | enum class future_status { 32 | ready, 33 | timeout 34 | }; 35 | 36 | enum class timeout_state { 37 | not_set, 38 | expired, 39 | result_set 40 | }; 41 | 42 | #define ERRC_LIST(APPLY) \ 43 | APPLY(broken_promise) \ 44 | APPLY(future_already_retrieved) \ 45 | APPLY(promise_already_satisfied) \ 46 | APPLY(no_state) 47 | 48 | #define ENUM_APPLY(value) value, 49 | 50 | enum class errc { 51 | ERRC_LIST(ENUM_APPLY) 52 | }; 53 | 54 | #define STRING_APPLY(value) case errc::value: return #value; 55 | 56 | inline std::string errc_string(errc value) { 57 | switch (value) { 58 | ERRC_LIST(STRING_APPLY) 59 | }; 60 | return ""; 61 | } 62 | 63 | #undef ENUM_APPLY 64 | #undef STRING_APPLY 65 | #undef ERRC_LIST 66 | 67 | struct future_error : public std::exception { 68 | future_error(errc ecode, const std::string& s) 69 | : ecode_(ecode), 70 | error_string_(s) {} 71 | 72 | virtual const char* what() const noexcept override { 73 | return error_string_.data(); 74 | } 75 | 76 | errc ecode() const { 77 | return ecode_; 78 | } 79 | 80 | errc ecode_; 81 | std::string error_string_; 82 | }; 83 | 84 | template 85 | class future; 86 | 87 | template 88 | future make_ready_future(T&& t); 89 | 90 | namespace detail { 91 | 92 | template 93 | class shared_state_base { 94 | using cb_type = movable_func; 95 | public: 96 | ~shared_state_base() {} 97 | shared_state_base() 98 | : satisfied_(false), 99 | executed_(false) 100 | {} 101 | 102 | void wait() const { 103 | std::unique_lock lock(mutex_); 104 | cond_.wait(lock, [this] { return satisfied_ == true; }); 105 | } 106 | 107 | template 108 | future_status wait_for(const std::chrono::duration& timeout) const { 109 | std::unique_lock lock(mutex_); 110 | cond_.wait_for(lock, timeout, [this] { return satisfied_ == true; }); 111 | return satisfied_ ? future_status::ready : future_status::timeout; 112 | } 113 | 114 | template 115 | future_status wait_until(const std::chrono::time_point& timepoint) const { 116 | std::unique_lock lock(mutex_); 117 | cond_.wait_until(lock, timepoint, [this] { return satisfied_ == true; }); 118 | return satisfied_ ? future_status::ready : future_status::timeout; 119 | } 120 | 121 | void set_ready(std::unique_lock& lock) { 122 | satisfied_ = true; 123 | cond_.notify_all(); 124 | if (cb_.empty()) { 125 | return; 126 | } 127 | bool need_execute = !executed_; 128 | if (!need_execute) { 129 | return; 130 | } 131 | executed_ = true; 132 | lock.unlock(); 133 | if (need_execute) { 134 | cb_(); 135 | } 136 | } 137 | 138 | template 139 | void set_callback(F&& f) { 140 | std::unique_lock lock(mutex_); 141 | cb_ = std::forward(f); 142 | bool need_execute = satisfied_ && !executed_; 143 | if (!need_execute) 144 | return; 145 | executed_ = true; 146 | lock.unlock(); 147 | if (need_execute) { 148 | cb_(); 149 | } 150 | } 151 | 152 | bool is_ready() const { 153 | std::lock_guard lock(mutex_); 154 | return satisfied_; 155 | } 156 | 157 | void set_exception(std::exception_ptr p) { 158 | std::unique_lock lock(mutex_); 159 | throw_if_satisfied(); 160 | exception_ptr_ = p; 161 | set_ready(lock); 162 | } 163 | 164 | bool has_exception() const { 165 | std::lock_guard lock(mutex_); 166 | return (bool)exception_ptr_; 167 | } 168 | 169 | std::exception_ptr get_exception() const { 170 | std::lock_guard lock(mutex_); 171 | return exception_ptr_; 172 | } 173 | 174 | void abandon() { 175 | std::unique_lock lock(mutex_); 176 | if (satisfied_) 177 | return; 178 | exception_ptr_ = std::make_exception_ptr( 179 | future_error(errc::broken_promise, 180 | errc_string(errc::broken_promise))); 181 | set_ready(lock); 182 | } 183 | 184 | std::mutex& get_timeout_mutex() { return timeout_mutex_; } 185 | 186 | timeout_state expired() const { return timeout_state_; } 187 | void expired(timeout_state state) {timeout_state_ = state; } 188 | 189 | protected: 190 | void throw_if_satisfied() { 191 | if (satisfied_) 192 | throw future_error(errc::promise_already_satisfied, 193 | errc_string(errc::promise_already_satisfied)); 194 | } 195 | 196 | protected: 197 | mutable std::mutex mutex_; 198 | mutable std::condition_variable cond_; 199 | bool satisfied_; 200 | bool executed_; 201 | std::exception_ptr exception_ptr_; 202 | cb_type cb_; 203 | timeout_state timeout_state_ = timeout_state::not_set; 204 | std::mutex timeout_mutex_; 205 | }; 206 | 207 | template 208 | class shared_state : public shared_state_base>, 209 | public std::enable_shared_from_this> { 210 | using value_type = T; 211 | using base_type = shared_state_base>; 212 | 213 | public: 214 | template 215 | void set_value(U&& value) { 216 | std::unique_lock lock(base_type::mutex_); 217 | base_type::throw_if_satisfied(); 218 | value_ = std::forward(value); 219 | base_type::set_ready(lock); 220 | } 221 | 222 | value_type get_value() { 223 | base_type::wait(); 224 | if (base_type::exception_ptr_) 225 | std::rethrow_exception(base_type::exception_ptr_); 226 | return std::move(value_); 227 | } 228 | 229 | private: 230 | value_type value_; 231 | }; 232 | 233 | template 234 | using shared_state_ptr = std::shared_ptr>; 235 | 236 | template 237 | void check_state(const shared_state_ptr& state) { 238 | if (!state) 239 | throw future_error(errc::no_state, errc_string(errc::no_state)); 240 | } 241 | 242 | // various type helpers 243 | 244 | // get the return type of a continuation callable 245 | template 246 | using then_arg_ret_type = std::invoke_result_t, future>; 247 | 248 | template 249 | using callable_ret_type = std::invoke_result_t, Args...>; 250 | 251 | template 252 | struct is_future { 253 | const static bool value = false; 254 | }; 255 | 256 | template 257 | struct is_future> { 258 | const static bool value = true; 259 | }; 260 | 261 | // future::then(F&& f) return type 262 | template 263 | using then_ret_type = std::conditional_t< 264 | is_future>::value, // if f returns future 265 | then_arg_ret_type, // then leave type untouched 266 | future> >; // else lift it into the future type 267 | 268 | template 269 | struct future_held_type; 270 | 271 | template 272 | struct future_held_type> { 273 | using type = std::decay_t; 274 | }; 275 | 276 | template 277 | void arg_type(R(*)()); 278 | 279 | template 280 | A arg_type(R(*)(A)); 281 | 282 | template 283 | void arg_type(R(C::*)()); 284 | 285 | template 286 | A arg_type(R(C::*)(A)); 287 | 288 | template 289 | void arg_type(R(C::*)() const); 290 | 291 | template 292 | A arg_type(R(C::*)(A) const); 293 | 294 | template 295 | decltype(arg_type(&F::operator())) arg_type(F f); 296 | 297 | template 298 | using arg_type_t = decltype(arg_type(std::declval())); 299 | 300 | template 301 | auto make_then_unwrap_handler(F&& f) { 302 | return [f = std::forward(f)](auto future) mutable { 303 | return std::move(f)(future.get()); 304 | }; 305 | } 306 | 307 | template 308 | auto ensure_future(U&& u) { 309 | return make_ready_future(std::forward(u)); 310 | } 311 | 312 | template 313 | auto ensure_future(future t) { 314 | return t; 315 | } 316 | 317 | template 318 | auto make_catch_handler(F&& f) { 319 | return [f = std::forward(f)](auto future) mutable { 320 | using T = decltype(future.get()); 321 | using E = arg_type_t>; 322 | try { 323 | return make_ready_future(future.get()); 324 | } catch (E e) { 325 | return ensure_future(std::move(f)(std::forward(e))); 326 | } 327 | }; 328 | } 329 | 330 | } // namespace detail 331 | 332 | template 333 | class future { 334 | template 335 | friend class promise; 336 | 337 | template 338 | friend future make_ready_future(U&& u); 339 | 340 | template 341 | friend future make_exceptional_future(std::exception_ptr p); 342 | 343 | public: 344 | using value_type = T; 345 | 346 | future() = default; 347 | 348 | future(const future& other) = delete; 349 | future& operator = (const future& other) = delete; 350 | 351 | future(future&& other) 352 | : state_(std::move(other.state_)) {} 353 | 354 | future& operator = (future&& other) { 355 | state_ = std::move(other.state_); 356 | return *this; 357 | } 358 | 359 | bool valid() const { 360 | return state_ != nullptr; 361 | } 362 | 363 | T get() { 364 | check_state(state_); 365 | return state_->get_value(); 366 | } 367 | 368 | std::exception_ptr exception() const { 369 | check_state(state_); 370 | return state_->get_exception(); 371 | } 372 | 373 | template 374 | detail::then_ret_type then(F&& f); 375 | 376 | template 377 | auto then_unwrap(F&& f); 378 | 379 | template 380 | auto catch_(F&& f); 381 | 382 | template 383 | future timeout(std::chrono::duration duration, 384 | const Exception& exception, 385 | TimeWatcher& watcher); 386 | 387 | template 388 | detail::then_ret_type then(Executor& executor, F&& f); 389 | 390 | template 391 | auto then_unwrap(Executor& executor, F&& f); 392 | 393 | template 394 | auto catch_(Executor& executor, F&& f); 395 | 396 | bool is_ready() const { 397 | check_state(state_); 398 | return state_->is_ready(); 399 | } 400 | 401 | void wait() const { 402 | check_state(state_); 403 | if (state_) 404 | state_->wait(); 405 | } 406 | 407 | template 408 | cf::future_status wait_for(const std::chrono::duration& timeout) { 409 | check_state(state_); 410 | return state_->wait_for(timeout); 411 | } 412 | 413 | template 414 | cf::future_status wait_until(const std::chrono::time_point& timepoint) { 415 | check_state(state_); 416 | return state_->wait_until(timepoint); 417 | } 418 | 419 | private: 420 | future(const detail::shared_state_ptr& state) 421 | : state_(state) {} 422 | 423 | template 424 | typename std::enable_if< 425 | detail::is_future< 426 | detail::then_arg_ret_type 427 | >::value, 428 | detail::then_ret_type 429 | >::type 430 | then_impl(F&& f); 431 | 432 | template 433 | typename std::enable_if< 434 | !detail::is_future< 435 | detail::then_arg_ret_type 436 | >::value, 437 | detail::then_ret_type 438 | >::type 439 | then_impl(F&& f); 440 | 441 | template 442 | typename std::enable_if< 443 | detail::is_future< 444 | detail::then_arg_ret_type 445 | >::value, 446 | detail::then_ret_type 447 | >::type 448 | then_impl(F&& f, Executor& executor); 449 | 450 | template 451 | typename std::enable_if< 452 | !detail::is_future< 453 | detail::then_arg_ret_type 454 | >::value, 455 | detail::then_ret_type 456 | >::type 457 | then_impl(F&& f, Executor& executor); 458 | 459 | template 460 | void set_callback(F&& f) { 461 | check_state(state_); 462 | state_->set_callback(std::forward(f)); 463 | } 464 | 465 | private: 466 | detail::shared_state_ptr state_; 467 | }; 468 | 469 | template 470 | future make_exceptional_future(std::exception_ptr p); 471 | 472 | // TODO: shared_future 473 | // TODO: T& specialization. Forbid and test. 474 | 475 | template<> 476 | class future; 477 | 478 | template 479 | class future; 480 | 481 | template 482 | template 483 | detail::then_ret_type future::then(F&& f) { 484 | check_state(state_); 485 | return then_impl(std::forward(f)); 486 | } 487 | 488 | template 489 | template 490 | auto future::then_unwrap(F&& f) { 491 | return then(detail::make_then_unwrap_handler(std::forward(f))); 492 | } 493 | 494 | template 495 | template 496 | auto future::catch_(F&& f) { 497 | return then(detail::make_catch_handler(std::forward(f))); 498 | } 499 | 500 | template 501 | template 502 | detail::then_ret_type future::then(Executor& executor, F&& f) { 503 | check_state(state_); 504 | return then_impl(std::forward(f), executor); 505 | } 506 | 507 | template 508 | template 509 | auto future::then_unwrap(Executor& executor, F&& f) { 510 | return then(executor, detail::make_then_unwrap_handler(std::forward(f))); 511 | } 512 | 513 | template 514 | template 515 | auto future::catch_(Executor& executor, F&& f) { 516 | return then(executor, detail::make_catch_handler(std::forward(f))); 517 | } 518 | 519 | template 520 | class promise; 521 | 522 | // future F(future) specialization 523 | template 524 | template 525 | typename std::enable_if< 526 | detail::is_future< 527 | detail::then_arg_ret_type 528 | >::value, 529 | detail::then_ret_type 530 | >::type 531 | future::then_impl(F&& f) { 532 | using R = typename detail::future_held_type< 533 | detail::then_arg_ret_type 534 | >::type; 535 | using S = typename std::remove_referencestate_)>::type; 536 | promise p; 537 | future ret = p.get_future(); 538 | set_callback([p = std::move(p), f = std::forward(f), 539 | state = std::weak_ptr(this->state_->shared_from_this())] () mutable { 540 | auto sp_state = state.lock(); 541 | cf::future arg_future; 542 | 543 | if (sp_state->has_exception()) { 544 | arg_future = cf::make_exceptional_future(sp_state->get_exception()); 545 | } else { 546 | arg_future = cf::make_ready_future(sp_state->get_value()); 547 | } 548 | 549 | try { 550 | auto inner_f = f(std::move(arg_future)); 551 | inner_f.then([p = std::move(p)] (cf::future f) mutable { 552 | try { 553 | p.set_value(f.get()); 554 | } catch (...) { 555 | p.set_exception(std::current_exception()); 556 | } 557 | return cf::unit(); 558 | }); 559 | } catch (...) { 560 | p.set_exception(std::current_exception()); 561 | } 562 | 563 | }); 564 | 565 | return ret; 566 | } 567 | 568 | // R F(future) specialization 569 | template 570 | template 571 | typename std::enable_if< 572 | !detail::is_future< 573 | detail::then_arg_ret_type 574 | >::value, 575 | detail::then_ret_type 576 | >::type 577 | future::then_impl(F&& f) { 578 | using R = detail::then_arg_ret_type; 579 | using S = typename std::remove_referencestate_)>::type; 580 | promise p; 581 | future ret = p.get_future(); 582 | set_callback([p = std::move(p), f = std::forward(f), 583 | state = std::weak_ptr(this->state_->shared_from_this())] () mutable { 584 | auto sp_state = state.lock(); 585 | cf::future arg_future; 586 | 587 | if (sp_state->has_exception()) { 588 | arg_future = cf::make_exceptional_future(sp_state->get_exception()); 589 | } else { 590 | arg_future = cf::make_ready_future(sp_state->get_value()); 591 | } 592 | 593 | try { 594 | auto&& result = f(std::move(arg_future)); 595 | if (sp_state->has_exception()) 596 | p.set_exception(sp_state->get_exception()); 597 | else 598 | p.set_value(std::move(result)); 599 | } catch (...) { 600 | p.set_exception(std::current_exception()); 601 | } 602 | }); 603 | 604 | return ret; 605 | } 606 | 607 | // future F(future) specialization via executor 608 | template 609 | template 610 | typename std::enable_if< 611 | detail::is_future< 612 | detail::then_arg_ret_type 613 | >::value, 614 | detail::then_ret_type 615 | >::type 616 | future::then_impl(F&& f, Executor& executor) { 617 | using R = typename detail::future_held_type< 618 | detail::then_arg_ret_type 619 | >::type; 620 | using S = typename std::remove_referencestate_)>::type; 621 | promise p; 622 | future ret = p.get_future(); 623 | set_callback([p = std::move(p), f = std::forward(f), 624 | state = std::weak_ptr(this->state_->shared_from_this()), &executor] () mutable { 625 | auto sp_state = state.lock(); 626 | cf::future arg_future; 627 | 628 | if (sp_state->has_exception()) { 629 | arg_future = cf::make_exceptional_future(sp_state->get_exception()); 630 | } else { 631 | arg_future = cf::make_ready_future(sp_state->get_value()); 632 | } 633 | 634 | auto promise_ptr = std::make_shared>(std::move(p)); 635 | auto arg_future_ptr = std::make_shared>(std::move(arg_future)); 636 | 637 | executor.post([promise_ptr, arg_future_ptr, f = std::forward(f), &executor] () mutable { 638 | try { 639 | auto inner_f = f(std::move(*arg_future_ptr)); 640 | inner_f.then(executor, [promise_ptr] (cf::future f) mutable { 641 | try { 642 | promise_ptr->set_value(f.get()); 643 | } catch (...) { 644 | promise_ptr->set_exception(std::current_exception()); 645 | } 646 | return cf::unit(); 647 | }); 648 | } catch (...) { 649 | promise_ptr->set_exception(std::current_exception()); 650 | } 651 | }); 652 | }); 653 | 654 | return ret; 655 | } 656 | 657 | // R F(future) specialization via executor 658 | template 659 | template 660 | typename std::enable_if< 661 | !detail::is_future< 662 | detail::then_arg_ret_type 663 | >::value, 664 | detail::then_ret_type 665 | >::type 666 | future::then_impl(F&& f, Executor& executor) { 667 | using R = detail::then_arg_ret_type; 668 | using S = typename std::remove_referencestate_)>::type; 669 | promise p; 670 | future ret = p.get_future(); 671 | set_callback([p = std::move(p), f = std::forward(f), 672 | state = std::weak_ptr(this->state_->shared_from_this()), &executor] () mutable { 673 | auto sp_state = state.lock(); 674 | cf::future arg_future; 675 | 676 | if (sp_state->has_exception()) { 677 | arg_future = cf::make_exceptional_future(sp_state->get_exception()); 678 | } else { 679 | arg_future = cf::make_ready_future(sp_state->get_value()); 680 | } 681 | 682 | struct local_state { 683 | promise p; 684 | F f; 685 | local_state(promise p, F f) 686 | : p(std::move(p)), 687 | f(std::move(f)) {} 688 | }; 689 | 690 | auto lstate = std::make_shared(std::move(p), std::move(f)); 691 | auto arg_future_ptr = std::make_shared>(std::move(arg_future)); 692 | 693 | executor.post([arg_future_ptr, lstate, sp_state] () mutable { 694 | try { 695 | auto&& result = lstate->f(std::move(*arg_future_ptr)); 696 | if (sp_state->has_exception()) 697 | lstate->p.set_exception(sp_state->get_exception()); 698 | else 699 | lstate->p.set_value(std::move(result)); 700 | } catch (...) { 701 | lstate->p.set_exception(std::current_exception()); 702 | } 703 | }); 704 | }); 705 | 706 | return ret; 707 | } 708 | 709 | template 710 | template 711 | future future::timeout(std::chrono::duration duration, 712 | const Exception& exception, 713 | TimeWatcher& watcher) { 714 | auto promise_ptr = std::make_shared>(); 715 | future ret = promise_ptr->get_future(); 716 | 717 | watcher.add([promise_ptr, 718 | state = this->state_->shared_from_this(), 719 | exception] () mutable { 720 | std::lock_guard lock(state->get_timeout_mutex()); 721 | if (state->expired() == timeout_state::result_set) 722 | return; 723 | state->expired(timeout_state::expired); 724 | promise_ptr->set_exception(std::make_exception_ptr(exception)); 725 | }, duration); 726 | 727 | set_callback([promise_ptr, state = this->state_->shared_from_this()] () mutable { 728 | std::lock_guard lock(state->get_timeout_mutex()); 729 | if (state->expired() == timeout_state::expired) 730 | return; 731 | state->expired(timeout_state::result_set); 732 | if (state->has_exception()) 733 | promise_ptr->set_exception(state->get_exception()); 734 | else { 735 | promise_ptr->set_value(state->get_value()); 736 | } 737 | }); 738 | 739 | return ret; 740 | } 741 | 742 | template 743 | class promise { 744 | public: 745 | promise() 746 | : state_(std::make_shared>()) {} 747 | 748 | promise(promise&& other) 749 | : state_(std::move(other.state_)) {} 750 | 751 | promise& operator = (promise&& other) { 752 | state_ = std::move(other.state_); 753 | return *this; 754 | } 755 | 756 | ~promise() { 757 | if (state_) 758 | state_->abandon(); 759 | } 760 | 761 | void swap(promise& other) noexcept { 762 | state_.swap(other.state_); 763 | } 764 | 765 | template 766 | void set_value(U&& value) { 767 | check_state(state_); 768 | state_->set_value(std::forward(value)); 769 | } 770 | 771 | future get_future() { 772 | check_state(state_); 773 | if (state_.use_count() > 1) { 774 | throw future_error(errc::future_already_retrieved, 775 | errc_string(errc::future_already_retrieved)); 776 | } 777 | return future(state_); 778 | } 779 | 780 | void set_exception(std::exception_ptr p) { 781 | check_state(state_); 782 | state_->set_exception(p); 783 | } 784 | 785 | private: 786 | detail::shared_state_ptr state_; 787 | }; 788 | 789 | template<> 790 | class promise; 791 | 792 | template 793 | class promise; 794 | 795 | template 796 | future make_ready_future(U&& u) { 797 | detail::shared_state_ptr state = 798 | std::make_shared>(); 799 | state->set_value(std::forward(u)); 800 | return future(state); 801 | } 802 | 803 | template 804 | future make_exceptional_future(std::exception_ptr p) { 805 | detail::shared_state_ptr state = 806 | std::make_shared>(); 807 | state->set_exception(p); 808 | return future(state); 809 | } 810 | 811 | #if !defined (__clang__) && defined (__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ <= 8) 812 | template 813 | future> async(F&& f) { 814 | using future_inner_type = detail::callable_ret_type; 815 | 816 | promise p; 817 | auto result = p.get_future(); 818 | 819 | std::thread([p = std::move(p), f = std::forward(f)] () mutable { 820 | try { 821 | p.set_value(std::forward(f)()); 822 | } catch (...) { 823 | p.set_exception(std::current_exception()); 824 | } 825 | }).detach(); 826 | 827 | return result; 828 | } 829 | 830 | template 831 | future> async(F&& f, Arg1&& arg1) { 832 | using future_inner_type = detail::callable_ret_type; 833 | 834 | promise p; 835 | auto result = p.get_future(); 836 | 837 | std::thread([p = std::move(p), f = std::forward(f), arg1] () mutable { 838 | try { 839 | p.set_value(std::forward(f)(arg1)); 840 | } catch (...) { 841 | p.set_exception(std::current_exception()); 842 | } 843 | }).detach(); 844 | 845 | return result; 846 | } 847 | 848 | template 849 | future> async(F&& f, Arg1&& arg1, Arg2&& arg2) { 850 | using future_inner_type = detail::callable_ret_type; 851 | 852 | promise p; 853 | auto result = p.get_future(); 854 | 855 | std::thread([p = std::move(p), f = std::forward(f), arg1, arg2] () mutable { 856 | try { 857 | p.set_value(std::forward(f)(arg1, arg2)); 858 | } catch (...) { 859 | p.set_exception(std::current_exception()); 860 | } 861 | }).detach(); 862 | 863 | return result; 864 | } 865 | 866 | template 867 | future> async(Executor& executor, F&& f) { 868 | using future_inner_type = detail::callable_ret_type; 869 | 870 | auto promise_ptr = std::make_shared>(); 871 | auto result = promise_ptr->get_future(); 872 | executor.post([promise_ptr, f = std::forward(f)] () mutable { 873 | try { 874 | promise_ptr->set_value(std::forward(f)()); 875 | } catch (...) { 876 | promise_ptr->set_exception(std::current_exception()); 877 | } 878 | }); 879 | 880 | return result; 881 | } 882 | 883 | template 884 | future> async(Executor& executor, F&& f, Arg1&& arg1) { 885 | using future_inner_type = detail::callable_ret_type; 886 | 887 | auto promise_ptr = std::make_shared>(); 888 | auto result = promise_ptr->get_future(); 889 | executor.post([promise_ptr, f = std::forward(f), arg1] () mutable { 890 | try { 891 | promise_ptr->set_value(std::forward(f)(arg1)); 892 | } catch (...) { 893 | promise_ptr->set_exception(std::current_exception()); 894 | } 895 | }); 896 | 897 | return result; 898 | } 899 | 900 | template 901 | future> 902 | async(Executor& executor, F&& f, Arg1&& arg1, Arg2&& arg2) { 903 | using future_inner_type = detail::callable_ret_type; 904 | 905 | auto promise_ptr = std::make_shared>(); 906 | auto result = promise_ptr->get_future(); 907 | executor.post([promise_ptr, f = std::forward(f), arg1, arg2] () mutable { 908 | try { 909 | promise_ptr->set_value(std::forward(f)(arg1, arg2)); 910 | } catch (...) { 911 | promise_ptr->set_exception(std::current_exception()); 912 | } 913 | }); 914 | 915 | return result; 916 | } 917 | #endif 918 | 919 | #if defined (__clang__) || defined(_MSC_VER) || \ 920 | (defined (__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || __GNUC__ >= 5)) 921 | 922 | template 923 | future> async(F&& f, Args&&... args) { 924 | using future_inner_type = detail::callable_ret_type; 925 | 926 | promise p; 927 | auto result = p.get_future(); 928 | 929 | std::thread([p = std::move(p), f = std::forward(f), args...] () mutable { 930 | try { 931 | p.set_value(std::forward(f)(args...)); 932 | } catch (...) { 933 | p.set_exception(std::current_exception()); 934 | } 935 | }).detach(); 936 | 937 | return result; 938 | } 939 | 940 | template 941 | future> async(Executor& executor, F&& f, Args&&... args) { 942 | using future_inner_type = detail::callable_ret_type; 943 | 944 | auto promise_ptr = std::make_shared>(); 945 | auto result = promise_ptr->get_future(); 946 | executor.post([promise_ptr, f = std::forward(f), args...] () mutable { 947 | try { 948 | promise_ptr->set_value(std::forward(f)(args...)); 949 | } catch (...) { 950 | promise_ptr->set_exception(std::current_exception()); 951 | } 952 | }); 953 | 954 | return result; 955 | } 956 | #endif 957 | 958 | template 959 | auto when_all(InputIt first, InputIt last) 960 | -> future::value_type>> { 961 | using argument_element_type = typename std::iterator_traits::value_type; 962 | using future_vector_type = std::vector; 963 | 964 | struct context { 965 | size_t total_futures = 0; 966 | size_t ready_futures = 0; 967 | future_vector_type future_vector; 968 | future_vector_type result; 969 | std::mutex mutex; 970 | promise p; 971 | }; 972 | 973 | auto shared_context = std::make_shared(); 974 | auto result_future = shared_context->p.get_future(); 975 | shared_context->total_futures = std::distance(first, last); 976 | shared_context->result.resize(shared_context->total_futures); 977 | size_t index = 0; 978 | 979 | if (shared_context->total_futures == 0) 980 | return cf::make_ready_future(future_vector_type()); 981 | 982 | for (; first != last; ++first, ++index) { 983 | shared_context->future_vector.emplace_back(std::move(*first)); 984 | shared_context->future_vector[index].then( 985 | [shared_context, index](argument_element_type f) mutable { 986 | std::lock_guard lock(shared_context->mutex); 987 | shared_context->result[index] = std::move(f); 988 | ++shared_context->ready_futures; 989 | if (shared_context->ready_futures == shared_context->total_futures) 990 | shared_context->p.set_value(std::move(shared_context->result)); 991 | return unit(); 992 | }); 993 | } 994 | 995 | return result_future; 996 | } 997 | 998 | namespace detail { 999 | template 1000 | void when_inner_helper(Context context, Future&& f) { 1001 | std::get(context->result) = std::move(f); 1002 | std::get(context->result).then( 1003 | [context](typename std::remove_reference::type f) { 1004 | std::lock_guard lock(context->mutex); 1005 | ++context->ready_futures; 1006 | std::get(context->result) = std::move(f); 1007 | if (context->ready_futures == context->total_futures) 1008 | context->p.set_value(std::move(context->result)); 1009 | return unit(); 1010 | }); 1011 | } 1012 | 1013 | template 1014 | void apply_helper(const Context&) {} 1015 | 1016 | template 1017 | void apply_helper(const Context& context, FirstFuture&& f, Futures&&... fs) { 1018 | detail::when_inner_helper(context, std::forward(f)); 1019 | apply_helper(context, std::forward(fs)...); 1020 | } 1021 | } 1022 | 1023 | template 1024 | auto when_all(Futures&&... futures) 1025 | -> future...>> { 1026 | using result_inner_type = std::tuple...>; 1027 | struct context { 1028 | size_t total_futures; 1029 | size_t ready_futures = 0; 1030 | result_inner_type result; 1031 | promise p; 1032 | std::mutex mutex; 1033 | }; 1034 | auto shared_context = std::make_shared(); 1035 | shared_context->total_futures = sizeof...(futures); 1036 | detail::apply_helper<0>(shared_context, std::forward(futures)...); 1037 | return shared_context->p.get_future(); 1038 | } 1039 | 1040 | template 1041 | struct when_any_result { 1042 | size_t index; 1043 | Sequence sequence; 1044 | }; 1045 | 1046 | template 1047 | auto when_any(InputIt first, InputIt last) 1048 | ->future< 1049 | when_any_result< 1050 | std::vector< 1051 | typename std::iterator_traits::value_type>>> { 1052 | using result_inner_type = 1053 | std::vector::value_type>; 1054 | using future_inner_type = when_any_result; 1055 | 1056 | struct context { 1057 | size_t total = 0; 1058 | std::atomic processed; 1059 | future_inner_type result; 1060 | promise p; 1061 | bool ready = false; 1062 | bool result_moved = false; 1063 | std::mutex mutex; 1064 | }; 1065 | 1066 | auto shared_context = std::make_shared(); 1067 | auto result_future = shared_context->p.get_future(); 1068 | shared_context->processed = 0; 1069 | shared_context->total = std::distance(first, last); 1070 | shared_context->result.sequence.reserve(shared_context->total); 1071 | size_t index = 0; 1072 | 1073 | auto first_copy = first; 1074 | for (; first_copy != last; ++first_copy) { 1075 | shared_context->result.sequence.push_back(std::move(*first_copy)); 1076 | } 1077 | 1078 | for (; first != last; ++first, ++index) { 1079 | shared_context->result.sequence[index].then( 1080 | [shared_context, index] 1081 | (typename std::iterator_traits::value_type f) mutable { 1082 | { 1083 | std::lock_guard lock(shared_context->mutex); 1084 | if (!shared_context->ready) { 1085 | shared_context->result.index = index; 1086 | shared_context->ready = true; 1087 | shared_context->result.sequence[index] = std::move(f); 1088 | if (shared_context->processed == shared_context->total && 1089 | !shared_context->result_moved) { 1090 | shared_context->p.set_value(std::move(shared_context->result)); 1091 | shared_context->result_moved = true; 1092 | } 1093 | } 1094 | } 1095 | return unit(); 1096 | }); 1097 | ++shared_context->processed; 1098 | } 1099 | 1100 | { 1101 | std::lock_guard lock(shared_context->mutex); 1102 | if (shared_context->ready && !shared_context->result_moved) { 1103 | shared_context->p.set_value(std::move(shared_context->result)); 1104 | shared_context->result_moved = true; 1105 | } 1106 | } 1107 | 1108 | return result_future; 1109 | } 1110 | 1111 | namespace detail { 1112 | template 1113 | void when_any_inner_helper(Context context) { 1114 | using ith_future_type = 1115 | std::decay_t(context->result.sequence))>; 1116 | std::get(context->result.sequence).then( 1117 | [context](ith_future_type f) { 1118 | std::lock_guard lock(context->mutex); 1119 | if (!context->ready) { 1120 | context->ready = true; 1121 | context->result.index = I; 1122 | std::get(context->result.sequence) = std::move(f); 1123 | if (context->processed == context->total && 1124 | !context->result_moved) { 1125 | context->p.set_value(std::move(context->result)); 1126 | context->result_moved = true; 1127 | } 1128 | } 1129 | return unit(); 1130 | }); 1131 | } 1132 | 1133 | template 1134 | struct when_any_helper_struct { 1135 | template 1136 | static void apply(const Context& context, std::tuple& t) { 1137 | when_any_inner_helper(context); 1138 | ++context->processed; 1139 | when_any_helper_struct::apply(context, t); 1140 | } 1141 | }; 1142 | 1143 | template 1144 | struct when_any_helper_struct { 1145 | template 1146 | static void apply(const Context&, std::tuple&) {} 1147 | }; 1148 | 1149 | template 1150 | void fill_result_helper(const Context&) {} 1151 | 1152 | template 1153 | void fill_result_helper(const Context& context, FirstFuture&& f, Futures&&... fs) { 1154 | std::get(context->result.sequence) = std::move(f); 1155 | fill_result_helper(context, std::forward(fs)...); 1156 | } 1157 | 1158 | template 1159 | auto make_initiate_handler(F&& f) { 1160 | return [f = std::forward(f)](auto future) mutable { 1161 | future.get(); 1162 | return std::move(f)(); 1163 | }; 1164 | } 1165 | 1166 | } 1167 | 1168 | template 1169 | auto when_any(Futures&&... futures) 1170 | -> future...>>> { 1171 | using result_inner_type = std::tuple...>; 1172 | using future_inner_type = when_any_result; 1173 | 1174 | struct context { 1175 | bool ready = false; 1176 | bool result_moved = false; 1177 | size_t total = 0; 1178 | std::atomic processed; 1179 | future_inner_type result; 1180 | promise p; 1181 | std::mutex mutex; 1182 | }; 1183 | 1184 | auto shared_context = std::make_shared(); 1185 | shared_context->processed = 0; 1186 | shared_context->total = sizeof...(futures); 1187 | 1188 | detail::fill_result_helper<0>(shared_context, std::forward(futures)...); 1189 | detail::when_any_helper_struct<0, sizeof...(futures)>::apply( 1190 | shared_context, shared_context->result.sequence); 1191 | { 1192 | std::lock_guard lock(shared_context->mutex); 1193 | if (shared_context->ready && !shared_context->result_moved) { 1194 | shared_context->p.set_value(std::move(shared_context->result)); 1195 | shared_context->result_moved = true; 1196 | } 1197 | } 1198 | return shared_context->p.get_future(); 1199 | } 1200 | 1201 | template 1202 | auto initiate(F&& f) { 1203 | return cf::make_ready_future(cf::unit()).then( 1204 | detail::make_initiate_handler(std::forward(f))); 1205 | } 1206 | 1207 | template 1208 | auto initiate(Executor& executor, F&& f) { 1209 | return cf::make_ready_future(cf::unit()).then(executor, 1210 | detail::make_initiate_handler(std::forward(f))); 1211 | } 1212 | 1213 | constexpr auto translate_broken_promise_to_operation_canceled = [](auto f) { 1214 | try { 1215 | return f.get(); 1216 | } catch (const future_error& e) { 1217 | if (e.ecode() != errc::broken_promise) 1218 | throw; 1219 | throw std::system_error( 1220 | (int) std::errc::operation_canceled, std::generic_category()); 1221 | } 1222 | }; 1223 | 1224 | constexpr auto discard_value = [](auto f) { 1225 | f.get(); 1226 | return cf::unit(); 1227 | }; 1228 | 1229 | } // namespace cf 1230 | --------------------------------------------------------------------------------