├── CMakeLists.txt ├── LICENSE.md ├── Readme.md ├── add_ons ├── asio │ ├── io.hpp │ └── timer.hpp ├── qt │ ├── promise_qt.cpp │ ├── promise_qt.hpp │ └── promise_qt_inl.hpp ├── simple_task │ └── simple_task.hpp └── windows │ └── promise_windows.hpp ├── doc └── lifetime.png ├── example ├── asio_benchmark_test.cpp ├── asio_http_client.cpp ├── asio_http_server.cpp ├── asio_timer.cpp ├── chain_defer_test.cpp ├── mfc_timer │ ├── CMakeLists.txt │ ├── Resource.h │ ├── framework.h │ ├── mfc_timer.cpp │ ├── mfc_timer.h │ ├── mfc_timer.sln │ ├── mfc_timer.vcxproj │ ├── mfc_timer.vcxproj.filters │ ├── mfc_timerDlg.cpp │ ├── mfc_timerDlg.h │ ├── mfctimer.rc │ ├── pch.cpp │ ├── pch.h │ ├── res │ │ ├── mfc_timer.ico │ │ └── mfctimer.rc2 │ └── targetver.h ├── multithread_test.cpp ├── qt_timer │ ├── CMakeLists.txt │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── mainwindow.ui │ └── qt_timer.pro ├── simple_benchmark_test.cpp ├── simple_timer.cpp ├── test.js └── test0.cpp ├── include └── promise-cpp │ ├── add_ons.hpp │ ├── any.hpp │ ├── call_traits.hpp │ ├── promise.hpp │ └── promise_inl.hpp └── src └── promise.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # cmake -DBOOST_ROOT=path_to_boost_include -DBOOST_LIBRARYDIR=path_to_boost_lib -DCMAKE_BUILD_TYPE=Release|Debug . 2 | 3 | 4 | cmake_minimum_required(VERSION 3.5) 5 | 6 | # make Release as default 7 | if (NOT EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt) 8 | if (NOT CMAKE_BUILD_TYPE) 9 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) 10 | endif() 11 | endif() 12 | 13 | set (CMAKE_CXX_STANDARD 11) 14 | 15 | project(promise) 16 | 17 | # build shared option 18 | option(PROMISE_BUILD_SHARED "Build shared library" OFF) 19 | option(PROMISE_BUILD_EXAMPLES "Build examples" ON) 20 | 21 | set(my_headers 22 | include/promise-cpp/promise.hpp 23 | include/promise-cpp/promise_inl.hpp 24 | include/promise-cpp/any.hpp 25 | include/promise-cpp/add_ons.hpp 26 | include/promise-cpp/call_traits.hpp 27 | ) 28 | 29 | set(my_sources 30 | src/promise.cpp 31 | Readme.md 32 | ) 33 | 34 | if(PROMISE_BUILD_SHARED OR BUILD_SHARED_LIBS) 35 | add_definitions(-DPROMISE_BUILD_SHARED) 36 | add_library(promise SHARED ${my_sources} ${my_headers}) 37 | else() 38 | add_library(promise STATIC ${my_sources} ${my_headers}) 39 | endif() 40 | 41 | target_include_directories(promise PUBLIC include .) 42 | 43 | find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets) 44 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets) 45 | if(NOT QT_FOUND) 46 | message(WARNING "QT not found, so project qt_timer will not be compiled") 47 | else() 48 | if(PROMISE_BUILD_SHARED OR BUILD_SHARED_LIBS) 49 | add_library(promise_qt SHARED ./add_ons/qt/promise_qt.cpp ${my_headers}) 50 | else() 51 | add_library(promise_qt STATIC ./add_ons/qt/promise_qt.cpp ${my_headers}) 52 | endif() 53 | target_link_libraries(promise_qt PRIVATE promise Qt${QT_VERSION_MAJOR}::Widgets) 54 | endif() 55 | 56 | if(PROMISE_BUILD_EXAMPLES) 57 | add_executable(test0 ${my_headers} example/test0.cpp) 58 | target_link_libraries(test0 PRIVATE promise) 59 | 60 | 61 | find_package(Threads) 62 | if(Threads_FOUND) 63 | add_executable(simple_timer ${my_headers} example/simple_timer.cpp) 64 | target_link_libraries(simple_timer PRIVATE promise Threads::Threads) 65 | 66 | add_executable(simple_benchmark_test ${my_headers} example/simple_benchmark_test.cpp) 67 | target_link_libraries(simple_benchmark_test PRIVATE promise Threads::Threads) 68 | 69 | add_executable(multithread_test ${my_headers} example/multithread_test.cpp) 70 | target_link_libraries(multithread_test PRIVATE promise Threads::Threads) 71 | endif() 72 | 73 | add_executable(chain_defer_test ${my_headers} example/chain_defer_test.cpp) 74 | target_link_libraries(chain_defer_test PRIVATE promise) 75 | 76 | find_package(Boost) 77 | if(NOT Boost_FOUND) 78 | message(WARNING "Boost not found, so asio projects will not be compiled") 79 | else() 80 | 81 | include_directories(${Boost_INCLUDE_DIRS}) 82 | if (UNIX) 83 | link_libraries(pthread) 84 | endif (UNIX) 85 | 86 | include_directories(. ./add_ons/asio) 87 | 88 | add_executable(asio_benchmark_test ${my_headers} example/asio_benchmark_test.cpp) 89 | target_compile_definitions(asio_benchmark_test PRIVATE BOOST_ALL_NO_LIB) 90 | target_link_libraries(asio_benchmark_test PRIVATE promise) 91 | 92 | add_executable(asio_timer ${my_headers} example/asio_timer.cpp) 93 | target_compile_definitions(asio_timer PRIVATE BOOST_ALL_NO_LIB) 94 | target_link_libraries(asio_timer PRIVATE promise) 95 | 96 | add_executable(asio_http_client ${my_headers} example/asio_http_client.cpp) 97 | target_compile_definitions(asio_http_client PRIVATE BOOST_ALL_NO_LIB) 98 | target_link_libraries(asio_http_client PRIVATE promise) 99 | 100 | add_executable(asio_http_server ${my_headers} example/asio_http_server.cpp) 101 | target_compile_definitions(asio_http_server PRIVATE BOOST_ALL_NO_LIB) 102 | target_link_libraries(asio_http_server PRIVATE promise) 103 | endif() 104 | 105 | if(QT_FOUND) 106 | add_subdirectory(./example/qt_timer) 107 | endif() 108 | 109 | find_package(MFC) 110 | if(NOT MFC_FOUND) 111 | message(WARNING "MFC not found, so project mfc_timer will not be compiled") 112 | else() 113 | add_subdirectory(./example/mfc_timer) 114 | endif() 115 | endif() 116 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 xhawk18 -at- gmail.com 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 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 散了吧,cpp20 coroutine了,这么代码已经没有意义 -- 作者 2 | 3 | # C++ promise/A+ library in Javascript style. 4 | 5 | 6 | - [What is promise-cpp ?](#what-is-promise-cpp-) 7 | - [Features](#features) 8 | - [Examples](#examples) 9 | - [Examples list](#examples-list) 10 | - [Compiler required](#compiler-required) 11 | - [Usage](#usage) 12 | - [Used as header only library](#used-as-header-only-library) 13 | - [Used as static library](#used-as-static-library) 14 | - [Used as shared library](#used-as-shared-library) 15 | - [Build tips about asio examples](#build-tips-about-asio-examples) 16 | - [Sample code 1](#sample-code-1) 17 | - [Sample code 2](#sample-code-2) 18 | - [Global functions](#global-functions) 19 | - [Promise newPromise(FUNC func);](#promise-newpromisefunc-func) 20 | - [Promise resolve(const RET_ARG... &ret_arg);](#promise-resolveconst-ret_arg-ret_arg) 21 | - [Promise reject(const RET_ARG... &ret_arg);](#promise-rejectconst-ret_arg-ret_arg) 22 | - [Promise all(const PROMISE_LIST &promise_list);](#promise-allconst-promise_list-promise_list) 23 | - [Promise race(const PROMISE_LIST &promise_list);](#promise-raceconst-promise_list-promise_list) 24 | - [Promise raceAndReject(const PROMISE_LIST &promise_list);](#promise-raceandrejectconst-promise_list-promise_list) 25 | - [Promise raceAndResolve(const PROMISE_LIST &promise_list);](#promise-raceandresolveconst-promise_list-promise_list) 26 | - [Promise doWhile(FUNC func);](#promise-dowhilefunc-func) 27 | - [Class Promise - type of promise object](#class-promise---type-of-promise-object) 28 | - [Promise::then(FUNC_ON_RESOLVED on_resolved, FUNC_ON_REJECTED on_rejected)](#promisethenfunc_on_resolved-on_resolved-func_on_rejected-on_rejected) 29 | - [Promise::then(FUNC_ON_RESOLVED on_resolved)](#promisethenfunc_on_resolved-on_resolved) 30 | - [Promise::then(Defer d)](#promisethendefer-d) 31 | - [Promise::then(DeferLoop d)](#promisethendeferloop-d) 32 | - [Promise::then(Promise promise)](#promisethenpromise-promise) 33 | - [Promise::fail(FUNC_ON_REJECTED on_rejected)](#promisefailfunc_on_rejected-on_rejected) 34 | - [Promise::finally(FUNC_ON_FINALLY on_finally)](#promisefinallyfunc_on_finally-on_finally) 35 | - [Promise::always(FUNC_ON_ALWAYS on_always)](#promisealwaysfunc_on_always-on_always) 36 | - [Class Defer - type of callback object for promise object.](#class-defer---type-of-callback-object-for-promise-object) 37 | - [Defer::resolve(const RET_ARG... &ret_arg);](#deferresolveconst-ret_arg-ret_arg) 38 | - [Defer::reject(const RET_ARG... &ret_arg);](#deferrejectconst-ret_arg-ret_arg) 39 | - [Class DeferLoop - type of callback object for doWhile.](#class-deferloop---type-of-callback-object-for-dowhile) 40 | - [DeferLoop::doContinue();](#deferloopdocontinue) 41 | - [DeferLoop::doBreak(const RET_ARG... &ret_arg);](#deferloopdobreakconst-ret_arg-ret_arg) 42 | - [And more ...](#and-more-) 43 | - [About exceptions](#about-exceptions) 44 | - [About the chaining parameter](#about-the-chaining-parameter) 45 | - [Match rule for chaining parameters](#match-rule-for-chaining-parameters) 46 | - [Resolved parameters](#resolved-parameters) 47 | - [Rejected parameters](#rejected-parameters) 48 | - [Omit parameters](#omit-parameters) 49 | - [Copy the promise object](#copy-the-promise-object) 50 | - [Life time of the internal storage inside a promise chain](#life-time-of-the-internal-storage-inside-a-promise-chain) 51 | - [Handle uncaught exceptional or rejected parameters](#handle-uncaught-exceptional-or-rejected-parameters) 52 | - [about multithread](#about-multithread) 53 | 54 | 55 | ## What is promise-cpp ? 56 | 57 | Promise-cpp is library that implements promise/A+ standard, which can be the base component in event-looped asynchronized programming. It is NOT std::promise. 58 | 59 | ## Features 60 | 61 | Similar to Javascript Promise API. 62 | 63 | Type safety: the resolved/rejected arguments can be captured by the "then" function with same arguments type. 64 | 65 | Exceptions supports: cpp exception will be received by the "on_rejected" function. 66 | 67 | Optional header-only configuration enabled with the PROMISE_HEADONLY macro 68 | 69 | Easy to use, just #include "promise-cpp/promise.hpp" is enough, code based on standard c++11 syntax, no external dependencies required. 70 | 71 | Easy to integrate with other libararies (see examples of [asio](example/asio_timer.cpp), [qt](example/qt_timer) and [mfc](example/mfc_timer)). 72 | 73 | Useful extended functions on promise object: doWhile, raceAndResolve, raceAndReject 74 | 75 | ## Examples 76 | 77 | ### Examples list 78 | 79 | * [example/test0.cpp](example/test0.cpp): a simple test code for promise resolve/reject operations. (no dependencies) 80 | 81 | * [example/simple_timer.cpp](example/simple_timer.cpp): simple promisified timer. (no dependencies) 82 | 83 | * [example/simple_benchmark_test.cpp](example/simple_benchmark_test.cpp): benchmark test for simple promisified asynchronized tasks. (no dependencies) 84 | 85 | * [example/asio_timer.cpp](example/asio_timer.cpp): promisified timer based on asio callback timer. (boost::asio required) 86 | 87 | * [example/asio_benchmark_test.cpp](example/asio_benchmark_test.cpp): benchmark test for promisified asynchronized tasks in asio. (boost::asio required) 88 | 89 | * [example/asio_http_client.cpp](example/asio_http_client.cpp): promisified flow for asynchronized http client. (boost::asio, boost::beast required) 90 | 91 | * [example/asio_http_server.cpp](example/asio_http_server.cpp): promisified flow for asynchronized http server. (boost::asio, boost::beast required) 92 | 93 | * [example/qt_timer](example/qt_timer): promisified timer in QT gui thread. (QT required) 94 | 95 | * [example/mfc_timer](example/mfc_timer): promisified timer in windows MFC gui thread. 96 | 97 | Please use cmake to build from [CMakeLists.txt](CMakeLists.txt). 98 | 99 | ### Compiler required 100 | 101 | The library has passed test on these compilers -- 102 | 103 | * gcc 5 104 | 105 | * Visual studio 2015 sp3 106 | 107 | * clang 3.4.2 108 | 109 | ### Usage 110 | 111 | #### Used as header only library 112 | 113 | To use as header only library, just define macro PROMISE_HEADONLY when compiling. 114 | 115 | #### Used as static library 116 | 117 | ``` 118 | cmake /path/to/promise_source 119 | ``` 120 | 121 | #### Used as shared library 122 | 123 | ``` 124 | cmake -DPROMISE_BUILD_SHARED=ON /path/to/promise_source 125 | ``` 126 | 127 | #### Build tips about asio examples 128 | 129 | Some of the [examples](example) use boost::asio as io service, and use boost::beast as http service. 130 | You need to [boost_1_66](https://www.boost.org/doc/libs/1_66_0/more/getting_started/index.html) 131 | or higher to build these examples. 132 | 133 | For examples, you can build with boost library -- 134 | 135 | ``` 136 | > cmake -DBOOST_ROOT=/path/to/boost_source /path/to/promise_source 137 | ``` 138 | 139 | ### Sample code 1 140 | 141 | Example of asio http client. [(full code here)](example/asio_http_client.cpp) 142 | 143 | ```cpp 144 | int main(int argc, char** argv) { 145 | // The io_context is required for all I/O 146 | asio::io_context ioc; 147 | 148 | // Launch the asynchronous operation 149 | download(ioc, "http://www.163.com/") 150 | .then([&]() { 151 | return download(ioc, "http://baidu.com/"); 152 | }).then([&]() { 153 | return download(ioc, "http://qq.com"); 154 | }).then([&]() { 155 | return download(ioc, "http://github.com/xhawk18"); 156 | }); 157 | 158 | // Run the I/O service. The call will return when 159 | // the get operation is complete. 160 | ioc.run(); 161 | 162 | return 0; 163 | } 164 | ``` 165 | 166 | ### Sample code 2 167 | 168 | This sample code shows converting a timer callback to promise object. 169 | 170 | ```cpp 171 | #include 172 | #include 173 | #include "add_ons/asio/timer.hpp" 174 | 175 | using namespace promise; 176 | using namespace boost::asio; 177 | 178 | /* Convert callback to a promise */ 179 | Promise myDelay(boost::asio::io_service &io, uint64_t time_ms) { 180 | return newPromise([&io, time_ms](Defer &d) { 181 | setTimeout(io, [d](bool cancelled) { 182 | if (cancelled) 183 | d.reject(); 184 | else 185 | d.resolve(); 186 | }, time_ms); 187 | }); 188 | } 189 | 190 | 191 | Promise testTimer(io_service &io) { 192 | 193 | return myDelay(io, 3000).then([&] { 194 | printf("timer after 3000 ms!\n"); 195 | return myDelay(io, 1000); 196 | }).then([&] { 197 | printf("timer after 1000 ms!\n"); 198 | return myDelay(io, 2000); 199 | }).then([] { 200 | printf("timer after 2000 ms!\n"); 201 | }).fail([] { 202 | printf("timer cancelled!\n"); 203 | }); 204 | } 205 | 206 | int main() { 207 | io_service io; 208 | 209 | Promise timer = testTimer(io); 210 | 211 | delay(io, 4500).then([=] { 212 | printf("clearTimeout\n"); 213 | clearTimeout(timer); 214 | }); 215 | 216 | io.run(); 217 | return 0; 218 | } 219 | ``` 220 | 221 | ## Global functions 222 | 223 | ### Promise newPromise(FUNC func); 224 | Creates a new promise object with a user-defined function. 225 | The user-defined functions, used as parameters by newPromise, must have a parameter Defer d. 226 | for example -- 227 | 228 | ```cpp 229 | return newPromise([](Defer d){ 230 | }) 231 | ``` 232 | 233 | ### Promise resolve(const RET_ARG... &ret_arg); 234 | Returns a promise that is resolved with the given value. 235 | for example -- 236 | 237 | ```cpp 238 | return resolve(3, '2'); 239 | ``` 240 | 241 | ### Promise reject(const RET_ARG... &ret_arg); 242 | Returns a promise that is rejected with the given arguments. 243 | for example -- 244 | 245 | ```cpp 246 | return reject("some_error"); 247 | ``` 248 | 249 | ### Promise all(const PROMISE_LIST &promise_list); 250 | Wait until all promise objects in "promise_list" are resolved or one of which is rejected. 251 | The "promise_list" can be any container that has promise object as element type. 252 | 253 | > for (Promise &promise : promise_list) { ... } 254 | 255 | for example -- 256 | 257 | ```cpp 258 | Promise d0 = newPromise([](Defer d){ /* ... */ }); 259 | Promise d1 = newPromise([](Defer d){ /* ... */ }); 260 | std::vector promise_list = { d0, d1 }; 261 | 262 | all(promise_list).then([](){ 263 | /* code here for all promise objects are resolved */ 264 | }).fail([](){ 265 | /* code here for one of the promise objects is rejected */ 266 | }); 267 | ``` 268 | 269 | ### Promise race(const PROMISE_LIST &promise_list); 270 | Returns a promise that resolves or rejects as soon as one of 271 | the promises in the iterable resolves or rejects, with the value 272 | or reason from that promise. 273 | The "promise_list" can be any container that has promise object as element type. 274 | 275 | > for (Promise &promise : promise_list) { ... } 276 | 277 | for example -- 278 | 279 | ```cpp 280 | Promise d0 = newPromise([](Defer d){ /* ... */ }); 281 | Promise d1 = newPromise([](Defer d){ /* ... */ }); 282 | std::vector promise_list = { d0, d1 }; 283 | 284 | race(promise_list).then([](){ 285 | /* code here for one of the promise objects is resolved */ 286 | }).fail([](){ 287 | /* code here for one of the promise objects is rejected */ 288 | }); 289 | ``` 290 | 291 | ### Promise raceAndReject(const PROMISE_LIST &promise_list); 292 | Same as function race(), and reject all depending promises object in the list. 293 | 294 | ### Promise raceAndResolve(const PROMISE_LIST &promise_list); 295 | Same as function race(), and resove all depending promises object in the list. 296 | 297 | ### Promise doWhile(FUNC func); 298 | "While loop" for promisied task. 299 | A promise object will passed as parameter when call func, which can be resolved to continue with the "while loop", or be rejected to break from the "while loop". 300 | 301 | for example -- 302 | 303 | ```cpp 304 | doWhile([](DeferLoop d){ 305 | // Add code here for your task in "while loop" 306 | 307 | // Call "d.doContinue()" to continue with the "while loop", 308 | 309 | // or call "d.doBreak()" to break from the "while loop", in this case, 310 | // the returned promise object will be in resolved status. 311 | }); 312 | 313 | ``` 314 | 315 | ## Class Promise - type of promise object 316 | 317 | ### Promise::then(FUNC_ON_RESOLVED on_resolved, FUNC_ON_REJECTED on_rejected) 318 | Return the chaining promise object, where on_resolved is the function to be called when 319 | previous promise object was resolved, on_rejected is the function to be called 320 | when previous promise object was rejected. 321 | for example -- 322 | 323 | ```cpp 324 | return newPromise([](Defer d){ 325 | d.resolve(9567, 'A'); 326 | }).then( 327 | 328 | /* function on_resolved */ [](int n, char ch){ 329 | printf("%d %c\n", n, ch); //will print 9567 here 330 | }, 331 | 332 | /* function on_rejected */ [](){ 333 | printf("promise rejected\n"); //will not run to here in this code 334 | } 335 | ); 336 | ``` 337 | 338 | ### Promise::then(FUNC_ON_RESOLVED on_resolved) 339 | Return the chaining promise object, where on_resolved is the function to be called when 340 | previous promise object was resolved. 341 | for example -- 342 | 343 | ```cpp 344 | return newPromise([](Defer d){ 345 | d.resolve(9567); 346 | }).then([](int n){ 347 | printf("%d\n", n); b //will print 9567 here 348 | }); 349 | ``` 350 | 351 | ### Promise::then(Defer d) 352 | Return the chaining promise object, where d is the callback function be called when 353 | previous promise object was resolved or rejected. 354 | 355 | ### Promise::then(DeferLoop d) 356 | Return the chaining promise object, where d is the callback function be called when 357 | previous promise object was resolved or rejected. 358 | 359 | ### Promise::then(Promise promise) 360 | Return the chaining promise object, where "promise" is the promise object be called when 361 | previous promise object was resolved or rejected. 362 | 363 | ### Promise::fail(FUNC_ON_REJECTED on_rejected) 364 | Return the chaining promise object, where on_rejected is the function to be called when 365 | previous promise object was rejected. 366 | 367 | This function is usually named "catch" in most implements of Promise library. 368 | https://www.promisejs.org/api/ 369 | 370 | In promise_cpp, function name "fail" is used instead of "catch", since "catch" is a keyword of c++. 371 | 372 | for example -- 373 | 374 | ```cpp 375 | return newPromise([](Defer d){ 376 | d.reject(-1, std::string("oh, no!")); 377 | }).fail([](int err, string &str){ 378 | printf("%d, %s\n", err, str.c_str()); //will print "-1, oh, no!" here 379 | }); 380 | ``` 381 | 382 | ### Promise::finally(FUNC_ON_FINALLY on_finally) 383 | Return the chaining promise object, where on_finally is the function to be called whenever 384 | the previous promise object was resolved or rejected. 385 | 386 | The returned promise object will keeps the resolved/rejected state of current promise object. 387 | 388 | for example -- 389 | 390 | ```cpp 391 | return newPromise([](Defer d){ 392 | d.reject(std::string("oh, no!")); 393 | }).finally([](){ 394 | printf("in finally\n"); //will print "in finally" here 395 | }); 396 | ``` 397 | 398 | ### Promise::always(FUNC_ON_ALWAYS on_always) 399 | Return the chaining promise object, where on_always is the function to be called whenever 400 | the previous promise object was resolved or rejected. 401 | 402 | The returned promise object will be in resolved state whenever current promise object is 403 | resolved or rejected. 404 | 405 | for example -- 406 | 407 | ```cpp 408 | return newPromise([](Defer d){ 409 | d.reject(std::string("oh, no!")); 410 | }).always([](){ 411 | printf("in always\n"); //will print "in always" here 412 | }); 413 | ``` 414 | 415 | ## Class Defer - type of callback object for promise object. 416 | 417 | ### Defer::resolve(const RET_ARG... &ret_arg); 418 | Resolve the promise object with arguments, where you can put any number of ret_arg with any type. 419 | (Please be noted that it is a method of Defer object, which is different from the global resolve function.) 420 | for example -- 421 | 422 | ```cpp 423 | return newPromise([](Defer d){ 424 | //d.resolve(); 425 | //d.resolve(3, '2', std::string("abcd")); 426 | d.resolve(9567); 427 | }) 428 | ``` 429 | 430 | ### Defer::reject(const RET_ARG... &ret_arg); 431 | Reject the promise object with arguments, where you can put any number of ret_arg with any type. 432 | (Please be noted that it is a method of Defer object, which is different from the global reject function.) 433 | for example -- 434 | 435 | ```cpp 436 | return newPromise([](Defer d){ 437 | //d.reject(); 438 | //d.reject(std::string("oh, no!")); 439 | d.reject(-1, std::string("oh, no!")) 440 | }) 441 | ``` 442 | 443 | ## Class DeferLoop - type of callback object for doWhile. 444 | 445 | ### DeferLoop::doContinue(); 446 | Continue the doWhile loop. 447 | 448 | for example -- 449 | 450 | ```cpp 451 | static int *i = new int(0); 452 | doWhile([i](DeferLoop d) { 453 | if(*i < 10) { 454 | ++ (*i); 455 | d.doContinue(); 456 | } 457 | else { 458 | d.doBreak(*i); 459 | } 460 | 461 | }).then([](int result) { 462 | printf("result = %d\n", result); 463 | }).finally([i]() { 464 | delete i; 465 | }) 466 | ``` 467 | 468 | ### DeferLoop::doBreak(const RET_ARG... &ret_arg); 469 | Break the doWhile loop (ret_arg will be transferred). 470 | 471 | (please see the example above) 472 | 473 | ## And more ... 474 | 475 | ### About exceptions 476 | To throw any object in the callback functions above, including on_resolved, on_rejected, on_always, 477 | will same as d.reject(the_throwed_object) and returns immediately. 478 | for example -- 479 | 480 | ```cpp 481 | return newPromise([](Defer d){ 482 | throw std::string("oh, no!"); 483 | }).fail([](string &str){ 484 | printf("%s\n", str.c_str()); //will print "oh, no!" here 485 | }); 486 | ``` 487 | For the better performance, we suggest to use function reject instead of throw. 488 | 489 | ### About the chaining parameter 490 | Any type of parameter can be used when call resolve, reject or throw, except that the plain string or array. 491 | To use plain string or array as chaining parameters, we may wrap it into an object. 492 | 493 | ```cpp 494 | newPromise([](Defer d){ 495 | // d.resolve("ok"); may cause a compiling error, use the following code instead. 496 | d.resolve(std::string("ok")); 497 | }) 498 | ``` 499 | 500 | ### Match rule for chaining parameters 501 | 502 | "then" and "fail" function can accept multiple promise parameters and they follows the below rule -- 503 | 504 | #### Resolved parameters 505 | 506 | Resolved parameters must match the next "then" function, otherwise it will throw an exception and can be caught by the following "fail" function. 507 | 508 | #### Rejected parameters 509 | 510 | First let's take a look at the rule of c++ try/catch, in which the thrown value will be caught in the block where value type is matched. 511 | If type in the catch block can not be matched, it will run into the default block catch(...) { }. 512 | 513 | ```cpp 514 | try{ 515 | throw (short)1; 516 | }catch(int a){ 517 | // will not go to here 518 | }catch(short b){ 519 | // (short)1 will be caught here 520 | }catch(...){ 521 | // will not go to here 522 | } 523 | ``` 524 | 525 | "Promise-cpp" implement "fail" chain as the match style of try/catch. 526 | 527 | ```cpp 528 | newPromise([](Defer d){ 529 | d.reject(3, 5, 6); 530 | }).fail([](std::string str){ 531 | // will not go to here since parameter types are not match 532 | }).fail([](const int &a, int b, int c) { 533 | // d.reject(3, 5, 6) will be caught here 534 | }).fail([](){ 535 | // Will not go to here sinace previous rejected promise was caught. 536 | }); 537 | ``` 538 | 539 | #### Omit parameters 540 | 541 | The number of parameters in "then" or "fail" chain can be lesser than that's in resolve function. 542 | ```cpp 543 | newPromise([](Defer d){ 544 | d.resolve(3, 5, 6); 545 | }).then([](int a, int b) { 546 | // d.resolve(3, 5, 6) will be caught here since a, b matched with the resolved parameters and ignore the 3rd parameter. 547 | }); 548 | ``` 549 | 550 | A function in "then" chain without any parameters can be used as default promise caught function. 551 | ```cpp 552 | newPromise([](Defer d){ 553 | d.resolve(3, 5, 6); 554 | }).then([]() { 555 | // Function without parameters will be matched with any resolved values, 556 | // so d.resolve(3, 5, 6) will be caught here. 557 | }); 558 | ``` 559 | 560 | The reject parameters follows the the same omit rule as resolved parameters. 561 | 562 | ### Copy the promise object 563 | To copy the promise object is allowed and effective, please do that when you need. 564 | 565 | ```cpp 566 | Promise promise = newPromise([](Defer d){}); 567 | Promise promise2 = promise; //It's safe and effective 568 | ``` 569 | 570 | ### Life time of the internal storage inside a promise chain 571 | 572 | The library uses std::shared_ptr to maintain the internal object of task and task chain. 573 | Resources of a task will be released after the task is finished (in resolved or rejected status) and not obtained by Defer or DeferLoop objects. 574 | Resources of a promise chain will be released when it is not obtained by any Promise, Defer or DeferLoop objects. 575 | 576 | ![lifetime](./doc/lifetime.png) 577 | 578 | ### Handle uncaught exceptional or rejected parameters 579 | 580 | The uncaught exceptional or rejected parameters are ignored by default. We can specify a handler function to do with these parameters -- 581 | 582 | ``` 583 | handleUncaughtException([](Promise &d) { 584 | d.fail([](int n, int m) { 585 | //go here if the uncaught parameters match types "int n, int m". 586 | }).fail([](char c) { 587 | //go here if the uncaught parameters match type "char c". 588 | }).fail([]() { 589 | //go here for all other uncaught parameters. 590 | }); 591 | }); 592 | ``` 593 | 594 | ### about multithread 595 | 596 | This library is thread safe by default. However, it is strongly recommented to use this library on single thread, 597 | especially when you don't clearly know about which thread will runs the chain tasks. 598 | 599 | For better performance, we can also disable multithread by adding macro PROMISE_MULTITHREAD=0 600 | 601 | -------------------------------------------------------------------------------- /add_ons/asio/io.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, xhawk18 3 | * at gmail.com 4 | * 5 | * The MIT License (MIT) 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #pragma once 27 | #ifndef INC_ASIO_IO_HPP_ 28 | #define INC_ASIO_IO_HPP_ 29 | 30 | #include "promise-cpp/promise.hpp" 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace promise{ 37 | 38 | template 39 | inline void setPromise(Defer defer, 40 | boost::system::error_code err, 41 | const char *errorString, 42 | const RESULT &result) { 43 | if (err) { 44 | std::cerr << errorString << ": " << err.message() << "\n"; 45 | defer.reject(err); 46 | } 47 | else 48 | defer.resolve(result); 49 | } 50 | 51 | // Promisified functions 52 | template 53 | inline Promise async_resolve( 54 | Resolver &resolver, 55 | const std::string &host, const std::string &port) { 56 | return newPromise([&](Defer &defer) { 57 | // Look up the domain name 58 | resolver.async_resolve( 59 | host, 60 | port, 61 | [defer](boost::system::error_code err, 62 | typename Resolver::results_type results) { 63 | setPromise(defer, err, "resolve", results); 64 | }); 65 | }); 66 | } 67 | 68 | template 69 | inline Promise async_connect( 70 | Socket &socket, 71 | const ResolverResult &results) { 72 | return newPromise([&](Defer &defer) { 73 | // Make the connection on the IP address we get from a lookup 74 | boost::asio::async_connect( 75 | socket, 76 | results.begin(), 77 | results.end(), 78 | [defer](boost::system::error_code err, 79 | typename ResolverResult::iterator i) { 80 | setPromise(defer, err, "connect", i); 81 | }); 82 | }); 83 | } 84 | 85 | 86 | 87 | template 88 | inline Promise async_read(Stream &stream, 89 | Buffer &buffer, 90 | Content &content) { 91 | //read 92 | return newPromise([&](Defer &defer) { 93 | boost::beast::http::async_read(stream, buffer, content, 94 | [defer](boost::system::error_code err, 95 | std::size_t bytes_transferred) { 96 | setPromise(defer, err, "read", bytes_transferred); 97 | }); 98 | }); 99 | } 100 | 101 | template 102 | inline Promise async_write(Stream &stream, Content &content) { 103 | return newPromise([&](Defer &defer) { 104 | //write 105 | boost::beast::http::async_write(stream, content, 106 | [defer](boost::system::error_code err, 107 | std::size_t bytes_transferred) { 108 | setPromise(defer, err, "write", bytes_transferred); 109 | }); 110 | }); 111 | } 112 | 113 | 114 | } 115 | #endif 116 | -------------------------------------------------------------------------------- /add_ons/asio/timer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, xhawk18 3 | * at gmail.com 4 | * 5 | * The MIT License (MIT) 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #pragma once 27 | #ifndef INC_ASIO_TIMER_HPP_ 28 | #define INC_ASIO_TIMER_HPP_ 29 | 30 | // 31 | // Promisified timer based on promise-cpp and boost::asio 32 | // 33 | // Functions -- 34 | // Promise yield(boost::asio::io_service &io); 35 | // Promise delay(boost::asio::io_service &io, uint64_t time_ms); 36 | // void cancelDelay(Promise promise); 37 | // 38 | // Promise setTimeout(boost::asio::io_service &io, 39 | // const std::function &func, 40 | // uint64_t time_ms); 41 | // void clearTimeout(Promise promise); 42 | // 43 | 44 | #include "promise-cpp/promise.hpp" 45 | #include 46 | #include 47 | #include 48 | 49 | namespace promise { 50 | 51 | inline Promise yield(boost::asio::io_service &io){ 52 | auto promise = newPromise([&io](Defer &defer) { 53 | #if BOOST_VERSION >= 106600 54 | boost::asio::defer(io, [defer]() { 55 | defer.resolve(); 56 | }); 57 | #else 58 | io.post([defer]() { 59 | defer.resolve(); 60 | }); 61 | #endif 62 | }); 63 | 64 | return promise; 65 | } 66 | 67 | inline Promise delay(boost::asio::io_service &io, uint64_t time_ms) { 68 | auto timer = std::make_shared(io, std::chrono::milliseconds(time_ms)); 69 | return newPromise([timer, &io](Defer &defer) { 70 | timer->async_wait([defer, timer](const boost::system::error_code& error_code) { 71 | if (timer) { 72 | //timer = nullptr; 73 | defer.resolve(); 74 | } 75 | }); 76 | }).finally([timer]() { 77 | timer->cancel(); 78 | }); 79 | } 80 | 81 | inline void cancelDelay(Promise promise) { 82 | promise.reject(); 83 | } 84 | 85 | inline Promise setTimeout(boost::asio::io_service &io, 86 | const std::function &func, 87 | uint64_t time_ms) { 88 | return delay(io, time_ms).then([func]() { 89 | func(false); 90 | }, [func]() { 91 | func(true); 92 | }); 93 | } 94 | 95 | inline void clearTimeout(Promise promise) { 96 | cancelDelay(promise); 97 | } 98 | 99 | #if 0 100 | inline Promise wait(boost::asio::io_service &io, Defer d, uint64_t time_ms) { 101 | return newPromise([&io, d, time_ms](Defer &dTimer) { 102 | boost::asio::steady_timer *timer = 103 | pm_new(io, std::chrono::milliseconds(time_ms)); 104 | dTimer->any_ = timer; 105 | 106 | d.finally([=](){ 107 | if (!dTimer->any_.empty()) { 108 | boost::asio::steady_timer *timer = any_cast(dTimer->any_); 109 | dTimer->any_.clear(); 110 | timer->cancel(); 111 | pm_delete(timer); 112 | } 113 | }).then(dTimer); 114 | 115 | timer->async_wait([=](const boost::system::error_code& error_code) { 116 | if (!dTimer->any_.empty()) { 117 | boost::asio::steady_timer *timer = any_cast(dTimer->any_); 118 | dTimer->any_.clear(); 119 | pm_delete(timer); 120 | d.reject(boost::system::errc::make_error_code(boost::system::errc::timed_out)); 121 | } 122 | }); 123 | }); 124 | } 125 | #endif 126 | 127 | 128 | } 129 | #endif 130 | -------------------------------------------------------------------------------- /add_ons/qt/promise_qt.cpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PROMISE_HEADONLY 3 | #include "promise_qt_inl.hpp" 4 | #endif 5 | 6 | -------------------------------------------------------------------------------- /add_ons/qt/promise_qt.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, xhawk18 3 | * at gmail.com 4 | * 5 | * The MIT License (MIT) 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #pragma once 27 | #ifndef INC_PROMISE_QT_HPP_ 28 | #define INC_PROMISE_QT_HPP_ 29 | 30 | // 31 | // Promisified timer based on promise-cpp and QT 32 | // 33 | // Functions -- 34 | // Defer yield(QWidget *widget); 35 | // Defer delay(QWidget *widget, uint64_t time_ms); 36 | // void cancelDelay(Defer d); 37 | // 38 | // Defer setTimeout(QWidget *widget, 39 | // const std::function &func, 40 | // uint64_t time_ms); 41 | // void clearTimeout(Defer d); 42 | // 43 | 44 | #include "promise-cpp/promise.hpp" 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #ifdef PROMISE_HEADONLY 53 | #define PROMISE_QT_API inline 54 | #elif defined PROMISE_BUILD_SHARED 55 | 56 | #if defined(_WIN32) || defined(__CYGWIN__) 57 | # if defined(promise_qt_EXPORTS) // add by CMake 58 | # ifdef __GNUC__ 59 | # define PROMISE_QT_API __attribute__(dllexport) 60 | # else 61 | # define PROMISE_QT_API __declspec(dllexport) 62 | # endif 63 | # else 64 | # ifdef __GNUC__ 65 | # define PROMISE_QT_API __attribute__(dllimport) 66 | # else 67 | # define PROMISE_QT_API __declspec(dllimport) 68 | # endif 69 | # endif // promise_qt_EXPORTS 70 | 71 | #elif defined __GNUC__ 72 | # if __GNUC__ >= 4 73 | # define PROMISE_QT_API __attribute__ ((visibility ("default"))) 74 | # else 75 | # define PROMISE_QT_API 76 | # endif 77 | 78 | #elif defined __clang__ 79 | # define PROMISE_QT_API __attribute__ ((visibility ("default"))) 80 | #else 81 | # error "Do not know how to export classes for this platform" 82 | #endif 83 | 84 | #else 85 | #define PROMISE_QT_API 86 | #endif 87 | 88 | namespace promise { 89 | 90 | class PromiseEventListener; 91 | class PromiseEventPrivate; 92 | class PromiseEventFilter : public QObject { 93 | private: 94 | PROMISE_QT_API PromiseEventFilter(); 95 | 96 | public: 97 | 98 | PROMISE_QT_API std::weak_ptr addEventListener(QObject *object, QEvent::Type eventType, const std::function &func); 99 | PROMISE_QT_API void removeEventListener(std::weak_ptr listener); 100 | PROMISE_QT_API static PromiseEventFilter &getSingleInstance(); 101 | 102 | protected: 103 | PROMISE_QT_API bool eventFilter(QObject *object, QEvent *event) override; 104 | std::shared_ptr private_; 105 | }; 106 | 107 | // Wait event will wait the event for only once 108 | PROMISE_QT_API Promise waitEvent(QObject *object, 109 | QEvent::Type eventType, 110 | bool callSysHandler = false); 111 | PROMISE_QT_API std::weak_ptr addEventListener(QObject *object, QEvent::Type eventType, const std::function &func); 112 | PROMISE_QT_API void removeEventListener(std::weak_ptr listener); 113 | 114 | struct QtTimerHolder: QObject { 115 | PROMISE_QT_API ~QtTimerHolder(); 116 | private: 117 | PROMISE_QT_API QtTimerHolder(); 118 | public: 119 | PROMISE_QT_API static Promise delay(int time_ms); 120 | PROMISE_QT_API static Promise yield(); 121 | PROMISE_QT_API static Promise setTimeout(const std::function &func, 122 | int time_ms); 123 | 124 | protected: 125 | PROMISE_QT_API void timerEvent(QTimerEvent *event); 126 | private: 127 | std::map defers_; 128 | 129 | PROMISE_QT_API static QtTimerHolder &getInstance(); 130 | }; 131 | 132 | 133 | PROMISE_QT_API Promise delay(int time_ms); 134 | PROMISE_QT_API Promise yield(); 135 | PROMISE_QT_API Promise setTimeout(const std::function &func, 136 | int time_ms); 137 | PROMISE_QT_API void cancelDelay(Promise promise); 138 | PROMISE_QT_API void clearTimeout(Promise promise); 139 | 140 | 141 | // Low level set timeout 142 | struct PROMISE_QT_API QtPromiseTimerHandler { 143 | void wait(); 144 | QTimer *timer_; 145 | }; 146 | PROMISE_QT_API std::shared_ptr qtPromiseSetTimeout(const std::function &cb, int ms); 147 | 148 | } 149 | 150 | 151 | #ifdef PROMISE_HEADONLY 152 | #include "promise_qt_inl.hpp" 153 | #endif 154 | 155 | #endif 156 | -------------------------------------------------------------------------------- /add_ons/qt/promise_qt_inl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INC_PROMISE_QT_CPP_ 2 | #define INC_PROMISE_QT_CPP_ 3 | 4 | /* 5 | * Copyright (c) 2021, xhawk18 6 | * at gmail.com 7 | * 8 | * The MIT License (MIT) 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in 18 | * all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | */ 28 | 29 | // 30 | // Promisified timer based on promise-cpp and QT 31 | // 32 | // Functions -- 33 | // Promise yield(QWidget *widget); 34 | // Promise delay(QWidget *widget, uint64_t time_ms); 35 | // void cancelDelay(Promise promise); 36 | // 37 | // Promise setTimeout(QWidget *widget, 38 | // const std::function &func, 39 | // uint64_t time_ms); 40 | // void clearTimeout(Promise promise); 41 | // 42 | 43 | #include "promise-cpp/promise.hpp" 44 | #include "promise_qt.hpp" 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | namespace promise { 53 | 54 | class PromiseEventListener { 55 | public: 56 | using Listeners = std::multimap, std::shared_ptr>; 57 | std::function cb_; 58 | Listeners::iterator self_; 59 | }; 60 | 61 | class PromiseEventPrivate { 62 | public: 63 | PromiseEventListener::Listeners listeners_; 64 | }; 65 | 66 | PromiseEventFilter::PromiseEventFilter() 67 | : private_(new PromiseEventPrivate) { 68 | } 69 | 70 | std::weak_ptr PromiseEventFilter::addEventListener(QObject *object, QEvent::Type eventType, const std::function &func) { 71 | std::shared_ptr listener = std::make_shared(); 72 | std::pair key = { object, eventType }; 73 | auto itr = private_->listeners_.insert({ key, listener }); 74 | listener->cb_ = func; 75 | listener->self_ = itr; 76 | return listener; 77 | } 78 | 79 | void PromiseEventFilter::removeEventListener(std::weak_ptr listener) { 80 | auto sListener = listener.lock(); 81 | if (sListener) { 82 | private_->listeners_.erase(sListener->self_); 83 | } 84 | } 85 | 86 | PromiseEventFilter &PromiseEventFilter::getSingleInstance() { 87 | static PromiseEventFilter promiseEventFilter; 88 | return promiseEventFilter; 89 | } 90 | 91 | bool PromiseEventFilter::eventFilter(QObject *object, QEvent *event) { 92 | std::pair key = { object, event->type() }; 93 | 94 | bool filtered = false; 95 | 96 | std::list> listeners; 97 | for (auto itr = private_->listeners_.lower_bound(key); 98 | itr != private_->listeners_.end() && key == itr->first; ++itr) { 99 | listeners.push_back(itr->second); 100 | } 101 | for (const auto &listener : listeners) { 102 | auto sListener = listener.lock(); 103 | if (sListener) { 104 | bool res = sListener->cb_(object, event); 105 | if (res) filtered = true; 106 | } 107 | } 108 | 109 | if (filtered) return true; 110 | else return QObject::eventFilter(object, event); 111 | } 112 | 113 | // Wait event will wait the event for only once 114 | Promise waitEvent(QObject *object, 115 | QEvent::Type eventType, 116 | bool callSysHandler) { 117 | auto listener = std::make_shared>(); 118 | Promise promise = newPromise([listener, object, eventType, callSysHandler](Defer &defer) { 119 | 120 | std::shared_ptr disableFilter = std::make_shared(false); 121 | //PTI; 122 | *listener = PromiseEventFilter::getSingleInstance().addEventListener( 123 | object, eventType, [defer, callSysHandler, disableFilter](QObject *object, QEvent *event) { 124 | (void)object; 125 | if (event->type() == QEvent::Destroy) { 126 | defer.reject(); 127 | return false; 128 | } 129 | // The next then function will be call immediately 130 | // Be care that do not use event in the next event loop 131 | else if (*disableFilter) { 132 | return false; 133 | } 134 | else if (callSysHandler) { 135 | //PTI; 136 | *disableFilter = true; 137 | QApplication::sendEvent(object, event); 138 | *disableFilter = false; 139 | defer.resolve(event); 140 | return true; 141 | } 142 | else { 143 | //PTI; 144 | defer.resolve(event); 145 | return false; 146 | } 147 | }); 148 | }).finally([listener]() { 149 | //PTI; 150 | PromiseEventFilter::getSingleInstance().removeEventListener(*listener); 151 | }); 152 | 153 | return promise; 154 | } 155 | 156 | 157 | std::weak_ptr addEventListener(QObject *object, QEvent::Type eventType, const std::function &func) { 158 | return PromiseEventFilter::getSingleInstance().addEventListener(object, eventType, func); 159 | } 160 | 161 | void removeEventListener(std::weak_ptr listener) { 162 | PromiseEventFilter::getSingleInstance().removeEventListener(listener); 163 | } 164 | 165 | 166 | QtTimerHolder::QtTimerHolder() { 167 | } 168 | 169 | QtTimerHolder::~QtTimerHolder() { 170 | } 171 | 172 | 173 | void QtPromiseTimerHandler::wait() { 174 | while (timer_ != nullptr) { 175 | QCoreApplication::processEvents(); 176 | } 177 | } 178 | 179 | 180 | std::shared_ptr qtPromiseSetTimeout(const std::function &cb, int ms) { 181 | //LOG_INFO("setTimeout({})", ms); 182 | std::shared_ptr handler(new QtPromiseTimerHandler); 183 | handler->timer_ = new QTimer(); 184 | 185 | QMetaObject::Connection conneciton = QObject::connect(QThread::currentThread(), &QThread::finished, [handler]() { 186 | QTimer *timer = handler->timer_; 187 | if (timer != nullptr) { 188 | handler->timer_ = nullptr; 189 | timer->stop(); 190 | timer->deleteLater(); 191 | } 192 | }); 193 | 194 | handler->timer_->singleShot(ms, [handler, cb, conneciton]() { 195 | cb(); 196 | QObject::disconnect(conneciton); 197 | QTimer *timer = handler->timer_; 198 | if (timer != nullptr) { 199 | handler->timer_ = nullptr; 200 | timer->deleteLater(); 201 | } 202 | }); 203 | 204 | return handler; 205 | } 206 | 207 | Promise QtTimerHolder::delay(int time_ms) { 208 | return newPromise([time_ms](Defer &defer) { 209 | qtPromiseSetTimeout([defer] { 210 | defer.resolve(); 211 | }, time_ms); 212 | }); 213 | } 214 | 215 | Promise QtTimerHolder::yield() { 216 | return delay(0); 217 | } 218 | 219 | Promise QtTimerHolder::setTimeout(const std::function &func, 220 | int time_ms) { 221 | return delay(time_ms).then([func]() { 222 | func(false); 223 | }, [func]() { 224 | func(true); 225 | }); 226 | } 227 | 228 | void QtTimerHolder::timerEvent(QTimerEvent *event) { 229 | int timerId = event->timerId(); 230 | auto found = this->defers_.find(timerId); 231 | if (found != this->defers_.end()) { 232 | Defer &defer = found->second; 233 | defer.resolve(); 234 | } 235 | QObject::timerEvent(event); 236 | } 237 | 238 | QtTimerHolder &QtTimerHolder::getInstance() { 239 | static QtTimerHolder s_qtTimerHolder_; 240 | return s_qtTimerHolder_; 241 | } 242 | 243 | Promise delay(int time_ms) { 244 | return QtTimerHolder::delay(time_ms); 245 | } 246 | 247 | Promise yield() { 248 | return QtTimerHolder::yield(); 249 | } 250 | 251 | Promise setTimeout(const std::function &func, 252 | int time_ms) { 253 | return QtTimerHolder::setTimeout(func, time_ms); 254 | } 255 | 256 | 257 | void cancelDelay(Promise promise) { 258 | promise.reject(); 259 | } 260 | 261 | void clearTimeout(Promise promise) { 262 | cancelDelay(promise); 263 | } 264 | 265 | } 266 | 267 | #endif 268 | -------------------------------------------------------------------------------- /add_ons/simple_task/simple_task.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Promise API implemented by cpp as Javascript promise style 3 | * 4 | * Copyright (c) 2016, xhawk18 5 | * at gmail.com 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | 28 | 29 | #pragma once 30 | #ifndef INC_SIMPLE_TASK_HPP_ 31 | #define INC_SIMPLE_TASK_HPP_ 32 | 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include "promise-cpp/promise.hpp" 46 | 47 | 48 | class Service { 49 | using Defer = promise::Defer; 50 | using Promise = promise::Promise; 51 | using TimePoint = std::chrono::time_point; 52 | using Timers = std::multimap; 53 | using Tasks = std::deque; 54 | #if PROMISE_MULTITHREAD 55 | using Mutex = promise::Mutex; 56 | #endif 57 | 58 | Timers timers_; 59 | Tasks tasks_; 60 | #if PROMISE_MULTITHREAD 61 | //std::recursive_mutex mutex_; 62 | std::shared_ptr mutex_; 63 | #endif 64 | std::condition_variable_any cond_; 65 | std::atomic isAutoStop_; 66 | std::atomic isStop_; 67 | //Unlock and then lock 68 | #if PROMISE_MULTITHREAD 69 | struct unlock_guard_t { 70 | inline unlock_guard_t(std::shared_ptr mutex) 71 | : mutex_(mutex) 72 | , lock_count_(mutex->lock_count()) { 73 | mutex_->unlock(lock_count_); 74 | } 75 | inline ~unlock_guard_t() { 76 | mutex_->lock(lock_count_); 77 | } 78 | std::shared_ptr mutex_; 79 | size_t lock_count_; 80 | }; 81 | #endif 82 | public: 83 | Service() 84 | : isAutoStop_(true) 85 | , isStop_(false) 86 | #if PROMISE_MULTITHREAD 87 | , mutex_(std::make_shared()) 88 | #endif 89 | { 90 | } 91 | 92 | // delay for milliseconds 93 | Promise delay(uint64_t time_ms) { 94 | return promise::newPromise([&](Defer &defer) { 95 | TimePoint now = std::chrono::steady_clock::now(); 96 | TimePoint time = now + std::chrono::milliseconds(time_ms); 97 | timers_.emplace(time, defer); 98 | }); 99 | } 100 | 101 | // yield for other tasks to run 102 | Promise yield() { 103 | return promise::newPromise([&](Defer &defer) { 104 | #if PROMISE_MULTITHREAD 105 | std::lock_guard lock(*mutex_); 106 | #endif 107 | tasks_.push_back(defer); 108 | cond_.notify_one(); 109 | }); 110 | } 111 | 112 | // Resolve the defer object in this io thread 113 | void runInIoThread(const std::function &func) { 114 | promise::newPromise([=](Defer &defer) { 115 | #if PROMISE_MULTITHREAD 116 | std::lock_guard lock(*mutex_); 117 | #endif 118 | tasks_.push_back(defer); 119 | cond_.notify_one(); 120 | }).then([func]() { 121 | func(); 122 | }); 123 | } 124 | 125 | // Set if the io thread will auto exist if no waiting tasks and timers. 126 | void setAutoStop(bool isAutoExit) { 127 | #if PROMISE_MULTITHREAD 128 | std::lock_guard lock(*mutex_); 129 | #endif 130 | isAutoStop_ = isAutoExit; 131 | cond_.notify_one(); 132 | } 133 | 134 | 135 | // run the service loop 136 | void run() { 137 | #if PROMISE_MULTITHREAD 138 | std::unique_lock lock(*mutex_); 139 | #endif 140 | 141 | while(!isStop_ && (!isAutoStop_ || tasks_.size() > 0 || timers_.size() > 0)) { 142 | 143 | if (tasks_.size() == 0 && timers_.size() == 0) { 144 | cond_.wait(lock); 145 | continue; 146 | } 147 | 148 | while (!isStop_ && timers_.size() > 0) { 149 | TimePoint now = std::chrono::steady_clock::now(); 150 | TimePoint time = timers_.begin()->first; 151 | if (time <= now) { 152 | Defer &defer = timers_.begin()->second; 153 | tasks_.push_back(defer); 154 | timers_.erase(timers_.begin()); 155 | } 156 | else if (tasks_.size() == 0) { 157 | //std::this_thread::sleep_for(time - now); 158 | cond_.wait_for(lock, time - now); 159 | } 160 | else { 161 | break; 162 | } 163 | } 164 | 165 | // Check fixed size of tasks in this loop, so that timer have a chance to run. 166 | if(!isStop_ && tasks_.size() > 0) { 167 | size_t size = tasks_.size(); 168 | for(size_t i = 0; i < size; ++i){ 169 | Defer defer = tasks_.front(); 170 | tasks_.pop_front(); 171 | 172 | #if PROMISE_MULTITHREAD 173 | unlock_guard_t unlock(mutex_); 174 | defer.resolve(); 175 | break; // for only once 176 | #else 177 | defer.resolve(); // loop with the size 178 | #endif 179 | } 180 | } 181 | } 182 | 183 | // Clear pending timers and tasks 184 | while (timers_.size() > 0 || tasks_.size()) { 185 | while (timers_.size() > 0) { 186 | Defer defer = timers_.begin()->second; 187 | timers_.erase(timers_.begin()); 188 | #if PROMISE_MULTITHREAD 189 | unlock_guard_t unlock(mutex_); 190 | #endif 191 | defer.reject(std::runtime_error("service stopped")); 192 | } 193 | while (tasks_.size() > 0) { 194 | Defer defer = tasks_.front(); 195 | tasks_.pop_front(); 196 | #if PROMISE_MULTITHREAD 197 | unlock_guard_t unlock(mutex_); 198 | #endif 199 | defer.reject(std::runtime_error("service stopped")); 200 | } 201 | } 202 | } 203 | 204 | // stop the service loop 205 | void stop() { 206 | #if PROMISE_MULTITHREAD 207 | std::lock_guard lock(*mutex_); 208 | #endif 209 | isStop_ = true; 210 | cond_.notify_one(); 211 | } 212 | }; 213 | 214 | #endif 215 | 216 | -------------------------------------------------------------------------------- /add_ons/windows/promise_windows.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, xhawk18 3 | * at gmail.com 4 | * 5 | * The MIT License (MIT) 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #pragma once 27 | #ifndef INC_PROMISE_WIN32_HPP_ 28 | #define INC_PROMISE_WIN32_HPP_ 29 | 30 | // 31 | // Promisified timer based on promise-cpp on windows platform 32 | // It can be used in any windows application other than MFC 33 | // 34 | // Functions -- 35 | // Defer yield(QWidget *widget); 36 | // Defer delay(QWidget *widget, uint64_t time_ms); 37 | // void cancelDelay(Defer d); 38 | // 39 | // Defer setTimeout(QWidget *widget, 40 | // const std::function &func, 41 | // uint64_t time_ms); 42 | // void clearTimeout(Defer d); 43 | // 44 | 45 | #include "promise-cpp/promise.hpp" 46 | #include 47 | #include 48 | #include 49 | 50 | namespace promise { 51 | 52 | inline void cancelDelay(Defer d); 53 | inline void clearTimeout(Defer d); 54 | 55 | struct WindowsTimerHolder { 56 | WindowsTimerHolder() {}; 57 | public: 58 | static Promise delay(int time_ms) { 59 | UINT_PTR timerId = ::SetTimer(NULL, 0, (UINT)time_ms, &WindowsTimerHolder::timerEvent); 60 | 61 | return newPromise([timerId](Defer &Defer) { 62 | //printf("timerId = %d\n", (int)timerId); 63 | WindowsTimerHolder::getDefers().insert({ timerId, Defer }); 64 | }).then([timerId]() { 65 | WindowsTimerHolder::getDefers().erase(timerId); 66 | return promise::resolve(); 67 | }, [timerId]() { 68 | ::KillTimer(NULL, timerId); 69 | WindowsTimerHolder::getDefers().erase(timerId); 70 | return promise::reject(); 71 | }); 72 | 73 | } 74 | 75 | static Promise yield() { 76 | return delay(0); 77 | } 78 | 79 | static Promise setTimeout(const std::function &func, 80 | int time_ms) { 81 | return delay(time_ms).then([func]() { 82 | func(false); 83 | }, [func]() { 84 | func(true); 85 | }); 86 | } 87 | 88 | protected: 89 | static void CALLBACK timerEvent(HWND unnamedParam1, 90 | UINT unnamedParam2, 91 | UINT_PTR timerId, 92 | DWORD unnamedParam4) { 93 | (void)unnamedParam1; 94 | (void)unnamedParam2; 95 | (void)unnamedParam4; 96 | //printf("%d %d %d %d\n", (int)unnamedParam1, (int)unnamedParam2, (int)unnamedParam3, (int)unnamedParam4); 97 | auto found = getDefers().find(timerId); 98 | if (found != getDefers().end()) { 99 | Defer d = found->second; 100 | d.resolve(); 101 | } 102 | } 103 | 104 | private: 105 | friend void cancelDelay(Defer d); 106 | 107 | inline static std::map &getDefers() { 108 | static std::map s_defers_; 109 | return s_defers_; 110 | } 111 | }; 112 | 113 | inline Promise delay(int time_ms) { 114 | return WindowsTimerHolder::delay(time_ms); 115 | } 116 | 117 | inline Promise yield() { 118 | return WindowsTimerHolder::yield(); 119 | } 120 | 121 | inline Promise setTimeout(const std::function &func, 122 | int time_ms) { 123 | return WindowsTimerHolder::setTimeout(func, time_ms); 124 | } 125 | 126 | 127 | inline void cancelDelay(Defer d) { 128 | d.reject(); 129 | } 130 | 131 | inline void clearTimeout(Defer d) { 132 | cancelDelay(d); 133 | } 134 | 135 | } 136 | #endif 137 | -------------------------------------------------------------------------------- /doc/lifetime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xhawk18/promise-cpp/556dce7183dc76a229e5f6f82bd5e7ef1203bbfd/doc/lifetime.png -------------------------------------------------------------------------------- /example/asio_benchmark_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Promise API implemented by cpp as Javascript promise style 3 | * 4 | * Copyright (c) 2016, xhawk18 5 | * at gmail.com 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | 28 | 29 | // It seems that disable IOCP is faster on windows 30 | #define BOOST_ASIO_DISABLE_IOCP 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "add_ons/asio/timer.hpp" 37 | 38 | using namespace promise; 39 | namespace chrono = std::chrono; 40 | namespace asio = boost::asio; 41 | using steady_clock = std::chrono::steady_clock; 42 | 43 | static const int N = 1000000; 44 | 45 | void dump(std::string name, int n, 46 | steady_clock::time_point start, 47 | steady_clock::time_point end) 48 | { 49 | auto ns = chrono::duration_cast(end - start); 50 | std::cout << name << " " << n << " " << 51 | ns.count() / n << 52 | "ns/op" << std::endl; 53 | } 54 | 55 | void task(asio::io_service &io, int task_id, int count, int *pcoro, Defer defer) { 56 | if (count == 0) { 57 | -- *pcoro; 58 | if (*pcoro == 0) { 59 | defer.resolve(); 60 | } 61 | return; 62 | } 63 | 64 | yield(io).then([=, &io]() { 65 | task(io, task_id, count - 1, pcoro, defer); 66 | }); 67 | }; 68 | 69 | 70 | Promise test_switch(asio::io_service &io, int coro) 71 | { 72 | steady_clock::time_point start = steady_clock::now(); 73 | 74 | int *pcoro = new int(coro); 75 | 76 | return newPromise([=, &io](Defer &defer){ 77 | for (int task_id = 0; task_id < coro; ++task_id) { 78 | task(io, task_id, N / coro, pcoro, defer); 79 | } 80 | }).then([=](){ 81 | delete pcoro; 82 | steady_clock::time_point end = steady_clock::now(); 83 | dump("BenchmarkSwitch_" + std::to_string(coro), N, start, end); 84 | }); 85 | } 86 | 87 | int main() { 88 | asio::io_service io; 89 | 90 | doWhile([&](DeferLoop &loop) { 91 | #ifdef PM_DEBUG 92 | printf("In while ..., alloc_size = %d\n", (int)(*dbg_alloc_size())); 93 | #else 94 | printf("In while ...\n"); 95 | #endif 96 | //Sleep(5000); 97 | test_switch(io, 1).then([&]() { 98 | return test_switch(io, 1000); 99 | }).then([&]() { 100 | return test_switch(io, 10000); 101 | }).then([&]() { 102 | return test_switch(io, 100000); 103 | }).then([&]() { 104 | return test_switch(io, 1000000); 105 | }).then(loop); 106 | }); 107 | 108 | io.run(); 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /example/asio_http_client.cpp: -------------------------------------------------------------------------------- 1 | // This code is forked from 2 | // boost_1_67_0/libs/beast/example/http/client/coro/http_client_coro.cpp 3 | // and modified by xhawk18 to use promise-cpp for better async control. 4 | // Copyright (c) 2018, xhawk18 5 | // at gmail.com 6 | 7 | // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 10 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 11 | // 12 | // Official repository: https://github.com/boostorg/beast 13 | // 14 | 15 | //------------------------------------------------------------------------------ 16 | // 17 | // Example: HTTP client, asynchronous with promise-cpp 18 | // 19 | //------------------------------------------------------------------------------ 20 | 21 | #include 22 | #if BOOST_VERSION < 106600 23 | # error "This program need boost 1.66.0 or higher!" 24 | #endif 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "add_ons/asio/io.hpp" 38 | 39 | using namespace promise; 40 | namespace asio = boost::asio; 41 | using tcp = boost::asio::ip::tcp; 42 | namespace http = boost::beast::http; 43 | namespace beast = boost::beast; 44 | 45 | //------------------------------------------------------------------------------ 46 | 47 | //From https://stackoverflow.com/a/11044337 48 | struct Uri 49 | { 50 | public: 51 | std::string QueryString, Path, Protocol, Host, Port; 52 | 53 | static Uri Parse(const std::string &uri) 54 | { 55 | Uri result; 56 | 57 | typedef std::string::const_iterator iterator_t; 58 | 59 | if (uri.length() == 0) 60 | return result; 61 | 62 | iterator_t uriEnd = uri.end(); 63 | 64 | // get query start 65 | iterator_t queryStart = std::find(uri.begin(), uriEnd, '?'); 66 | 67 | // protocol 68 | iterator_t protocolStart = uri.begin(); 69 | iterator_t protocolEnd = std::find(protocolStart, uriEnd, ':'); //"://"); 70 | 71 | if (protocolEnd != uriEnd) 72 | { 73 | std::string prot = &*(protocolEnd); 74 | if ((prot.length() > 3) && (prot.substr(0, 3) == "://")) 75 | { 76 | result.Protocol = std::string(protocolStart, protocolEnd); 77 | protocolEnd += 3; // :// 78 | } 79 | else 80 | protocolEnd = uri.begin(); // no protocol 81 | } 82 | else 83 | protocolEnd = uri.begin(); // no protocol 84 | 85 | // host 86 | iterator_t hostStart = protocolEnd; 87 | iterator_t pathStart = std::find(hostStart, uriEnd, '/'); // get pathStart 88 | 89 | iterator_t hostEnd = std::find(protocolEnd, 90 | (pathStart != uriEnd) ? pathStart : queryStart, 91 | L':'); // check for port 92 | 93 | result.Host = std::string(hostStart, hostEnd); 94 | 95 | // port 96 | if ((hostEnd != uriEnd) && ((&*(hostEnd))[0] == ':')) // we have a port 97 | { 98 | hostEnd++; 99 | iterator_t portEnd = (pathStart != uriEnd) ? pathStart : queryStart; 100 | result.Port = std::string(hostEnd, portEnd); 101 | } 102 | 103 | // path 104 | if (pathStart != uriEnd) 105 | result.Path = std::string(pathStart, queryStart); 106 | 107 | // query 108 | if (queryStart != uriEnd) 109 | result.QueryString = std::string(queryStart, uri.end()); 110 | 111 | return result; 112 | 113 | } // Parse 114 | }; // uri 115 | 116 | // Performs an HTTP GET and prints the response 117 | Promise do_session( 118 | std::string const& host, 119 | std::string const& port, 120 | std::string const& target, 121 | int version, 122 | asio::io_context& ioc) { 123 | 124 | struct Session { 125 | tcp::resolver resolver_; 126 | tcp::socket socket_; 127 | beast::flat_buffer buffer_; 128 | http::request req_; 129 | http::response res_; 130 | 131 | explicit Session(asio::io_context& ioc) 132 | : resolver_(ioc) 133 | , socket_(ioc) { 134 | } 135 | }; 136 | 137 | // (Must persist in io_context ioc) 138 | auto session = std::make_shared(ioc); 139 | 140 | // Set up an HTTP GET request message 141 | session->req_.version(version); 142 | session->req_.method(http::verb::get); 143 | session->req_.target(target); 144 | session->req_.set(http::field::host, host); 145 | session->req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); 146 | 147 | //<1> Resolve the host 148 | return async_resolve(session->resolver_, host, port) 149 | 150 | .then([=](tcp::resolver::results_type &results) { 151 | //<2> Connect to the host 152 | return async_connect(session->socket_, results); 153 | 154 | }).then([=]() { 155 | //<3> Write the request 156 | return async_write(session->socket_, session->req_); 157 | 158 | }).then([=](size_t bytes_transferred) { 159 | boost::ignore_unused(bytes_transferred); 160 | //<4> Read the response 161 | return async_read(session->socket_, session->buffer_, session->res_); 162 | 163 | }).then([=](size_t bytes_transferred) { 164 | boost::ignore_unused(bytes_transferred); 165 | //<5> Write the message to standard out 166 | std::cout << session->res_ << std::endl; 167 | 168 | }).then([]() { 169 | //<6> success, return default error_code 170 | return boost::system::error_code(); 171 | }, [](const boost::system::error_code err) { 172 | //<6> failed, return the error_code 173 | return err; 174 | 175 | }).then([=](boost::system::error_code &err) { 176 | //<7> Gracefully close the socket 177 | std::cout << "shutdown..." << std::endl; 178 | session->socket_.shutdown(tcp::socket::shutdown_both, err); 179 | }); 180 | } 181 | 182 | 183 | Promise download(asio::io_context &ioc, const std::string &url) { 184 | const Uri &uri = Uri::Parse(url); 185 | if (uri.Protocol != "http") { 186 | std::cerr << "Support http protocol only!\n"; 187 | return promise::reject(); 188 | } 189 | 190 | std::string port = (uri.Port.empty() ? "80" : uri.Port); 191 | std::string target = uri.Path + uri.QueryString; 192 | 193 | std::cout << target << "\n"; 194 | std::cout << port << "\n"; 195 | std::cout << uri.Host << "\n"; 196 | 197 | int http_version = 10; 198 | return do_session(uri.Host, port, target, http_version, ioc); 199 | 200 | } 201 | 202 | //------------------------------------------------------------------------------ 203 | 204 | int main(int argc, char** argv) { 205 | // The io_context is required for all I/O 206 | asio::io_context ioc; 207 | 208 | // Launch the asynchronous operation 209 | download(ioc, "http://www.163.com/") 210 | .then([&]() { 211 | return download(ioc, "http://baidu.com/"); 212 | }).then([&]() { 213 | return download(ioc, "http://qq.com"); 214 | }).then([&]() { 215 | return download(ioc, "http://github.com/xhawk18"); 216 | }); 217 | 218 | // Run the I/O service. The call will return when 219 | // the get operation is complete. 220 | ioc.run(); 221 | 222 | return EXIT_SUCCESS; 223 | } 224 | -------------------------------------------------------------------------------- /example/asio_http_server.cpp: -------------------------------------------------------------------------------- 1 | // This code is forked from 2 | // boost_1_67_0/libs/beast/example/http/server/coro/http_server_coro.cpp 3 | // and modified by xhawk18 to use promise-cpp for better async control. 4 | // Copyright (c) 2018, xhawk18 5 | // at gmail.com 6 | 7 | // 8 | // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) 9 | // 10 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 11 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 12 | // 13 | // Official repository: https://github.com/boostorg/beast 14 | // 15 | 16 | //------------------------------------------------------------------------------ 17 | // 18 | // Example: HTTP server, asynchronous with promise-cpp 19 | // 20 | //------------------------------------------------------------------------------ 21 | 22 | #include 23 | #if BOOST_VERSION < 106600 24 | # error "This program need boost 1.66.0 or higher!" 25 | #endif 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | 40 | #include "add_ons/asio/io.hpp" 41 | 42 | using namespace promise; 43 | 44 | namespace asio = boost::asio; 45 | namespace ip = boost::asio::ip; 46 | using socket_base = boost::asio::socket_base; 47 | using tcp = boost::asio::ip::tcp; 48 | namespace beast = boost::beast; 49 | namespace http = boost::beast::http; 50 | 51 | struct Session { 52 | bool close_; 53 | tcp::socket socket_; 54 | beast::flat_buffer buffer_; 55 | std::string doc_root_; 56 | http::request req_; 57 | 58 | explicit Session( 59 | tcp::socket &socket, 60 | std::string const& doc_root 61 | ) 62 | : close_(false) 63 | , socket_(std::move(socket)) 64 | , doc_root_(doc_root) { 65 | std::cout << "new session" << std::endl; 66 | } 67 | 68 | ~Session() { 69 | std::cout << "delete session" << std::endl; 70 | } 71 | }; 72 | 73 | // Promisified functions 74 | Promise async_accept(tcp::acceptor &acceptor) { 75 | return newPromise([&](Defer &defer) { 76 | // Look up the domain name 77 | auto socket = std::make_shared(static_cast(acceptor.get_executor().context())); 78 | acceptor.async_accept(*socket, 79 | [=](boost::system::error_code err) { 80 | setPromise(defer, err, "resolve", socket); 81 | }); 82 | }); 83 | } 84 | 85 | 86 | // Return a reasonable mime type based on the extension of a file. 87 | beast::string_view 88 | mime_type(beast::string_view path) 89 | { 90 | using beast::iequals; 91 | auto const ext = [&path] 92 | { 93 | auto const pos = path.rfind("."); 94 | if(pos == beast::string_view::npos) 95 | return beast::string_view{}; 96 | return path.substr(pos); 97 | }(); 98 | if(iequals(ext, ".htm")) return "text/html"; 99 | if(iequals(ext, ".html")) return "text/html"; 100 | if(iequals(ext, ".php")) return "text/html"; 101 | if(iequals(ext, ".css")) return "text/css"; 102 | if(iequals(ext, ".txt")) return "text/plain"; 103 | if(iequals(ext, ".js")) return "application/javascript"; 104 | if(iequals(ext, ".json")) return "application/json"; 105 | if(iequals(ext, ".xml")) return "application/xml"; 106 | if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; 107 | if(iequals(ext, ".flv")) return "video/x-flv"; 108 | if(iequals(ext, ".png")) return "image/png"; 109 | if(iequals(ext, ".jpe")) return "image/jpeg"; 110 | if(iequals(ext, ".jpeg")) return "image/jpeg"; 111 | if(iequals(ext, ".jpg")) return "image/jpeg"; 112 | if(iequals(ext, ".gif")) return "image/gif"; 113 | if(iequals(ext, ".bmp")) return "image/bmp"; 114 | if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; 115 | if(iequals(ext, ".tiff")) return "image/tiff"; 116 | if(iequals(ext, ".tif")) return "image/tiff"; 117 | if(iequals(ext, ".svg")) return "image/svg+xml"; 118 | if(iequals(ext, ".svgz")) return "image/svg+xml"; 119 | return "application/text"; 120 | } 121 | 122 | // Append an HTTP rel-path to a local filesystem path. 123 | // The returned path is normalized for the platform. 124 | std::string 125 | path_cat( 126 | beast::string_view base, 127 | beast::string_view path) 128 | { 129 | if(base.empty()) 130 | return path.to_string(); 131 | std::string result = base.to_string(); 132 | #if BOOST_MSVC 133 | char constexpr path_separator = '\\'; 134 | if(result.back() == path_separator) 135 | result.resize(result.size() - 1); 136 | result.append(path.data(), path.size()); 137 | for(auto& c : result) 138 | if(c == '/') 139 | c = path_separator; 140 | #else 141 | char constexpr path_separator = '/'; 142 | if(result.back() == path_separator) 143 | result.resize(result.size() - 1); 144 | result.append(path.data(), path.size()); 145 | #endif 146 | return result; 147 | } 148 | 149 | // This function produces an HTTP response for the given 150 | // request. The type of the response object depends on the 151 | // contents of the request, so the interface requires the 152 | // caller to pass a generic lambda for receiving the response. 153 | template 154 | Promise 155 | handle_request( 156 | std::shared_ptr session, 157 | Send&& send) 158 | { 159 | // Returns a bad request response 160 | auto const bad_request = 161 | [session](beast::string_view why) 162 | { 163 | http::response res{http::status::bad_request, session->req_.version()}; 164 | res.set(http::field::server, BOOST_BEAST_VERSION_STRING); 165 | res.set(http::field::content_type, "text/html"); 166 | res.keep_alive(session->req_.keep_alive()); 167 | res.body() = why.to_string(); 168 | res.prepare_payload(); 169 | return res; 170 | }; 171 | 172 | // Returns a not found response 173 | auto const not_found = 174 | [session](beast::string_view target) 175 | { 176 | http::response res{http::status::not_found, session->req_.version()}; 177 | res.set(http::field::server, BOOST_BEAST_VERSION_STRING); 178 | res.set(http::field::content_type, "text/html"); 179 | res.keep_alive(session->req_.keep_alive()); 180 | res.body() = "The resource '" + target.to_string() + "' was not found."; 181 | res.prepare_payload(); 182 | return res; 183 | }; 184 | 185 | // Returns a server error response 186 | auto const server_error = 187 | [session](beast::string_view what) 188 | { 189 | http::response res{http::status::internal_server_error, session->req_.version()}; 190 | res.set(http::field::server, BOOST_BEAST_VERSION_STRING); 191 | res.set(http::field::content_type, "text/html"); 192 | res.keep_alive(session->req_.keep_alive()); 193 | res.body() = "An error occurred: '" + what.to_string() + "'"; 194 | res.prepare_payload(); 195 | return res; 196 | }; 197 | 198 | // Make sure we can handle the method 199 | if(session->req_.method() != http::verb::get && 200 | session->req_.method() != http::verb::head) 201 | return send(bad_request("Unknown HTTP-method")); 202 | 203 | // Request path must be absolute and not contain "..". 204 | if(session->req_.target().empty() || 205 | session->req_.target()[0] != '/' || 206 | session->req_.target().find("..") != beast::string_view::npos) 207 | return send(bad_request("Illegal request-target")); 208 | 209 | // Build the path to the requested file 210 | std::string path = path_cat(session->doc_root_, session->req_.target()); 211 | if(session->req_.target().back() == '/') 212 | path.append("index.html"); 213 | 214 | // Attempt to open the file 215 | beast::error_code ec; 216 | http::file_body::value_type body; 217 | body.open(path.c_str(), beast::file_mode::scan, ec); 218 | 219 | // Handle the case where the file doesn't exist 220 | if(ec == boost::system::errc::no_such_file_or_directory) 221 | return send(not_found(session->req_.target())); 222 | 223 | // Handle an unknown error 224 | if(ec) 225 | return send(server_error(ec.message())); 226 | 227 | // Cache the size since we need it after the move 228 | auto const size = body.size(); 229 | 230 | // Respond to HEAD request 231 | if(session->req_.method() == http::verb::head) 232 | { 233 | http::response res{http::status::ok, session->req_.version()}; 234 | res.set(http::field::server, BOOST_BEAST_VERSION_STRING); 235 | res.set(http::field::content_type, mime_type(path)); 236 | res.content_length(size); 237 | res.keep_alive(session->req_.keep_alive()); 238 | return send(std::move(res)); 239 | } 240 | 241 | // Respond to GET request 242 | http::response res{ 243 | std::piecewise_construct, 244 | std::make_tuple(std::move(body)), 245 | std::make_tuple(http::status::ok, session->req_.version())}; 246 | res.set(http::field::server, BOOST_BEAST_VERSION_STRING); 247 | res.set(http::field::content_type, mime_type(path)); 248 | res.content_length(size); 249 | res.keep_alive(session->req_.keep_alive()); 250 | return send(std::move(res)); 251 | } 252 | 253 | //------------------------------------------------------------------------------ 254 | 255 | // Report a failure 256 | void 257 | fail(boost::system::error_code ec, char const* what) 258 | { 259 | std::cerr << what << ": " << ec.message() << "\n"; 260 | } 261 | 262 | // This is the C++11 equivalent of a generic lambda. 263 | // The function object is used to send an HTTP message. 264 | template 265 | struct send_lambda 266 | { 267 | Stream& stream_; 268 | bool& close_; 269 | 270 | explicit 271 | send_lambda( 272 | Stream& stream, 273 | bool& close) 274 | : stream_(stream) 275 | , close_(close) 276 | { 277 | } 278 | 279 | template 280 | Promise 281 | operator()(http::message&& msg) const 282 | { 283 | // Determine if we should close the connection after 284 | close_ = msg.need_eof(); 285 | // The lifetime of the message has to extend 286 | // for the duration of the async operation so 287 | // we use a shared_ptr to manage it. 288 | auto sp = std::make_shared>(std::move(msg)); 289 | return async_write(stream_, *sp).finally([sp]() { 290 | //sp will be deleted util finally() called 291 | }); 292 | } 293 | }; 294 | 295 | // Handles an HTTP server connection 296 | void 297 | do_session( 298 | std::shared_ptr session) 299 | { 300 | doWhile([=](DeferLoop &loop){ 301 | std::cout << "read new http request ... " << std::endl; 302 | //<1> Read a request 303 | session->req_ = {}; 304 | async_read(session->socket_, session->buffer_, session->req_) 305 | 306 | .then([=]() { 307 | //<2> Send the response 308 | // This lambda is used to send messages 309 | send_lambda lambda{ session->socket_, session->close_ }; 310 | return handle_request(session, lambda); 311 | 312 | }).then([]() { 313 | //<3> success, return default error_code 314 | return boost::system::error_code(); 315 | }, [](const boost::system::error_code err) { 316 | //<3> failed, return the error_code 317 | return err; 318 | 319 | }).then([=](boost::system::error_code &err) { 320 | //<4> Keep-alive or close the connection. 321 | if (!err && !session->close_) { 322 | loop.doContinue();//continue doWhile ... 323 | } 324 | else { 325 | std::cout << "shutdown..." << std::endl; 326 | session->socket_.shutdown(tcp::socket::shutdown_send, err); 327 | loop.doBreak(); //break from doWhile 328 | } 329 | }); 330 | }); 331 | } 332 | 333 | //------------------------------------------------------------------------------ 334 | 335 | // Accepts incoming connections and launches the sessions 336 | void 337 | do_listen( 338 | asio::io_context& ioc, 339 | tcp::endpoint endpoint, 340 | std::string const& doc_root) 341 | { 342 | boost::system::error_code ec; 343 | 344 | // Open the acceptor 345 | auto acceptor = std::make_shared(ioc); 346 | acceptor->open(endpoint.protocol(), ec); 347 | if(ec) 348 | return fail(ec, "open"); 349 | 350 | // Allow address reuse 351 | acceptor->set_option(socket_base::reuse_address(true)); 352 | if(ec) 353 | return fail(ec, "set_option"); 354 | 355 | // Bind to the server address 356 | acceptor->bind(endpoint, ec); 357 | if(ec) 358 | return fail(ec, "bind"); 359 | 360 | // Start listening for connections 361 | acceptor->listen(socket_base::max_listen_connections, ec); 362 | if(ec) 363 | return fail(ec, "listen"); 364 | 365 | auto doc_root_ = std::make_shared(doc_root); 366 | doWhile([acceptor, doc_root_](DeferLoop &loop){ 367 | async_accept(*acceptor).then([=](std::shared_ptr socket) { 368 | std::cout << "accepted" << std::endl; 369 | auto session = std::make_shared(*socket, *doc_root_); 370 | do_session(session); 371 | }).fail([](const boost::system::error_code err) { 372 | }).then(loop); 373 | }); 374 | } 375 | 376 | int main(int argc, char* argv[]) 377 | { 378 | // Check command line arguments. 379 | if (argc != 5) 380 | { 381 | std::cerr << 382 | "Usage: http-server-coro
\n" << 383 | "Example:\n" << 384 | " http-server-coro 0.0.0.0 8080 . 1\n"; 385 | return EXIT_FAILURE; 386 | } 387 | auto const address = ip::make_address(argv[1]); 388 | auto const port = static_cast(std::atoi(argv[2])); 389 | std::string const doc_root = argv[3]; 390 | auto const threads = std::max(1, std::atoi(argv[4])); 391 | 392 | // The io_context is required for all I/O 393 | asio::io_context ioc{threads}; 394 | 395 | // Spawn a listening port 396 | do_listen(ioc, 397 | tcp::endpoint{ address, port }, 398 | doc_root); 399 | 400 | // Run the I/O service on the requested number of threads 401 | std::vector v; 402 | v.reserve(threads - 1); 403 | for(auto i = threads - 1; i > 0; --i) 404 | v.emplace_back( 405 | [&ioc] 406 | { 407 | ioc.run(); 408 | }); 409 | ioc.run(); 410 | 411 | return EXIT_SUCCESS; 412 | } 413 | -------------------------------------------------------------------------------- /example/asio_timer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Promise API implemented by cpp as Javascript promise style 3 | * 4 | * Copyright (c) 2016, xhawk18 5 | * at gmail.com 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | 28 | #include "add_ons/asio/timer.hpp" 29 | #include 30 | #include 31 | 32 | using namespace promise; 33 | namespace asio = boost::asio; 34 | 35 | Promise testTimer(asio::io_service &io) { 36 | 37 | return delay(io, 3000).then([&] { 38 | printf("timer after 3000 ms!\n"); 39 | return delay(io, 1000); 40 | }).then([&] { 41 | printf("timer after 1000 ms!\n"); 42 | return delay(io, 12000); 43 | }).then([] { 44 | printf("timer after 12000 ms!\n"); 45 | }).fail([] { 46 | printf("timer cancelled!\n"); 47 | }); 48 | } 49 | 50 | void testPromiseRace(asio::io_service &io) { 51 | auto promise0 = delay(io, 10000).then([] { 52 | printf("race: one resolved\n"); 53 | return "one"; 54 | }); 55 | auto promise1 = delay(io, 5000).then([] { 56 | printf("race: two resolved\n"); 57 | return "two"; 58 | }); 59 | 60 | race(promise0, promise1).then([](const char *str) { 61 | printf("race result = %s\n", str); 62 | // Both resolve, but promise1 is faster 63 | }); 64 | } 65 | 66 | void testPromiseAll(asio::io_service &io) { 67 | auto promise0 = delay(io, 10000).then([] { 68 | printf("all: one resolved\n"); 69 | return std::string("one"); 70 | }); 71 | auto promise1 = delay(io, 5000).then([] { 72 | printf("all: two resolved\n"); 73 | return std::string("two"); 74 | }); 75 | 76 | all(promise0, promise1).then([](const std::vector &results) { 77 | printf("all size = %d\n", (int)results.size()); 78 | for (size_t i = 0; i < results.size(); ++i) { 79 | printf("all result = %s\n", 80 | results[i].cast().c_str()); 81 | } 82 | // Both resolve, but promise1 is faster 83 | }); 84 | } 85 | 86 | int main() { 87 | asio::io_service io; 88 | 89 | testPromiseRace(io); 90 | testPromiseAll(io); 91 | 92 | Promise timer = testTimer(io); 93 | 94 | delay(io, 4500).then([=] { 95 | printf("clearTimeout\n"); 96 | clearTimeout(timer); 97 | }); 98 | 99 | io.run(); 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /example/chain_defer_test.cpp: -------------------------------------------------------------------------------- 1 | #include "promise-cpp/promise.hpp" 2 | #include 3 | #include 4 | 5 | promise::Promise writeTo(std::ostream& out) { 6 | return promise::newPromise().then([&out](int value) { 7 | out << value; 8 | return promise::reject(std::string(" testErrorReason ")); 9 | }, [&out](const std::string& reason) { 10 | out << reason; 11 | return 456; 12 | }); 13 | } 14 | 15 | int main() { 16 | promise::Promise d = promise::newPromise(); 17 | std::ostringstream out; 18 | promise::Promise writeToOut = writeTo(out); 19 | d.then(writeToOut).then(writeTo(out)).then(writeTo(out)); 20 | d.resolve(123); 21 | 22 | std::string expected = "123 testErrorReason 456"; 23 | if (out.str() != expected) { 24 | std::cout << "FAIL chain_defer_test got \"" << out.str() << "\", " 25 | << "expected \"" << expected << "\"\n"; 26 | return 1; 27 | } 28 | 29 | std::cout << "PASS\n"; 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /example/mfc_timer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(mfc_timer LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | set(CMAKE_CXX_STANDARD 11) 8 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 9 | set(CMAKE_MFC_FLAG 1) 10 | 11 | set(PROJECT_SOURCES 12 | Resource.h 13 | framework.h 14 | mfc_timer.cpp 15 | mfc_timer.h 16 | mfc_timerDlg.cpp 17 | mfc_timerDlg.h 18 | mfctimer.rc 19 | pch.cpp 20 | pch.h 21 | targetver.h 22 | res/mfc_timer.ico 23 | res/mfctimer.rc2 24 | 25 | ../../include/promise-cpp/promise.hpp 26 | ../../include/promise-cpp/promise_inl.hpp 27 | ../../include/promise-cpp/any.hpp 28 | ../../include/promise-cpp/add_ons.hpp 29 | ../../include/promise-cpp/call_traits.hpp 30 | ../../add_ons/windows/promise_windows.hpp 31 | ) 32 | 33 | add_executable(mfc_timer WIN32 ${PROJECT_SOURCES}) 34 | target_compile_definitions(mfc_timer PRIVATE _AFXDLL) 35 | target_link_libraries(mfc_timer PRIVATE promise) 36 | 37 | -------------------------------------------------------------------------------- /example/mfc_timer/Resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ 生成的包含文件。 3 | // 由 mfctimer.rc 使用 4 | // 5 | #define IDR_MAINFRAME 128 6 | #define IDM_ABOUTBOX 0x0010 7 | #define IDD_ABOUTBOX 100 8 | #define IDS_ABOUTBOX 101 9 | #define IDD_MFC_TIMER_DIALOG 102 10 | 11 | // 新对象的下一组默认值 12 | // 13 | #ifdef APSTUDIO_INVOKED 14 | #ifndef APSTUDIO_READONLY_SYMBOLS 15 | 16 | #define _APS_NEXT_RESOURCE_VALUE 129 17 | #define _APS_NEXT_CONTROL_VALUE 1000 18 | #define _APS_NEXT_SYMED_VALUE 101 19 | #define _APS_NEXT_COMMAND_VALUE 32771 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /example/mfc_timer/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef VC_EXTRALEAN 4 | #define VC_EXTRALEAN // 从 Windows 头中排除极少使用的资料 5 | #endif 6 | 7 | #include "targetver.h" 8 | 9 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 某些 CString 构造函数将是显式的 10 | 11 | // 关闭 MFC 的一些常见且经常可放心忽略的隐藏警告消息 12 | #define _AFX_ALL_WARNINGS 13 | 14 | #include // MFC 核心组件和标准组件 15 | #include // MFC 扩展 16 | 17 | 18 | #include // MFC 自动化类 19 | 20 | 21 | 22 | #ifndef _AFX_NO_OLE_SUPPORT 23 | #include // MFC 对 Internet Explorer 4 公共控件的支持 24 | #endif 25 | #ifndef _AFX_NO_AFXCMN_SUPPORT 26 | #include // MFC 对 Windows 公共控件的支持 27 | #endif // _AFX_NO_AFXCMN_SUPPORT 28 | 29 | #include // MFC 支持功能区和控制条 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | #ifdef _UNICODE 40 | #if defined _M_IX86 41 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 42 | #elif defined _M_X64 43 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 44 | #else 45 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 46 | #endif 47 | #endif 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/mfc_timer/mfc_timer.cpp: -------------------------------------------------------------------------------- 1 |  2 | // mfc_timer.cpp: 定义应用程序的类行为。 3 | // 4 | 5 | #include "pch.h" 6 | #include "framework.h" 7 | #include "mfc_timer.h" 8 | #include "mfc_timerDlg.h" 9 | 10 | #ifdef _DEBUG 11 | #define new DEBUG_NEW 12 | #endif 13 | 14 | 15 | // CMfcTimerApp 16 | 17 | BEGIN_MESSAGE_MAP(CMfcTimerApp, CWinApp) 18 | ON_COMMAND(ID_HELP, &CWinApp::OnHelp) 19 | END_MESSAGE_MAP() 20 | 21 | 22 | // CMfcTimerApp 构造 23 | 24 | CMfcTimerApp::CMfcTimerApp() 25 | { 26 | // 支持重新启动管理器 27 | m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART; 28 | 29 | // TODO: 在此处添加构造代码, 30 | // 将所有重要的初始化放置在 InitInstance 中 31 | } 32 | 33 | 34 | // 唯一的 CMfcTimerApp 对象 35 | 36 | CMfcTimerApp theApp; 37 | 38 | 39 | // CMfcTimerApp 初始化 40 | 41 | BOOL CMfcTimerApp::InitInstance() 42 | { 43 | // 如果一个运行在 Windows XP 上的应用程序清单指定要 44 | // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式, 45 | //则需要 InitCommonControlsEx()。 否则,将无法创建窗口。 46 | INITCOMMONCONTROLSEX InitCtrls; 47 | InitCtrls.dwSize = sizeof(InitCtrls); 48 | // 将它设置为包括所有要在应用程序中使用的 49 | // 公共控件类。 50 | InitCtrls.dwICC = ICC_WIN95_CLASSES; 51 | InitCommonControlsEx(&InitCtrls); 52 | 53 | CWinApp::InitInstance(); 54 | 55 | 56 | AfxEnableControlContainer(); 57 | 58 | // 创建 shell 管理器,以防对话框包含 59 | // 任何 shell 树视图控件或 shell 列表视图控件。 60 | CShellManager *pShellManager = new CShellManager; 61 | 62 | // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题 63 | CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); 64 | 65 | // 标准初始化 66 | // 如果未使用这些功能并希望减小 67 | // 最终可执行文件的大小,则应移除下列 68 | // 不需要的特定初始化例程 69 | // 更改用于存储设置的注册表项 70 | // TODO: 应适当修改该字符串, 71 | // 例如修改为公司或组织名 72 | SetRegistryKey(_T("应用程序向导生成的本地应用程序")); 73 | 74 | CmfctimerDlg dlg; 75 | m_pMainWnd = &dlg; 76 | INT_PTR nResponse = dlg.DoModal(); 77 | if (nResponse == IDOK) 78 | { 79 | // TODO: 在此放置处理何时用 80 | // “确定”来关闭对话框的代码 81 | } 82 | else if (nResponse == IDCANCEL) 83 | { 84 | // TODO: 在此放置处理何时用 85 | // “取消”来关闭对话框的代码 86 | } 87 | else if (nResponse == -1) 88 | { 89 | TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n"); 90 | TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n"); 91 | } 92 | 93 | // 删除上面创建的 shell 管理器。 94 | if (pShellManager != nullptr) 95 | { 96 | delete pShellManager; 97 | } 98 | 99 | #if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS) 100 | ControlBarCleanUp(); 101 | #endif 102 | 103 | // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序, 104 | // 而不是启动应用程序的消息泵。 105 | return FALSE; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /example/mfc_timer/mfc_timer.h: -------------------------------------------------------------------------------- 1 |  2 | // mfc_timer.h: PROJECT_NAME 应用程序的主头文件 3 | // 4 | 5 | #pragma once 6 | 7 | #ifndef __AFXWIN_H__ 8 | #error "在包含此文件之前包含 'pch.h' 以生成 PCH" 9 | #endif 10 | 11 | #include "resource.h" // 主符号 12 | 13 | 14 | // CMfcTimerApp: 15 | // 有关此类的实现,请参阅 mfc_timer.cpp 16 | // 17 | 18 | class CMfcTimerApp : public CWinApp 19 | { 20 | public: 21 | CMfcTimerApp(); 22 | 23 | // 重写 24 | public: 25 | virtual BOOL InitInstance(); 26 | 27 | // 实现 28 | 29 | DECLARE_MESSAGE_MAP() 30 | }; 31 | 32 | extern CMfcTimerApp theApp; 33 | -------------------------------------------------------------------------------- /example/mfc_timer/mfc_timer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31313.79 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mfc_timer", "mfc_timer.vcxproj", "{1D471B31-E4B3-4028-8DB1-96A746486FD6}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {1D471B31-E4B3-4028-8DB1-96A746486FD6}.Debug|x64.ActiveCfg = Debug|x64 17 | {1D471B31-E4B3-4028-8DB1-96A746486FD6}.Debug|x64.Build.0 = Debug|x64 18 | {1D471B31-E4B3-4028-8DB1-96A746486FD6}.Debug|x86.ActiveCfg = Debug|Win32 19 | {1D471B31-E4B3-4028-8DB1-96A746486FD6}.Debug|x86.Build.0 = Debug|Win32 20 | {1D471B31-E4B3-4028-8DB1-96A746486FD6}.Release|x64.ActiveCfg = Release|x64 21 | {1D471B31-E4B3-4028-8DB1-96A746486FD6}.Release|x64.Build.0 = Release|x64 22 | {1D471B31-E4B3-4028-8DB1-96A746486FD6}.Release|x86.ActiveCfg = Release|Win32 23 | {1D471B31-E4B3-4028-8DB1-96A746486FD6}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {FC32D2BE-8864-474C-BC28-8A5B31045133} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /example/mfc_timer/mfc_timer.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {1D471B31-E4B3-4028-8DB1-96A746486FD6} 24 | MFCProj 25 | mfctimer 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | Dynamic 35 | false 36 | 37 | 38 | Application 39 | false 40 | v142 41 | true 42 | Unicode 43 | Dynamic 44 | false 45 | 46 | 47 | Application 48 | true 49 | v142 50 | Unicode 51 | Dynamic 52 | false 53 | 54 | 55 | Application 56 | false 57 | v142 58 | true 59 | Unicode 60 | Dynamic 61 | false 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | true 83 | 84 | 85 | true 86 | 87 | 88 | false 89 | 90 | 91 | false 92 | 93 | 94 | 95 | Use 96 | Level3 97 | true 98 | WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) 99 | pch.h 100 | ../../include;../.. 101 | 102 | 103 | Windows 104 | 105 | 106 | false 107 | true 108 | _DEBUG;%(PreprocessorDefinitions) 109 | 110 | 111 | 0x0804 112 | _DEBUG;%(PreprocessorDefinitions) 113 | $(IntDir);%(AdditionalIncludeDirectories) 114 | 115 | 116 | 117 | 118 | Use 119 | Level3 120 | true 121 | _WINDOWS;_DEBUG;%(PreprocessorDefinitions) 122 | pch.h 123 | ../../include;../.. 124 | 125 | 126 | Windows 127 | 128 | 129 | false 130 | true 131 | _DEBUG;%(PreprocessorDefinitions) 132 | 133 | 134 | 0x0804 135 | _DEBUG;%(PreprocessorDefinitions) 136 | $(IntDir);%(AdditionalIncludeDirectories) 137 | 138 | 139 | 140 | 141 | Use 142 | Level3 143 | true 144 | true 145 | true 146 | WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) 147 | pch.h 148 | ../../include;../.. 149 | 150 | 151 | Windows 152 | true 153 | true 154 | 155 | 156 | false 157 | true 158 | NDEBUG;%(PreprocessorDefinitions) 159 | 160 | 161 | 0x0804 162 | NDEBUG;%(PreprocessorDefinitions) 163 | $(IntDir);%(AdditionalIncludeDirectories) 164 | 165 | 166 | 167 | 168 | Use 169 | Level3 170 | true 171 | true 172 | true 173 | _WINDOWS;NDEBUG;%(PreprocessorDefinitions) 174 | pch.h 175 | ../../include;../.. 176 | 177 | 178 | Windows 179 | true 180 | true 181 | 182 | 183 | false 184 | true 185 | NDEBUG;%(PreprocessorDefinitions) 186 | 187 | 188 | 0x0804 189 | NDEBUG;%(PreprocessorDefinitions) 190 | $(IntDir);%(AdditionalIncludeDirectories) 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | NotUsing 205 | NotUsing 206 | NotUsing 207 | NotUsing 208 | 209 | 210 | 211 | 212 | Create 213 | Create 214 | Create 215 | Create 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /example/mfc_timer/mfc_timer.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 头文件 32 | 33 | 34 | 头文件 35 | 36 | 37 | 头文件 38 | 39 | 40 | 41 | 42 | 源文件 43 | 44 | 45 | 源文件 46 | 47 | 48 | 源文件 49 | 50 | 51 | 源文件 52 | 53 | 54 | 55 | 56 | 资源文件 57 | 58 | 59 | 60 | 61 | 资源文件 62 | 63 | 64 | 65 | 66 | 资源文件 67 | 68 | 69 | -------------------------------------------------------------------------------- /example/mfc_timer/mfc_timerDlg.cpp: -------------------------------------------------------------------------------- 1 | 2 | // mfc_timerDlg.cpp: 实现文件 3 | // 4 | 5 | #include "pch.h" 6 | #include "framework.h" 7 | #include "mfc_timer.h" 8 | #include "mfc_timerDlg.h" 9 | #include "afxdialogex.h" 10 | #include 11 | #include 12 | #include "add_ons/windows/promise_windows.hpp" 13 | 14 | #ifdef _DEBUG 15 | #define new DEBUG_NEW 16 | #endif 17 | 18 | 19 | // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 20 | 21 | class CAboutDlg : public CDialogEx 22 | { 23 | public: 24 | CAboutDlg(); 25 | 26 | // 对话框数据 27 | #ifdef AFX_DESIGN_TIME 28 | enum { IDD = IDD_ABOUTBOX }; 29 | #endif 30 | 31 | protected: 32 | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 33 | 34 | // 实现 35 | protected: 36 | DECLARE_MESSAGE_MAP() 37 | }; 38 | 39 | CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) 40 | { 41 | } 42 | 43 | void CAboutDlg::DoDataExchange(CDataExchange* pDX) 44 | { 45 | CDialogEx::DoDataExchange(pDX); 46 | } 47 | 48 | BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) 49 | END_MESSAGE_MAP() 50 | 51 | 52 | // CmfctimerDlg 对话框 53 | 54 | 55 | 56 | CmfctimerDlg::CmfctimerDlg(CWnd* pParent /*=nullptr*/) 57 | : CDialogEx(IDD_MFC_TIMER_DIALOG, pParent) 58 | { 59 | m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 60 | } 61 | 62 | void CmfctimerDlg::DoDataExchange(CDataExchange* pDX) 63 | { 64 | CDialogEx::DoDataExchange(pDX); 65 | } 66 | 67 | BEGIN_MESSAGE_MAP(CmfctimerDlg, CDialogEx) 68 | ON_WM_SYSCOMMAND() 69 | ON_WM_PAINT() 70 | ON_WM_QUERYDRAGICON() 71 | END_MESSAGE_MAP() 72 | 73 | 74 | // CmfctimerDlg 消息处理程序 75 | 76 | BOOL CmfctimerDlg::OnInitDialog() 77 | { 78 | CDialogEx::OnInitDialog(); 79 | 80 | // 将“关于...”菜单项添加到系统菜单中。 81 | 82 | // IDM_ABOUTBOX 必须在系统命令范围内。 83 | ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); 84 | ASSERT(IDM_ABOUTBOX < 0xF000); 85 | 86 | CMenu* pSysMenu = GetSystemMenu(FALSE); 87 | if (pSysMenu != nullptr) 88 | { 89 | BOOL bNameValid; 90 | CString strAboutMenu; 91 | bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); 92 | ASSERT(bNameValid); 93 | if (!strAboutMenu.IsEmpty()) 94 | { 95 | pSysMenu->AppendMenu(MF_SEPARATOR); 96 | pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); 97 | } 98 | } 99 | 100 | // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 101 | // 执行此操作 102 | SetIcon(m_hIcon, TRUE); // 设置大图标 103 | SetIcon(m_hIcon, FALSE); // 设置小图标 104 | 105 | // TODO: 在此添加额外的初始化代码 106 | 107 | // Update text on the dialog 108 | auto updateText = [=](int sourceLine) { 109 | #ifdef UNICODE 110 | std::wstringstream os; 111 | #else 112 | std::stringstream os; 113 | #endif 114 | os << "thread id = " << std::this_thread::get_id() << "\n" 115 | << "source line = " << sourceLine; 116 | CWnd *label = GetDlgItem(IDC_STATIC); 117 | label->SetWindowText(os.str().c_str()); 118 | //os.str(); 119 | }; 120 | updateText(__LINE__); 121 | 122 | // delay tasks 123 | using namespace promise; 124 | doWhile([=](DeferLoop &loop) { 125 | promise::delay(1000).then([=]() { 126 | updateText(__LINE__); 127 | return promise::delay(1000); 128 | }).then([=]() { 129 | updateText(__LINE__); 130 | return promise::delay(1000); 131 | }).then([=]() { 132 | updateText(__LINE__); 133 | return promise::yield(); 134 | }).then([=]() { 135 | updateText(__LINE__); 136 | return promise::delay(1000); 137 | }).then([=]() { 138 | updateText(__LINE__); 139 | return promise::delay(1000); 140 | }).then([=]() { 141 | updateText(__LINE__); 142 | return promise::delay(1000); 143 | }).then([=]() { 144 | updateText(__LINE__); 145 | return promise::delay(1000); 146 | }).then(loop); //call doWhile 147 | }); 148 | 149 | return TRUE; // 除非将焦点设置到控件,否则返回 TRUE 150 | } 151 | 152 | void CmfctimerDlg::OnSysCommand(UINT nID, LPARAM lParam) 153 | { 154 | if ((nID & 0xFFF0) == IDM_ABOUTBOX) 155 | { 156 | CAboutDlg dlgAbout; 157 | dlgAbout.DoModal(); 158 | } 159 | else 160 | { 161 | CDialogEx::OnSysCommand(nID, lParam); 162 | } 163 | } 164 | 165 | // 如果向对话框添加最小化按钮,则需要下面的代码 166 | // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, 167 | // 这将由框架自动完成。 168 | 169 | void CmfctimerDlg::OnPaint() 170 | { 171 | if (IsIconic()) 172 | { 173 | CPaintDC dc(this); // 用于绘制的设备上下文 174 | 175 | SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); 176 | 177 | // 使图标在工作区矩形中居中 178 | int cxIcon = GetSystemMetrics(SM_CXICON); 179 | int cyIcon = GetSystemMetrics(SM_CYICON); 180 | CRect rect; 181 | GetClientRect(&rect); 182 | int x = (rect.Width() - cxIcon + 1) / 2; 183 | int y = (rect.Height() - cyIcon + 1) / 2; 184 | 185 | // 绘制图标 186 | dc.DrawIcon(x, y, m_hIcon); 187 | } 188 | else 189 | { 190 | CDialogEx::OnPaint(); 191 | } 192 | } 193 | 194 | //当用户拖动最小化窗口时系统调用此函数取得光标 195 | //显示。 196 | HCURSOR CmfctimerDlg::OnQueryDragIcon() 197 | { 198 | return static_cast(m_hIcon); 199 | } 200 | 201 | -------------------------------------------------------------------------------- /example/mfc_timer/mfc_timerDlg.h: -------------------------------------------------------------------------------- 1 | 2 | // mfc_timerDlg.h: 头文件 3 | // 4 | 5 | #pragma once 6 | 7 | // CmfctimerDlg 对话框 8 | class CmfctimerDlg : public CDialogEx 9 | { 10 | // 构造 11 | public: 12 | CmfctimerDlg(CWnd* pParent = nullptr); // 标准构造函数 13 | 14 | // 对话框数据 15 | #ifdef AFX_DESIGN_TIME 16 | enum { IDD = IDD_MFC_TIMER_DIALOG }; 17 | #endif 18 | 19 | protected: 20 | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 21 | 22 | 23 | // 实现 24 | protected: 25 | HICON m_hIcon; 26 | 27 | // 生成的消息映射函数 28 | virtual BOOL OnInitDialog(); 29 | afx_msg void OnSysCommand(UINT nID, LPARAM lParam); 30 | afx_msg void OnPaint(); 31 | afx_msg HCURSOR OnQueryDragIcon(); 32 | DECLARE_MESSAGE_MAP() 33 | }; 34 | -------------------------------------------------------------------------------- /example/mfc_timer/mfctimer.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xhawk18/promise-cpp/556dce7183dc76a229e5f6f82bd5e7ef1203bbfd/example/mfc_timer/mfctimer.rc -------------------------------------------------------------------------------- /example/mfc_timer/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: 与预编译标头对应的源文件 2 | 3 | #include "pch.h" 4 | 5 | // 当使用预编译的头时,需要使用此源文件,编译才能成功。 6 | -------------------------------------------------------------------------------- /example/mfc_timer/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: 这是预编译标头文件。 2 | // 下方列出的文件仅编译一次,提高了将来生成的生成性能。 3 | // 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 4 | // 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。 5 | // 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // 添加要在此处预编译的标头 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /example/mfc_timer/res/mfc_timer.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xhawk18/promise-cpp/556dce7183dc76a229e5f6f82bd5e7ef1203bbfd/example/mfc_timer/res/mfc_timer.ico -------------------------------------------------------------------------------- /example/mfc_timer/res/mfctimer.rc2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xhawk18/promise-cpp/556dce7183dc76a229e5f6f82bd5e7ef1203bbfd/example/mfc_timer/res/mfctimer.rc2 -------------------------------------------------------------------------------- /example/mfc_timer/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。 4 | 5 | // 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将 6 | // 将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /example/multithread_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Promise API implemented by cpp as Javascript promise style 3 | * 4 | * Copyright (c) 2016, xhawk18 5 | * at gmail.com 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "promise-cpp/promise.hpp" 34 | #include "add_ons/simple_task/simple_task.hpp" 35 | 36 | using namespace promise; 37 | namespace chrono = std::chrono; 38 | using steady_clock = std::chrono::steady_clock; 39 | 40 | static const int N = 10000; 41 | 42 | void dump(std::string name, int n, 43 | steady_clock::time_point start, 44 | steady_clock::time_point end) 45 | { 46 | auto ns = chrono::duration_cast(end - start); 47 | std::cout << name << " " << n << " " << 48 | ns.count() / n << 49 | "ns/op" << std::endl; 50 | } 51 | 52 | void task(Service &io, int task_id, int count, int *pcoro, Defer defer) { 53 | if (count == 0) { 54 | -- *pcoro; 55 | if (*pcoro == 0) 56 | defer.resolve(); 57 | return; 58 | } 59 | 60 | io.yield().then([=, &io]() { 61 | task(io, task_id, count - 1, pcoro, defer); 62 | }); 63 | }; 64 | 65 | 66 | Promise test_switch(Service &io, int coro) { 67 | steady_clock::time_point start = steady_clock::now(); 68 | 69 | int *pcoro = new int(coro); 70 | 71 | return newPromise([=, &io](Defer &defer){ 72 | for (int task_id = 0; task_id < coro; ++task_id) { 73 | task(io, task_id, N / coro, pcoro, defer); 74 | } 75 | }).then([=]() { 76 | delete pcoro; 77 | steady_clock::time_point end = steady_clock::now(); 78 | dump("BenchmarkSwitch_" + std::to_string(coro), N, start, end); 79 | }); 80 | } 81 | 82 | 83 | int main() { 84 | Service io; 85 | 86 | std::function func = []() { 87 | }; 88 | promise::any ff = [func]() { 89 | func(); 90 | }; 91 | 92 | promise::newPromise([=](Defer &defer) { 93 | }).then(ff); 94 | //return 0; 95 | 96 | int i = 0; 97 | doWhile([&](DeferLoop &loop) { 98 | #ifdef PM_DEBUG 99 | printf("In while ..., alloc_size = %d\n", (int)(*dbg_alloc_size())); 100 | #else 101 | printf("In while ...\n"); 102 | #endif 103 | //Sleep(5000); 104 | test_switch(io, 1).then([&]() { 105 | return test_switch(io, 10); 106 | }).then([&]() { 107 | return test_switch(io, 100); 108 | }).then([&]() { 109 | 110 | io.setAutoStop(false); 111 | // Run in new thread 112 | return newPromise([](Defer &defer) { 113 | std::thread([=]() { 114 | defer.resolve(); 115 | }).detach(); 116 | 117 | }); 118 | 119 | }).then([&]() { 120 | printf("after thread\n"); 121 | 122 | // Run in io thread 123 | return newPromise([&](Defer &defer) { 124 | io.runInIoThread([=]() { 125 | defer.resolve(); 126 | }); 127 | }); 128 | 129 | }).then([&]() { 130 | io.setAutoStop(true); 131 | 132 | return test_switch(io, 1000); 133 | }).then([&, loop]() { 134 | if (i++ > 3) loop.doBreak(); 135 | return test_switch(io, 10000); 136 | }).then(loop); 137 | }); 138 | 139 | io.run(); 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /example/qt_timer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(qt_timer LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | set(CMAKE_AUTOUIC ON) 8 | set(CMAKE_AUTOMOC ON) 9 | set(CMAKE_AUTORCC ON) 10 | 11 | set(CMAKE_CXX_STANDARD 11) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | 14 | # QtCreator supports the following variables for Android, which are identical to qmake Android variables. 15 | # Check https://doc.qt.io/qt/deployment-android.html for more information. 16 | # They need to be set before the find_package( ...) calls below. 17 | 18 | #if(ANDROID) 19 | # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") 20 | # if (ANDROID_ABI STREQUAL "armeabi-v7a") 21 | # set(ANDROID_EXTRA_LIBS 22 | # ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so 23 | # ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so) 24 | # endif() 25 | #endif() 26 | 27 | find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED) 28 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED) 29 | 30 | set(PROJECT_SOURCES 31 | main.cpp 32 | mainwindow.cpp 33 | mainwindow.h 34 | mainwindow.ui 35 | ../../include/promise-cpp/promise.hpp 36 | ../../include/promise-cpp/promise_inl.hpp 37 | ../../include/promise-cpp/any.hpp 38 | ../../include/promise-cpp/add_ons.hpp 39 | ../../include/promise-cpp/call_traits.hpp 40 | ../../add_ons/qt/promise_qt.hpp 41 | ) 42 | 43 | if(${QT_VERSION_MAJOR} GREATER 5) 44 | qt_add_executable(qt_timer 45 | ${PROJECT_SOURCES} 46 | ) 47 | else() 48 | if(ANDROID) 49 | add_library(qt_timer SHARED 50 | ${PROJECT_SOURCES} 51 | ) 52 | else() 53 | add_executable(qt_timer 54 | ${PROJECT_SOURCES} 55 | ) 56 | endif() 57 | endif() 58 | 59 | target_link_libraries(qt_timer PRIVATE promise promise_qt Qt${QT_VERSION_MAJOR}::Widgets) 60 | #target_include_directories(qt_timer PRIVATE "../..") 61 | 62 | # Set vc debugger PATH 63 | # Supported by cmake 3.13 or higher, ignored by lower version. 64 | string(REGEX REPLACE "[\\/]lib[\\/]cmake[\\/].*$" "/bin" MY_QT_BINARY_PATH ${QT_DIR}) 65 | file(TO_NATIVE_PATH "${MY_QT_BINARY_PATH}" MY_QT_BINARY_PATH) 66 | message("PATH=$(PATH);${MY_QT_BINARY_PATH}") 67 | 68 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_ENVIRONMENT "PATH=$(PATH);${MY_QT_BINARY_PATH}") 69 | -------------------------------------------------------------------------------- /example/qt_timer/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | MainWindow w; 9 | w.show(); 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /example/qt_timer/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "./ui_mainwindow.h" 3 | #include 4 | #include 5 | 6 | using namespace promise; 7 | 8 | 9 | MainWindow::MainWindow(QWidget *parent) 10 | : QMainWindow(parent) 11 | , ui(new Ui::MainWindow) 12 | { 13 | ui->setupUi(this); 14 | 15 | auto updateText = [=](int sourceLine) { 16 | std::stringstream os; 17 | os << "thread id = " << std::this_thread::get_id() << "\n" 18 | << "source line = " << sourceLine; 19 | ui->label->setText(QString::fromStdString(os.str())); 20 | }; 21 | updateText(__LINE__); 22 | 23 | // delay tasks 24 | #if 1 25 | doWhile([=](DeferLoop &loop) { 26 | delay(1000).then([=]() { 27 | updateText(__LINE__); 28 | return delay(1000); 29 | }).then([=]() { 30 | updateText(__LINE__); 31 | return delay(1000); 32 | }).then([=]() { 33 | updateText(__LINE__); 34 | return yield(); 35 | }).then([=]() { 36 | updateText(__LINE__); 37 | return delay(1000); 38 | }).then([=]() { 39 | updateText(__LINE__); 40 | return delay(1000); 41 | }).then([=]() { 42 | updateText(__LINE__); 43 | return delay(1000); 44 | }).then([=]() { 45 | updateText(__LINE__); 46 | return delay(1000); 47 | }).then(loop); //call doWhile 48 | }); 49 | #endif 50 | 51 | } 52 | 53 | MainWindow::~MainWindow() 54 | { 55 | delete ui; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /example/qt_timer/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "add_ons/qt/promise_qt.hpp" 6 | 7 | QT_BEGIN_NAMESPACE 8 | namespace Ui { class MainWindow; } 9 | QT_END_NAMESPACE 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | MainWindow(QWidget *parent = nullptr); 17 | ~MainWindow(); 18 | 19 | private: 20 | Ui::MainWindow *ui; 21 | }; 22 | #endif // MAINWINDOW_H 23 | -------------------------------------------------------------------------------- /example/qt_timer/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 900 10 | 600 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 50 21 | 240 22 | 870 23 | 201 24 | 25 | 26 | 27 | 28 | 18 29 | 30 | 31 | 32 | TextLabel 33 | 34 | 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 800 42 | 23 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /example/qt_timer/qt_timer.pro: -------------------------------------------------------------------------------- 1 | QT += core gui 2 | 3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 4 | 5 | CONFIG += c++11 6 | 7 | # You can make your code fail to compile if it uses deprecated APIs. 8 | # In order to do so, uncomment the following line. 9 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 10 | 11 | SOURCES += \ 12 | main.cpp \ 13 | mainwindow.cpp \ 14 | ../../src/promise.cpp \ 15 | ../../add_ons/qt/promise_qt.cpp 16 | 17 | HEADERS += \ 18 | mainwindow.h \ 19 | ../../include/promise-cpp/promise.hpp 20 | ../../include/promise-cpp/promise_inl.hpp 21 | ../../add_ons/qt/promise_qt.hpp 22 | ../../add_ons/qt/promise_qt_inl.hpp 23 | 24 | FORMS += \ 25 | mainwindow.ui 26 | 27 | INCLUDEPATH += \ 28 | ../../include \ 29 | ../../ 30 | 31 | # Default rules for deployment. 32 | qnx: target.path = /tmp/$${TARGET}/bin 33 | else: unix:!android: target.path = /opt/$${TARGET}/bin 34 | !isEmpty(target.path): INSTALLS += target 35 | -------------------------------------------------------------------------------- /example/simple_benchmark_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Promise API implemented by cpp as Javascript promise style 3 | * 4 | * Copyright (c) 2016, xhawk18 5 | * at gmail.com 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "promise-cpp/promise.hpp" 34 | #include "add_ons/simple_task/simple_task.hpp" 35 | 36 | using namespace promise; 37 | namespace chrono = std::chrono; 38 | using steady_clock = std::chrono::steady_clock; 39 | 40 | static const int N = 1000000; 41 | 42 | void dump(std::string name, int n, 43 | steady_clock::time_point start, 44 | steady_clock::time_point end) 45 | { 46 | auto ns = chrono::duration_cast(end - start); 47 | std::cout << name << " " << n << " " << 48 | ns.count() / n << 49 | "ns/op" << std::endl; 50 | } 51 | 52 | void task(Service &io, int task_id, int count, int *pcoro, Defer defer) { 53 | if (count == 0) { 54 | -- *pcoro; 55 | if (*pcoro == 0) 56 | defer.resolve(); 57 | return; 58 | } 59 | 60 | io.yield().then([=, &io]() { 61 | task(io, task_id, count - 1, pcoro, defer); 62 | }); 63 | }; 64 | 65 | 66 | Promise test_switch(Service &io, int coro) { 67 | steady_clock::time_point start = steady_clock::now(); 68 | 69 | int *pcoro = new int(coro); 70 | 71 | return newPromise([=, &io](Defer &defer){ 72 | for (int task_id = 0; task_id < coro; ++task_id) { 73 | task(io, task_id, N / coro, pcoro, defer); 74 | } 75 | }).then([=]() { 76 | delete pcoro; 77 | steady_clock::time_point end = steady_clock::now(); 78 | dump("BenchmarkSwitch_" + std::to_string(coro), N, start, end); 79 | }); 80 | } 81 | 82 | 83 | int main() { 84 | Service io; 85 | 86 | int i = 0; 87 | doWhile([&](DeferLoop &loop) { 88 | #ifdef PM_DEBUG 89 | printf("In while ..., alloc_size = %d\n", (int)(*dbg_alloc_size())); 90 | #else 91 | printf("In while ...\n"); 92 | #endif 93 | //Sleep(5000); 94 | test_switch(io, 1).then([&]() { 95 | return test_switch(io, 1000); 96 | }).then([&]() { 97 | return test_switch(io, 10000); 98 | }).then([&]() { 99 | return test_switch(io, 100000); 100 | }).then([&, loop]() { 101 | if (i++ > 3) loop.doBreak(); 102 | return test_switch(io, 1000000); 103 | }).then(loop); 104 | }); 105 | 106 | io.run(); 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /example/simple_timer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Promise API implemented by cpp as Javascript promise style 3 | * 4 | * Copyright (c) 2016, xhawk18 5 | * at gmail.com 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "promise-cpp/promise.hpp" 34 | #include "add_ons/simple_task/simple_task.hpp" 35 | 36 | using namespace promise; 37 | 38 | Promise testTimer(Service &service) { 39 | 40 | return service.delay(3000).then([&] { 41 | printf("timer after 3000 ms!\n"); 42 | return service.delay(1000); 43 | }).then([&] { 44 | printf("timer after 1000 ms!\n"); 45 | return service.delay(2000); 46 | }).then([] { 47 | printf("timer after 2000 ms!\n"); 48 | }).fail([] { 49 | printf("timer cancelled!\n"); 50 | }); 51 | } 52 | 53 | int main() { 54 | 55 | Service service; 56 | 57 | testTimer(service); 58 | testTimer(service); 59 | testTimer(service); 60 | 61 | service.run(); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /example/test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var a = new Promise((resolve, reject) => { 4 | reject(15); 5 | resolve(13); 6 | }).then((x) => { 7 | console.log('resolved x = ', x); 8 | }, (x) => { 9 | console.log('rejected x = ', x); 10 | return 18; 11 | }); 12 | 13 | var b = a; 14 | var c = b; 15 | 16 | /*.then(function(n){ 17 | console.log('n = ', n); 18 | return 4; 19 | });/**/ 20 | 21 | 22 | new Promise((resolve, reject) => { 23 | resolve(); 24 | }).then(function(){ 25 | console.log(a); 26 | return c; 27 | }).then(function(x){ 28 | console.log('resolved x = ', x); 29 | return a; 30 | }, function(x){ 31 | console.log('rejected x = ', x); 32 | }).then(function(x){ 33 | console.log('pre finally x = ', x); 34 | }).then(function(x){ 35 | console.log('finally'); 36 | }); 37 | 38 | -------------------------------------------------------------------------------- /example/test0.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Promise API implemented by cpp as Javascript promise style 3 | * 4 | * Copyright (c) 2016, xhawk18 5 | * at gmail.com 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #include 28 | #include 29 | #include 30 | #include "promise-cpp/promise.hpp" 31 | 32 | using namespace promise; 33 | 34 | #define output_func_name() do{ printf("in function %s, line %d\n", __func__, __LINE__); } while(0) 35 | 36 | void test1() { 37 | output_func_name(); 38 | } 39 | 40 | int test2() { 41 | output_func_name(); 42 | return 5; 43 | } 44 | 45 | void test3(int n) { 46 | output_func_name(); 47 | printf("n = %d\n", n); 48 | } 49 | 50 | Promise run(Promise &next){ 51 | 52 | return newPromise([](Defer d) { 53 | output_func_name(); 54 | d.resolve(3, 5, 6); 55 | }).then([](pm_any &any){ 56 | output_func_name(); 57 | return resolve(3, 5, 16); 58 | }).then([](const int &a, int b, int c) { 59 | printf("%d %d %d\n", a, b, c); 60 | output_func_name(); 61 | }).then([](){ 62 | output_func_name(); 63 | }).then([&next](){ 64 | output_func_name(); 65 | next = newPromise([](Defer d) { 66 | output_func_name(); 67 | }); 68 | //Will call next.resole() or next.reject() later 69 | //throw 33; 70 | //next.reject(55, 66); 71 | return next; 72 | }).finally([](int n) { 73 | printf("finally n == %d\n", n); 74 | }).then([](int n, char c) { 75 | output_func_name(); 76 | printf("n = %d, c = %c\n", (int)n, c); 77 | }).then([](char n) { 78 | output_func_name(); 79 | printf("n = %d\n", (int)n); 80 | }).fail([](char n) { 81 | output_func_name(); 82 | printf("n = %d\n", (int)n); 83 | }).fail([](short n) { 84 | output_func_name(); 85 | printf("n = %d\n", (int)n); 86 | }).fail([](int &n) { 87 | output_func_name(); 88 | printf("n = %d\n", (int)n); 89 | }).fail([](const std::string &str) { 90 | output_func_name(); 91 | printf("str = %s\n", str.c_str()); 92 | }).fail([](uint64_t n) { 93 | output_func_name(); 94 | printf("n = %d\n", (int)n); 95 | }).then(test1) 96 | .then(test2) 97 | .then(&test3); 98 | //.always([]() { 99 | // output_func_name(); 100 | //}); 101 | } 102 | 103 | void test_promise_all() { 104 | std::vector ds = { 105 | newPromise([](Defer d) { 106 | output_func_name(); 107 | d.resolve(1); 108 | //d.reject(1); 109 | }), 110 | newPromise([](Defer d) { 111 | output_func_name(); 112 | d.resolve(2); 113 | }) 114 | }; 115 | 116 | all(ds).then([]() { 117 | output_func_name(); 118 | }, []() { 119 | output_func_name(); 120 | }); 121 | } 122 | 123 | int main(int argc, char **argv) { 124 | handleUncaughtException([](Promise &d) { 125 | 126 | d.fail([](long n, int m) { 127 | printf("UncaughtException parameters = %d %d\n", (int)n, m); 128 | }).fail([]() { 129 | printf("UncaughtException\n"); 130 | }); 131 | }); 132 | 133 | test_promise_all(); 134 | 135 | Promise next; 136 | 137 | run(next); 138 | printf("====== after call run ======\n"); 139 | 140 | //next.reject(123, 'a'); 141 | if(next) { 142 | next.reject((long)123, (int)345); //it will go to handleUncaughtException(...) 143 | } 144 | //next.resolve('b'); 145 | //next.reject(std::string("xhchen")); 146 | //next.reject(45); 147 | 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /include/promise-cpp/add_ons.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef INC_COMPATIBILITY_HPP_ 3 | #define INC_COMPATIBILITY_HPP_ 4 | 5 | #include 6 | #include 7 | 8 | #ifdef __cpp_rtti 9 | #include 10 | namespace promise { 11 | using type_index = std::type_index; 12 | 13 | template 14 | inline type_index type_id() { 15 | return typeid(T); 16 | } 17 | } // namespace promise 18 | #else 19 | namespace promise { 20 | using type_index = ptrdiff_t; 21 | 22 | template 23 | inline type_index type_id() { 24 | static char idHolder; 25 | return (type_index)&idHolder; 26 | } 27 | } // namespace promise 28 | #endif 29 | 30 | #ifndef INC_STD_ADD_ONS_HPP_ 31 | #define INC_STD_ADD_ONS_HPP_ 32 | 33 | namespace std { 34 | 35 | 36 | #if (defined(_MSVC_LANG) && _MSVC_LANG < 201402L) || (!defined(_MSVC_LANG) && __cplusplus < 201402L) 37 | template 38 | struct index_sequence { 39 | using type = index_sequence; 40 | using value_type = size_t; 41 | static constexpr std::size_t size() noexcept { return sizeof...(Ints); } 42 | }; 43 | 44 | template 45 | struct _merge_and_renumber; 46 | 47 | template 48 | struct _merge_and_renumber, index_sequence> 49 | : index_sequence { 50 | }; 51 | 52 | template 53 | struct make_index_sequence 54 | : _merge_and_renumber::type, 55 | typename make_index_sequence::type> { 56 | }; 57 | 58 | template<> struct make_index_sequence<0> : index_sequence<> { }; 59 | template<> struct make_index_sequence<1> : index_sequence<0> { }; 60 | #endif //__cplusplus < 201402L 61 | 62 | 63 | #if (defined(_MSVC_LANG) && _MSVC_LANG < 201703L) || (!defined(_MSVC_LANG) && __cplusplus < 201703L) 64 | 65 | template< class... > 66 | using void_t = void; 67 | 68 | #endif //__cplusplus < 201703L 69 | 70 | 71 | #if (defined(_MSVC_LANG) && _MSVC_LANG < 202002L) || (!defined(_MSVC_LANG) && __cplusplus < 202002L) 72 | 73 | template 74 | struct remove_cvref { 75 | typedef typename std::remove_cv::type>::type type; 76 | }; 77 | 78 | #endif //__cplusplus < 202002L 79 | 80 | } //namespace std 81 | 82 | #endif // #ifndef INC_STD_ADD_ONS_HPP_ 83 | 84 | namespace promise { 85 | 86 | template 87 | struct tuple_remove_cvref { 88 | using type = typename std::remove_cvref::type; 89 | }; 90 | template 91 | struct tuple_remove_cvref> { 92 | using type = std::tuple::type...>; 93 | }; 94 | 95 | 96 | 97 | template 98 | struct is_iterable : std::false_type {}; 99 | 100 | // this gets used only when we can call std::begin() and std::end() on that type 101 | template 102 | struct is_iterable())), 103 | decltype(std::end(std::declval())) 104 | >> : std::true_type {}; 105 | 106 | 107 | template struct is_std_function : std::false_type {}; 108 | template struct is_std_function> : std::true_type {}; 109 | 110 | } //namespace promise 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /include/promise-cpp/any.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef INC_PM_ANY_HPP_ 3 | #define INC_PM_ANY_HPP_ 4 | 5 | /* 6 | * Promise API implemented by cpp as Javascript promise style 7 | * 8 | * Copyright (c) 2016, xhawk18 9 | * at gmail.com 10 | * 11 | * The MIT License (MIT) 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in 21 | * all copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | * THE SOFTWARE. 30 | */ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "add_ons.hpp" 37 | #include "call_traits.hpp" 38 | 39 | namespace promise { 40 | 41 | // Any library 42 | // See http://www.boost.org/libs/any for Documentation. 43 | // what: variant type any 44 | // who: contributed by Kevlin Henney, 45 | // with features contributed and bugs found by 46 | // Ed Brey, Mark Rodgers, Peter Dimov, and James Curran 47 | // when: July 2001 48 | // where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95 49 | 50 | class any; 51 | template 52 | inline ValueType any_cast(const any &operand); 53 | 54 | class any { 55 | public: // structors 56 | any() 57 | : content(0) { 58 | } 59 | 60 | template 61 | any(const ValueType &value) 62 | : content(new holder::type>(value)) { 63 | } 64 | 65 | template 66 | any(RET value(ARG...)) 67 | : content(new holder(value)) { 68 | } 69 | 70 | any(const any &other) 71 | : content(other.content ? other.content->clone() : 0) { 72 | } 73 | 74 | // Move constructor 75 | any(any &&other) 76 | : content(other.content) { 77 | other.content = 0; 78 | } 79 | 80 | // Perfect forwarding of ValueType 81 | template 82 | any(ValueType &&value 83 | , typename std::enable_if::value>::type* = nullptr // disable if value has type `any&` 84 | , typename std::enable_if::value>::type* = nullptr) // disable if value has type `const ValueType&&` 85 | : content(new holder::type>(static_cast(value))) { 86 | } 87 | 88 | ~any() { 89 | if (content != nullptr) { 90 | delete(content); 91 | } 92 | } 93 | 94 | any call(const any &arg) const { 95 | return content ? content->call(arg) : any(); 96 | } 97 | 98 | template::value>::type *dummy = nullptr> 100 | inline ValueType cast() const { 101 | return any_cast(*this); 102 | } 103 | 104 | template::value>::type *dummy = nullptr> 106 | inline ValueType cast() const { 107 | if (this->empty()) 108 | return nullptr; 109 | else 110 | return any_cast(*this); 111 | } 112 | 113 | 114 | 115 | public: // modifiers 116 | 117 | any & swap(any & rhs) { 118 | std::swap(content, rhs.content); 119 | return *this; 120 | } 121 | 122 | template 123 | any & operator=(const ValueType & rhs) { 124 | any(rhs).swap(*this); 125 | return *this; 126 | } 127 | 128 | any & operator=(const any & rhs) { 129 | any(rhs).swap(*this); 130 | return *this; 131 | } 132 | 133 | public: // queries 134 | bool empty() const { 135 | return !content; 136 | } 137 | 138 | void clear() { 139 | any().swap(*this); 140 | } 141 | 142 | type_index type() const { 143 | return content ? content->type() : type_id(); 144 | } 145 | 146 | public: // types (public so any_cast can be non-friend) 147 | class placeholder { 148 | public: // structors 149 | virtual ~placeholder() { 150 | } 151 | 152 | public: // queries 153 | virtual type_index type() const = 0; 154 | virtual placeholder *clone() const = 0; 155 | virtual any call(const any &arg) const = 0; 156 | }; 157 | 158 | template 159 | class holder : public placeholder { 160 | public: // structors 161 | holder(const ValueType & value) 162 | : held(value) { 163 | } 164 | 165 | public: // queries 166 | virtual type_index type() const { 167 | return type_id(); 168 | } 169 | 170 | virtual placeholder * clone() const { 171 | return new holder(held); 172 | } 173 | 174 | virtual any call(const any &arg) const { 175 | return any_call(held, arg); 176 | } 177 | public: // representation 178 | ValueType held; 179 | private: // intentionally left unimplemented 180 | holder & operator=(const holder &); 181 | }; 182 | 183 | public: // representation (public so any_cast can be non-friend) 184 | placeholder * content; 185 | }; 186 | 187 | class bad_any_cast : public std::bad_cast { 188 | public: 189 | type_index from_; 190 | type_index to_; 191 | bad_any_cast(const type_index &from, const type_index &to) 192 | : from_(from) 193 | , to_(to) { 194 | //fprintf(stderr, "bad_any_cast: from = %s, to = %s\n", from.name(), to.name()); 195 | } 196 | virtual const char * what() const throw() { 197 | return "bad_any_cast"; 198 | } 199 | }; 200 | 201 | template 202 | ValueType * any_cast(any *operand) { 203 | typedef typename any::template holder holder_t; 204 | return operand && 205 | operand->type() == type_id() 206 | ? &static_cast(operand->content)->held 207 | : 0; 208 | } 209 | 210 | template 211 | inline const ValueType * any_cast(const any *operand) { 212 | return any_cast(const_cast(operand)); 213 | } 214 | 215 | template 216 | ValueType any_cast(any & operand) { 217 | typedef typename std::remove_cvref::type nonref; 218 | 219 | nonref *result = any_cast(&operand); 220 | if (!result) 221 | throw bad_any_cast(operand.type(), type_id()); 222 | return *result; 223 | } 224 | 225 | template 226 | inline ValueType any_cast(const any &operand) { 227 | typedef typename std::remove_cvref::type nonref; 228 | return any_cast(const_cast(operand)); 229 | } 230 | 231 | 232 | 233 | template 234 | struct any_call_t; 235 | 236 | template 237 | struct any_call_t, FUNC> { 238 | 239 | static inline RET call(const typename FUNC::fun_type &func, const any &arg) { 240 | return call(func, arg, std::make_index_sequence()); 241 | } 242 | 243 | template 244 | static inline RET call(const typename FUNC::fun_type &func, const any &arg, const std::index_sequence &) { 245 | using nocvr_argument_type = std::tuple; 246 | using any_arguemnt_type = std::vector; 247 | 248 | const any_arguemnt_type &args = (arg.type() != type_id() 249 | ? any_arguemnt_type{ arg } 250 | : any_cast(arg)); 251 | if(args.size() < sizeof...(NOCVR_ARGS)) 252 | throw bad_any_cast(arg.type(), type_id()); 253 | 254 | return func(any_cast::type &>(args[I])...); 255 | } 256 | }; 257 | 258 | template 259 | struct any_call_t, FUNC> { 260 | 261 | static inline RET call(const typename FUNC::fun_type &func, const any &arg) { 262 | using nocvr_argument_type = std::tuple; 263 | using any_arguemnt_type = std::vector; 264 | 265 | if (arg.type() == type_id()) { 266 | try { 267 | std::rethrow_exception(any_cast(arg)); 268 | } 269 | catch (const NOCVR_ARG &ex_arg) { 270 | return func(const_cast(ex_arg)); 271 | } 272 | } 273 | 274 | if (type_id() == type_id()) { 275 | return func(any_cast(arg)); 276 | } 277 | 278 | const any_arguemnt_type &args = (arg.type() != type_id() 279 | ? any_arguemnt_type{ arg } 280 | : any_cast(arg)); 281 | if(args.size() < 1) 282 | throw bad_any_cast(arg.type(), type_id()); 283 | //printf("[%s] [%s]\n", args.front().type().name(), type_id().name()); 284 | return func(any_cast(args.front())); 285 | } 286 | }; 287 | 288 | 289 | template 290 | struct any_call_t, FUNC> { 291 | static inline RET call(const typename FUNC::fun_type &func, const any &arg) { 292 | using any_arguemnt_type = std::vector; 293 | if (arg.type() != type_id()) 294 | return (func(const_cast(arg))); 295 | 296 | any_arguemnt_type &args = any_cast(arg); 297 | if (args.size() == 0) { 298 | any empty; 299 | return (func(empty)); 300 | } 301 | else if(args.size() == 1) 302 | return (func(args.front())); 303 | else 304 | return (func(const_cast(arg))); 305 | } 306 | }; 307 | 308 | template 309 | struct any_call_with_ret_t { 310 | static inline any call(const typename FUNC::fun_type &func, const any &arg) { 311 | return any_call_t::call(func, arg); 312 | } 313 | }; 314 | 315 | template 316 | struct any_call_with_ret_t { 317 | static inline any call(const typename FUNC::fun_type &func, const any &arg) { 318 | any_call_t::call(func, arg); 319 | return any(); 320 | } 321 | }; 322 | 323 | template 324 | inline any any_call(const FUNC &func, const any &arg) { 325 | using func_t = call_traits; 326 | using nocvr_argument_type = typename tuple_remove_cvref::type; 327 | const auto &stdFunc = func_t::to_std_function(func); 328 | if (!stdFunc) 329 | return any(); 330 | 331 | if (arg.type() == type_id()) { 332 | try { 333 | std::rethrow_exception(any_cast(arg)); 334 | } 335 | catch (const any &ex_arg) { 336 | return any_call_with_ret_t::result_type, nocvr_argument_type, func_t>::call(stdFunc, ex_arg); 337 | } 338 | catch (...) { 339 | } 340 | } 341 | 342 | return any_call_with_ret_t::result_type, nocvr_argument_type, func_t>::call(stdFunc, arg); 343 | } 344 | 345 | using pm_any = any; 346 | 347 | // Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved. 348 | // 349 | // Distributed under the Boost Software License, Version 1.0. (See 350 | // accompanying file LICENSE_1_0.txt or copy at 351 | // http://www.boost.org/LICENSE_1_0.txt) 352 | 353 | 354 | } 355 | #endif 356 | -------------------------------------------------------------------------------- /include/promise-cpp/call_traits.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************************ 2 | * * 3 | * Copyright (c) 2021 xhchen8018@163.com * 4 | * * 5 | * License: MIT License * 6 | * * 7 | * Permission is hereby granted, free of charge, to any person obtaining * 8 | * a copy of this software and associated documentation files (the "Software"), * 9 | * to deal in the Software without restriction, including without limitation * 10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, * 11 | * and/or sell copies of the Software, and to permit persons to whom the * 12 | * Software is furnished to do so, subject to the following conditions: * 13 | * * 14 | * The above copyright notice and this permission notice shall be included in * 15 | * all copies or substantial portions of the Software. * 16 | * * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * 23 | * SOFTWARE. * 24 | * * 25 | *************************************************************************************/ 26 | 27 | /* 将任意可执行函数或对象的可调用性质 28 | * 29 | * 判断类型T是否可被调用 30 | * call_traits::is_callable 31 | * 32 | * 将对象t转为std::function 33 | * call_traits::to_std_function(t) 34 | * 如果t不可调用,会返回一个空函数 35 | */ 36 | 37 | #pragma once 38 | #ifndef INC_CALL_TRAITS_HPP_ 39 | #define INC_CALL_TRAITS_HPP_ 40 | 41 | #include 42 | #include 43 | #include 44 | 45 | namespace promise { 46 | 47 | template::value 49 | || std::is_fundamental::value 50 | || (std::is_pointer::value && !std::is_function::type>::value) 51 | || std::is_union::value 52 | || std::is_enum::value 53 | || std::is_array::value) && !std::is_function::value> 54 | struct call_traits_impl; 55 | 56 | template 57 | struct has_operator_parentheses { 58 | private: 59 | struct Fallback { void operator()(); }; 60 | struct Derived : T, Fallback { }; 61 | 62 | template struct Check; 63 | 64 | template 65 | static std::true_type test(...); 66 | 67 | template 68 | static std::false_type test(Check*); 69 | 70 | public: 71 | typedef decltype(test(nullptr)) type; 72 | }; 73 | 74 | template 75 | struct operator_parentheses_traits { 76 | typedef std::function fun_type; 77 | typedef void result_type; 78 | typedef std::tuple<> argument_type; 79 | 80 | static fun_type to_std_function(const T &t) { 81 | (void)t; 82 | return nullptr; 83 | } 84 | }; 85 | 86 | template 87 | struct operator_parentheses_traits { 88 | private: 89 | typedef decltype(&FUNCTOR::operator()) callable_type; 90 | typedef call_traits_impl the_type; 91 | public: 92 | typedef typename the_type::fun_type fun_type; 93 | typedef typename the_type::result_type result_type; 94 | typedef typename the_type::argument_type argument_type; 95 | 96 | static fun_type to_std_function(const FUNCTOR &functor) { 97 | if (is_std_function::value) { 98 | // on windows, FUNCTOR is not same as std::function<...>, must return functor directly. 99 | return functor; 100 | } 101 | else { 102 | return call_traits_impl::to_std_function(const_cast(functor), &FUNCTOR::operator()); 103 | } 104 | } 105 | }; 106 | 107 | //template 108 | //struct call_traits_impl { 109 | // typedef call_traits_impl the_type; 110 | // static const bool is_callable = the_type::is_callable; 111 | // typedef typename the_type::fun_type fun_type; 112 | 113 | // static fun_type to_std_function(const T &t) { 114 | // return the_type::to_std_function(t); 115 | // } 116 | //}; 117 | 118 | template 119 | struct call_traits_impl { 120 | static const bool is_callable = false; 121 | typedef std::function fun_type; 122 | typedef void result_type; 123 | typedef std::tuple<> argument_type; 124 | 125 | static fun_type to_std_function(const T &t) { 126 | (void)t; 127 | return nullptr; 128 | } 129 | }; 130 | 131 | 132 | template 133 | struct call_traits_impl { 134 | static const bool is_callable = true; 135 | typedef std::function fun_type; 136 | typedef RET result_type; 137 | typedef std::tuple argument_type; 138 | 139 | static fun_type to_std_function(T &obj, RET(T::*func)(ARG...)) { 140 | return [obj, func](ARG...arg) -> RET { 141 | return (const_cast::type &>(obj).*func)(arg...); 142 | }; 143 | } 144 | 145 | // Never called, to make compiler happy 146 | static fun_type to_std_function(RET(T::*)(ARG...)) { 147 | return nullptr; 148 | } 149 | }; 150 | 151 | template 152 | struct call_traits_impl { 153 | static const bool is_callable = true; 154 | typedef std::function fun_type; 155 | typedef RET result_type; 156 | typedef std::tuple argument_type; 157 | 158 | static fun_type to_std_function(T &obj, RET(T:: *func)(ARG...) const) { 159 | return [obj, func](ARG...arg) -> RET { 160 | return (obj.*func)(arg...); 161 | }; 162 | } 163 | 164 | // Never called, to make compiler happy 165 | static fun_type to_std_function(RET(T:: *)(ARG...)) { 166 | return nullptr; 167 | } 168 | }; 169 | 170 | template 171 | struct call_traits_impl { 172 | static const bool is_callable = true; 173 | typedef std::function fun_type; 174 | typedef RET result_type; 175 | typedef std::tuple argument_type; 176 | 177 | static fun_type to_std_function(RET(*func)(ARG...)) { 178 | return func; 179 | } 180 | }; 181 | 182 | template 183 | struct call_traits_impl { 184 | static const bool is_callable = true; 185 | typedef std::function fun_type; 186 | typedef RET result_type; 187 | typedef std::tuple argument_type; 188 | 189 | static fun_type to_std_function(RET(*func)(ARG...)) { 190 | return func; 191 | } 192 | }; 193 | 194 | template 195 | struct call_traits_impl { 196 | static const bool is_callable = has_operator_parentheses::type::value; 197 | private: 198 | typedef operator_parentheses_traits the_type; 199 | public: 200 | typedef typename the_type::fun_type fun_type; 201 | typedef typename the_type::result_type result_type; 202 | typedef typename the_type::argument_type argument_type; 203 | 204 | static fun_type to_std_function(const T &t) { 205 | return the_type::to_std_function(t); 206 | } 207 | }; 208 | 209 | template 210 | struct call_traits { 211 | private: 212 | using RawT = typename std::remove_cv::type>::type; 213 | typedef call_traits_impl the_type; 214 | public: 215 | static const bool is_callable = the_type::is_callable; 216 | typedef typename the_type::fun_type fun_type; 217 | typedef typename the_type::result_type result_type; 218 | typedef typename the_type::argument_type argument_type; 219 | 220 | static fun_type to_std_function(const T &t) { 221 | return the_type::to_std_function(t); 222 | } 223 | }; 224 | 225 | #if 0 // Not used currently 226 | 227 | template 228 | struct subst_gather {}; 229 | 230 | template class Obj, typename T, typename ...P> 231 | struct subst_matcher; 232 | 233 | template class Obj, typename ...P1, typename T, typename ...P2> 234 | struct subst_matcher, T, P2...> 235 | { 236 | using type = typename subst_matcher< 237 | (lastN >= std::tuple_size>::value), 238 | lastN, Obj, subst_gather, P2...>::type; 239 | }; 240 | 241 | template class Obj, typename ...P1, typename ...P2> 242 | struct subst_matcher, P2...> 243 | { 244 | using type = Obj; 245 | }; 246 | 247 | template class Obj, typename ...P> 248 | struct subst_all_but_last { 249 | using type = typename subst_matcher< 250 | (lastN >= std::tuple_size>::value), 251 | lastN, Obj, subst_gather<>, P...>::type; 252 | }; 253 | 254 | 255 | template 256 | struct call_with_more_args_helper { 257 | template 258 | static auto call(FUNC &&func, ARGS&&...args, MORE&&...) -> typename call_traits::result_type { 259 | return func(args...); 260 | } 261 | }; 262 | 263 | 264 | template 265 | auto call_with_more_args(FUNC &&func, ARGS&&...args) -> typename call_traits::result_type { 266 | using Type = typename subst_all_but_last< 267 | (std::tuple_size>::value - std::tuple_size::argument_type>::value), 268 | call_with_more_args_helper, ARGS...>::type; 269 | return Type::call(std::forward(func), std::forward(args)...); 270 | } 271 | 272 | #endif 273 | 274 | } 275 | #endif 276 | -------------------------------------------------------------------------------- /include/promise-cpp/promise.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef INC_PROMISE_HPP_ 3 | #define INC_PROMISE_HPP_ 4 | 5 | #if defined PROMISE_HEADONLY 6 | #define PROMISE_API inline 7 | #elif defined PROMISE_BUILD_SHARED 8 | 9 | #if defined(_WIN32) || defined(__CYGWIN__) 10 | # if defined(promise_EXPORTS) // add by CMake 11 | # ifdef __GNUC__ 12 | # define PROMISE_API __attribute__(dllexport) 13 | # else 14 | # define PROMISE_API __declspec(dllexport) 15 | # endif 16 | # else 17 | # ifdef __GNUC__ 18 | # define PROMISE_API __attribute__(dllimport) 19 | # else 20 | # define PROMISE_API __declspec(dllimport) 21 | # endif 22 | # endif // promise_EXPORTS 23 | 24 | #elif defined __GNUC__ 25 | # if __GNUC__ >= 4 26 | # define PROMISE_API __attribute__ ((visibility ("default"))) 27 | # else 28 | # define PROMISE_API 29 | # endif 30 | 31 | #elif defined __clang__ 32 | # define PROMISE_API __attribute__ ((visibility ("default"))) 33 | #else 34 | # error "Do not know how to export classes for this platform" 35 | #endif 36 | 37 | #else 38 | #define PROMISE_API 39 | #endif 40 | 41 | #ifndef PROMISE_MULTITHREAD 42 | # define PROMISE_MULTITHREAD 1 43 | #endif 44 | 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include "any.hpp" 53 | 54 | namespace promise { 55 | 56 | enum class TaskState { 57 | kPending, 58 | kResolved, 59 | kRejected 60 | }; 61 | 62 | struct PromiseHolder; 63 | struct SharedPromise; 64 | class Promise; 65 | 66 | struct Task { 67 | TaskState state_; 68 | std::weak_ptr promiseHolder_; 69 | any onResolved_; 70 | any onRejected_; 71 | }; 72 | 73 | #if PROMISE_MULTITHREAD 74 | struct Mutex { 75 | public: 76 | PROMISE_API Mutex(); 77 | PROMISE_API void lock(); 78 | PROMISE_API void unlock(); 79 | PROMISE_API void lock(size_t lock_count); 80 | PROMISE_API void unlock(size_t lock_count); 81 | inline size_t lock_count() const { return lock_count_; } 82 | std::condition_variable_any cond_; 83 | private: 84 | std::recursive_mutex mutex_; 85 | size_t lock_count_; 86 | }; 87 | #endif 88 | 89 | /* 90 | * Task state in TaskList always be kPending 91 | */ 92 | struct PromiseHolder { 93 | PROMISE_API PromiseHolder(); 94 | PROMISE_API ~PromiseHolder(); 95 | std::list> owners_; 96 | std::list> pendingTasks_; 97 | TaskState state_; 98 | any value_; 99 | #if PROMISE_MULTITHREAD 100 | std::shared_ptr mutex_; 101 | #endif 102 | 103 | PROMISE_API void dump() const; 104 | PROMISE_API static any *getUncaughtExceptionHandler(); 105 | PROMISE_API static any *getDefaultUncaughtExceptionHandler(); 106 | PROMISE_API static void onUncaughtException(const any &arg); 107 | PROMISE_API static void handleUncaughtException(const any &onUncaughtException); 108 | }; 109 | 110 | // Check if ...ARGS only has one any type 111 | template 112 | struct is_one_any : public std::is_same>::type, std::tuple> { 113 | }; 114 | 115 | struct SharedPromise { 116 | std::shared_ptr promiseHolder_; 117 | PROMISE_API void dump() const; 118 | #if PROMISE_MULTITHREAD 119 | PROMISE_API std::shared_ptr obtainLock() const; 120 | #endif 121 | }; 122 | 123 | class Defer { 124 | public: 125 | template::value>::type *dummy = nullptr> 127 | inline void resolve(ARGS &&...args) const { 128 | resolve(any{ std::vector{std::forward(args)...} }); 129 | } 130 | 131 | template::value>::type *dummy = nullptr> 133 | inline void reject(ARGS &&...args) const { 134 | reject(any{ std::vector{std::forward(args)...} }); 135 | } 136 | 137 | PROMISE_API void resolve(const any &arg) const; 138 | PROMISE_API void reject(const any &arg) const; 139 | 140 | PROMISE_API Promise getPromise() const; 141 | 142 | private: 143 | friend class Promise; 144 | friend PROMISE_API Promise newPromise(const std::function &run); 145 | PROMISE_API Defer(const std::shared_ptr &task); 146 | std::shared_ptr task_; 147 | std::shared_ptr sharedPromise_; 148 | }; 149 | 150 | class DeferLoop { 151 | public: 152 | template::value>::type *dummy = nullptr> 154 | inline void doBreak(ARGS &&...args) const { 155 | doBreak(any{ std::vector{std::forward(args)...} }); 156 | } 157 | 158 | template::value>::type *dummy = nullptr> 160 | inline void reject(ARGS &&...args) const { 161 | reject(any{ std::vector{std::forward(args)...} }); 162 | } 163 | 164 | PROMISE_API void doContinue() const; 165 | PROMISE_API void doBreak(const any &arg) const; 166 | PROMISE_API void reject(const any &arg) const; 167 | 168 | 169 | PROMISE_API Promise getPromise() const; 170 | 171 | private: 172 | friend PROMISE_API Promise doWhile(const std::function &run); 173 | PROMISE_API DeferLoop(const Defer &cb); 174 | Defer defer_; 175 | }; 176 | 177 | class Promise { 178 | public: 179 | PROMISE_API Promise &then(const any &deferOrPromiseOrOnResolved); 180 | PROMISE_API Promise &then(const any &onResolved, const any &onRejected); 181 | PROMISE_API Promise &fail(const any &onRejected); 182 | PROMISE_API Promise &always(const any &onAlways); 183 | PROMISE_API Promise &finally(const any &onFinally); 184 | 185 | template::value>::type *dummy = nullptr> 187 | inline void resolve(ARGS &&...args) const { 188 | resolve(any{ std::vector{std::forward(args)...} }); 189 | } 190 | template::value>::type *dummy = nullptr> 192 | inline void reject(ARGS &&...args) const { 193 | reject(any{ std::vector{std::forward(args)...} }); 194 | } 195 | 196 | PROMISE_API void resolve(const any &arg) const; 197 | PROMISE_API void reject(const any &arg) const; 198 | 199 | PROMISE_API void clear(); 200 | PROMISE_API operator bool() const; 201 | 202 | PROMISE_API void dump() const; 203 | 204 | std::shared_ptr sharedPromise_; 205 | }; 206 | 207 | 208 | PROMISE_API Promise newPromise(const std::function &run); 209 | PROMISE_API Promise newPromise(); 210 | PROMISE_API Promise doWhile(const std::function &run); 211 | template 212 | inline Promise resolve(ARGS &&...args) { 213 | return newPromise([&args...](Defer &defer) { defer.resolve(std::forward(args)...); }); 214 | } 215 | 216 | template 217 | inline Promise reject(ARGS &&...args) { 218 | return newPromise([&args...](Defer &defer) { defer.reject(std::forward(args)...); }); 219 | } 220 | 221 | 222 | /* Returns a promise that resolves when all of the promises in the iterable 223 | argument have resolved, or rejects with the reason of the first passed 224 | promise that rejects. */ 225 | PROMISE_API Promise all(const std::list &promise_list); 226 | template::value 228 | && !std::is_same>::value 229 | >::type *dummy = nullptr> 230 | inline Promise all(const PROMISE_LIST &promise_list) { 231 | std::list copy_list = { std::begin(promise_list), std::end(promise_list) }; 232 | return all(copy_list); 233 | } 234 | template ::value>::type *dummy = nullptr> 235 | inline Promise all(PROMISE0 defer0, PROMISE_LIST ...promise_list) { 236 | return all(std::list{ defer0, promise_list ... }); 237 | } 238 | 239 | 240 | /* returns a promise that resolves or rejects as soon as one of 241 | the promises in the iterable resolves or rejects, with the value 242 | or reason from that promise. */ 243 | PROMISE_API Promise race(const std::list &promise_list); 244 | template::value 246 | && !std::is_same>::value 247 | >::type *dummy = nullptr> 248 | inline Promise race(const PROMISE_LIST &promise_list) { 249 | std::list copy_list = { std::begin(promise_list), std::end(promise_list) }; 250 | return race(copy_list); 251 | } 252 | template ::value>::type *dummy = nullptr> 253 | inline Promise race(PROMISE0 defer0, PROMISE_LIST ...promise_list) { 254 | return race(std::list{ defer0, promise_list ... }); 255 | } 256 | 257 | 258 | PROMISE_API Promise raceAndReject(const std::list &promise_list); 259 | template::value 261 | && !std::is_same>::value 262 | >::type *dummy = nullptr> 263 | inline Promise raceAndReject(const PROMISE_LIST &promise_list) { 264 | std::list copy_list = { std::begin(promise_list), std::end(promise_list) }; 265 | return raceAndReject(copy_list); 266 | } 267 | template ::value>::type *dummy = nullptr> 268 | inline Promise raceAndReject(PROMISE0 defer0, PROMISE_LIST ...promise_list) { 269 | return raceAndReject(std::list{ defer0, promise_list ... }); 270 | } 271 | 272 | 273 | PROMISE_API Promise raceAndResolve(const std::list &promise_list); 274 | template::value 276 | && !std::is_same>::value 277 | >::type *dummy = nullptr> 278 | inline Promise raceAndResolve(const PROMISE_LIST &promise_list) { 279 | std::list copy_list = { std::begin(promise_list), std::end(promise_list) }; 280 | return raceAndResolve(copy_list); 281 | } 282 | template ::value>::type *dummy = nullptr> 283 | inline Promise raceAndResolve(PROMISE0 defer0, PROMISE_LIST ...promise_list) { 284 | return raceAndResolve(std::list{ defer0, promise_list ... }); 285 | } 286 | 287 | inline void handleUncaughtException(const any &onUncaughtException) { 288 | PromiseHolder::handleUncaughtException(onUncaughtException); 289 | } 290 | 291 | } // namespace promise 292 | 293 | #ifdef PROMISE_HEADONLY 294 | #include "promise_inl.hpp" 295 | #endif 296 | 297 | #endif 298 | -------------------------------------------------------------------------------- /include/promise-cpp/promise_inl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef INC_PROMISE_INL_HPP_ 3 | #define INC_PROMISE_INL_HPP_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "promise.hpp" 10 | 11 | namespace promise { 12 | 13 | static inline void healthyCheck(int line, PromiseHolder *promiseHolder) { 14 | (void)line; 15 | (void)promiseHolder; 16 | #ifndef NDEBUG 17 | if (!promiseHolder) { 18 | fprintf(stderr, "line = %d, %d, promiseHolder is null\n", line, __LINE__); 19 | throw std::runtime_error(""); 20 | } 21 | 22 | for (const auto &owner_ : promiseHolder->owners_) { 23 | auto owner = owner_.lock(); 24 | if (owner && owner->promiseHolder_.get() != promiseHolder) { 25 | fprintf(stderr, "line = %d, %d, owner->promiseHolder_ = %p, promiseHolder = %p\n", 26 | line, __LINE__, 27 | owner->promiseHolder_.get(), 28 | promiseHolder); 29 | throw std::runtime_error(""); 30 | } 31 | } 32 | 33 | for (const std::shared_ptr &task : promiseHolder->pendingTasks_) { 34 | if (!task) { 35 | fprintf(stderr, "line = %d, %d, promiseHolder = %p, task is null\n", line, __LINE__, promiseHolder); 36 | throw std::runtime_error(""); 37 | } 38 | if (task->state_ != TaskState::kPending) { 39 | fprintf(stderr, "line = %d, %d, promiseHolder = %p, task = %p, task->state_ = %d\n", line, __LINE__, 40 | promiseHolder, task.get(), (int)task->state_); 41 | throw std::runtime_error(""); 42 | } 43 | if (task->promiseHolder_.lock().get() != promiseHolder) { 44 | fprintf(stderr, "line = %d, %d, promiseHolder = %p, task = %p, task->promiseHolder_ = %p\n", line, __LINE__, 45 | promiseHolder, task.get(), task->promiseHolder_.lock().get()); 46 | throw std::runtime_error(""); 47 | } 48 | } 49 | #endif 50 | } 51 | 52 | void Promise::dump() const { 53 | #ifndef NDEBUG 54 | printf("Promise = %p, SharedPromise = %p\n", this, this->sharedPromise_.get()); 55 | if (this->sharedPromise_) 56 | this->sharedPromise_->dump(); 57 | #endif 58 | } 59 | 60 | void SharedPromise::dump() const { 61 | #ifndef NDEBUG 62 | printf("SharedPromise = %p, PromiseHolder = %p\n", this, this->promiseHolder_.get()); 63 | if (this->promiseHolder_) 64 | this->promiseHolder_->dump(); 65 | #endif 66 | } 67 | 68 | void PromiseHolder::dump() const { 69 | #ifndef NDEBUG 70 | printf("PromiseHolder = %p, owners = %d, pendingTasks = %d\n", this, (int)this->owners_.size(), (int)this->pendingTasks_.size()); 71 | for (const auto &owner_ : owners_) { 72 | auto owner = owner_.lock(); 73 | printf(" owner = %p\n", owner.get()); 74 | } 75 | for (const auto &task : pendingTasks_) { 76 | if (task) { 77 | auto promiseHolder = task->promiseHolder_.lock(); 78 | printf(" task = %p, PromiseHolder = %p\n", task.get(), promiseHolder.get()); 79 | } 80 | else { 81 | printf(" task = %p\n", task.get()); 82 | } 83 | } 84 | #endif 85 | } 86 | 87 | static inline void join(const std::shared_ptr &left, const std::shared_ptr &right) { 88 | healthyCheck(__LINE__, left.get()); 89 | healthyCheck(__LINE__, right.get()); 90 | //left->dump(); 91 | //right->dump(); 92 | 93 | for (const std::shared_ptr &task : right->pendingTasks_) { 94 | task->promiseHolder_ = left; 95 | } 96 | left->pendingTasks_.splice(left->pendingTasks_.end(), right->pendingTasks_); 97 | 98 | std::list> owners; 99 | owners.splice(owners.end(), right->owners_); 100 | 101 | // Looked on resolved if the PromiseHolder was joined to another, 102 | // so that it will not throw onUncaughtException when destroyed. 103 | right->state_ = TaskState::kResolved; 104 | 105 | if(owners.size() > 100) { 106 | fprintf(stderr, "Maybe memory leak, too many promise owners: %d", (int)owners.size()); 107 | } 108 | 109 | for (const std::weak_ptr &owner_ : owners) { 110 | std::shared_ptr owner = owner_.lock(); 111 | if (owner) { 112 | owner->promiseHolder_ = left; 113 | left->owners_.push_back(owner); 114 | } 115 | } 116 | 117 | //left->dump(); 118 | //right->dump(); 119 | //fprintf(stderr, "left->promiseHolder_->owners_ size = %d\n", (int)left->promiseHolder_->owners_.size()); 120 | 121 | 122 | healthyCheck(__LINE__, left.get()); 123 | healthyCheck(__LINE__, right.get()); 124 | } 125 | 126 | //Unlock and then lock 127 | #if PROMISE_MULTITHREAD 128 | struct unlock_guard_t { 129 | inline unlock_guard_t(std::shared_ptr mutex) 130 | : mutex_(mutex) 131 | , lock_count_(mutex->lock_count()) { 132 | mutex_->unlock(lock_count_); 133 | } 134 | inline ~unlock_guard_t() { 135 | mutex_->lock(lock_count_); 136 | } 137 | std::shared_ptr mutex_; 138 | size_t lock_count_; 139 | }; 140 | #endif 141 | 142 | static inline void call(std::shared_ptr task) { 143 | std::shared_ptr promiseHolder; //Can hold the temporarily created promise 144 | while (true) { 145 | promiseHolder = task->promiseHolder_.lock(); 146 | if (!promiseHolder) return; 147 | 148 | // lock for 1st stage 149 | { 150 | #if PROMISE_MULTITHREAD 151 | std::shared_ptr mutex = promiseHolder->mutex_; 152 | std::unique_lock lock(*mutex); 153 | #endif 154 | 155 | if (task->state_ != TaskState::kPending) return; 156 | if (promiseHolder->state_ == TaskState::kPending) return; 157 | 158 | std::list> &pendingTasks = promiseHolder->pendingTasks_; 159 | //promiseHolder->dump(); 160 | 161 | #if PROMISE_MULTITHREAD 162 | while (pendingTasks.front() != task) { 163 | mutex->cond_.wait>(lock); 164 | } 165 | #else 166 | assert(pendingTasks.front() == task); 167 | #endif 168 | pendingTasks.pop_front(); 169 | task->state_ = promiseHolder->state_; 170 | //promiseHolder->dump(); 171 | 172 | try { 173 | if (promiseHolder->state_ == TaskState::kResolved) { 174 | if (task->onResolved_.empty() 175 | || task->onResolved_.type() == type_id()) { 176 | //to next resolved task 177 | } 178 | else { 179 | promiseHolder->state_ = TaskState::kPending; // avoid recursive task using this state 180 | #if PROMISE_MULTITHREAD 181 | std::shared_ptr mutex0 = nullptr; 182 | auto call = [&]() -> any { 183 | unlock_guard_t lock_inner(mutex); 184 | const any &value = task->onResolved_.call(promiseHolder->value_); 185 | // Make sure the returned promised is locked before than "mutex" 186 | if (value.type() == type_id()) { 187 | Promise &promise = value.cast(); 188 | mutex0 = promise.sharedPromise_->obtainLock(); 189 | } 190 | return value; 191 | }; 192 | const any &value = call(); 193 | 194 | if (mutex0 == nullptr) { 195 | promiseHolder->value_ = value; 196 | promiseHolder->state_ = TaskState::kResolved; 197 | } 198 | else { 199 | // join the promise 200 | Promise &promise = value.cast(); 201 | std::lock_guard lock0(*mutex0, std::adopt_lock_t()); 202 | join(promise.sharedPromise_->promiseHolder_, promiseHolder); 203 | promiseHolder = promise.sharedPromise_->promiseHolder_; 204 | } 205 | #else 206 | const any &value = task->onResolved_.call(promiseHolder->value_); 207 | 208 | if (value.type() != type_id()) { 209 | promiseHolder->value_ = value; 210 | promiseHolder->state_ = TaskState::kResolved; 211 | } 212 | else { 213 | // join the promise 214 | Promise &promise = value.cast(); 215 | join(promise.sharedPromise_->promiseHolder_, promiseHolder); 216 | promiseHolder = promise.sharedPromise_->promiseHolder_; 217 | } 218 | #endif 219 | } 220 | } 221 | else if (promiseHolder->state_ == TaskState::kRejected) { 222 | if (task->onRejected_.empty() 223 | || task->onRejected_.type() == type_id()) { 224 | //to next rejected task 225 | //promiseHolder->value_ = promiseHolder->value_; 226 | //promiseHolder->state_ = TaskState::kRejected; 227 | } 228 | else { 229 | try { 230 | promiseHolder->state_ = TaskState::kPending; // avoid recursive task using this state 231 | #if PROMISE_MULTITHREAD 232 | std::shared_ptr mutex0 = nullptr; 233 | auto call = [&]() -> any { 234 | unlock_guard_t lock_inner(mutex); 235 | const any &value = task->onRejected_.call(promiseHolder->value_); 236 | // Make sure the returned promised is locked before than "mutex" 237 | if (value.type() == type_id()) { 238 | Promise &promise = value.cast(); 239 | mutex0 = promise.sharedPromise_->obtainLock(); 240 | } 241 | return value; 242 | }; 243 | const any &value = call(); 244 | 245 | if (mutex0 == nullptr) { 246 | promiseHolder->value_ = value; 247 | promiseHolder->state_ = TaskState::kResolved; 248 | } 249 | else { 250 | // join the promise 251 | Promise promise = value.cast(); 252 | std::lock_guard lock0(*mutex0, std::adopt_lock_t()); 253 | join(promise.sharedPromise_->promiseHolder_, promiseHolder); 254 | promiseHolder = promise.sharedPromise_->promiseHolder_; 255 | } 256 | #else 257 | const any &value = task->onRejected_.call(promiseHolder->value_); 258 | 259 | if (value.type() != type_id()) { 260 | promiseHolder->value_ = value; 261 | promiseHolder->state_ = TaskState::kResolved; 262 | } 263 | else { 264 | // join the promise 265 | Promise &promise = value.cast(); 266 | join(promise.sharedPromise_->promiseHolder_, promiseHolder); 267 | promiseHolder = promise.sharedPromise_->promiseHolder_; 268 | } 269 | #endif 270 | } 271 | catch (const bad_any_cast &) { 272 | //just go through if argument type is not match 273 | promiseHolder->state_ = TaskState::kRejected; 274 | } 275 | } 276 | } 277 | } 278 | catch (const promise::bad_any_cast &ex) { 279 | fprintf(stderr, "promise::bad_any_cast: %s -> %s", ex.from_.name(), ex.to_.name()); 280 | promiseHolder->value_ = std::current_exception(); 281 | promiseHolder->state_ = TaskState::kRejected; 282 | } 283 | catch (...) { 284 | promiseHolder->value_ = std::current_exception(); 285 | promiseHolder->state_ = TaskState::kRejected; 286 | } 287 | 288 | task->onResolved_.clear(); 289 | task->onRejected_.clear(); 290 | } 291 | 292 | // lock for 2nd stage 293 | // promiseHolder may be changed, so we need to lock again 294 | { 295 | // get next task 296 | #if PROMISE_MULTITHREAD 297 | std::shared_ptr mutex = promiseHolder->mutex_; 298 | std::lock_guard lock(*mutex); 299 | #endif 300 | std::list> &pendingTasks2 = promiseHolder->pendingTasks_; 301 | if (pendingTasks2.size() == 0) { 302 | return; 303 | } 304 | 305 | task = pendingTasks2.front(); 306 | } 307 | } 308 | } 309 | 310 | Defer::Defer(const std::shared_ptr &task) { 311 | std::shared_ptr sharedPromise(new SharedPromise{ task->promiseHolder_.lock() }); 312 | #if PROMISE_MULTITHREAD 313 | std::shared_ptr mutex = sharedPromise->obtainLock(); 314 | std::lock_guard lock(*mutex, std::adopt_lock_t()); 315 | #endif 316 | 317 | task_ = task; 318 | sharedPromise_ = sharedPromise; 319 | } 320 | 321 | 322 | void Defer::resolve(const any &arg) const { 323 | #if PROMISE_MULTITHREAD 324 | std::shared_ptr mutex = this->sharedPromise_->obtainLock(); 325 | std::lock_guard lock(*mutex, std::adopt_lock_t()); 326 | #endif 327 | 328 | if (task_->state_ != TaskState::kPending) return; 329 | std::shared_ptr &promiseHolder = sharedPromise_->promiseHolder_; 330 | promiseHolder->state_ = TaskState::kResolved; 331 | promiseHolder->value_ = arg; 332 | call(task_); 333 | } 334 | 335 | void Defer::reject(const any &arg) const { 336 | #if PROMISE_MULTITHREAD 337 | std::shared_ptr mutex = this->sharedPromise_->obtainLock(); 338 | std::lock_guard lock(*mutex, std::adopt_lock_t()); 339 | #endif 340 | 341 | if (task_->state_ != TaskState::kPending) return; 342 | std::shared_ptr &promiseHolder = sharedPromise_->promiseHolder_; 343 | promiseHolder->state_ = TaskState::kRejected; 344 | promiseHolder->value_ = arg; 345 | call(task_); 346 | } 347 | 348 | 349 | Promise Defer::getPromise() const { 350 | return Promise{ sharedPromise_ }; 351 | } 352 | 353 | 354 | struct DoBreakTag {}; 355 | 356 | DeferLoop::DeferLoop(const Defer &defer) 357 | : defer_(defer) { 358 | } 359 | 360 | void DeferLoop::doContinue() const { 361 | defer_.resolve(); 362 | } 363 | 364 | void DeferLoop::doBreak(const any &arg) const { 365 | defer_.reject(DoBreakTag(), arg); 366 | } 367 | 368 | void DeferLoop::reject(const any &arg) const { 369 | defer_.reject(arg); 370 | } 371 | 372 | Promise DeferLoop::getPromise() const { 373 | return defer_.getPromise(); 374 | } 375 | 376 | #if PROMISE_MULTITHREAD 377 | Mutex::Mutex() 378 | : cond_() 379 | , mutex_() 380 | , lock_count_(0) { 381 | } 382 | 383 | void Mutex::lock() { 384 | mutex_.lock(); 385 | ++lock_count_; 386 | } 387 | 388 | void Mutex::unlock() { 389 | --lock_count_; 390 | mutex_.unlock(); 391 | } 392 | 393 | void Mutex::lock(size_t lock_count) { 394 | for (size_t i = 0; i < lock_count; ++i) 395 | this->lock(); 396 | } 397 | 398 | void Mutex::unlock(size_t lock_count) { 399 | for (size_t i = 0; i < lock_count; ++i) 400 | this->unlock(); 401 | } 402 | #endif 403 | 404 | PromiseHolder::PromiseHolder() 405 | : owners_() 406 | , pendingTasks_() 407 | , state_(TaskState::kPending) 408 | , value_() 409 | #if PROMISE_MULTITHREAD 410 | , mutex_(std::make_shared()) 411 | #endif 412 | { 413 | } 414 | 415 | PromiseHolder::~PromiseHolder() { 416 | if (this->state_ == TaskState::kRejected) { 417 | static thread_local std::atomic s_inUncaughtExceptionHandler{false}; 418 | if(s_inUncaughtExceptionHandler) return; 419 | s_inUncaughtExceptionHandler = true; 420 | struct Releaser { 421 | Releaser(std::atomic *inUncaughtExceptionHandler) 422 | : inUncaughtExceptionHandler_(inUncaughtExceptionHandler) {} 423 | ~Releaser() { 424 | *inUncaughtExceptionHandler_ = false; 425 | } 426 | std::atomic *inUncaughtExceptionHandler_; 427 | } releaser(&s_inUncaughtExceptionHandler); 428 | 429 | PromiseHolder::onUncaughtException(this->value_); 430 | } 431 | } 432 | 433 | 434 | 435 | any *PromiseHolder::getUncaughtExceptionHandler() { 436 | static any onUncaughtException; 437 | return &onUncaughtException; 438 | } 439 | 440 | any *PromiseHolder::getDefaultUncaughtExceptionHandler() { 441 | static any defaultUncaughtExceptionHandler = [](Promise &d) { 442 | d.fail([](const std::runtime_error &err) { 443 | fprintf(stderr, "onUncaughtException in line %d, %s\n", __LINE__, err.what()); 444 | }).fail([]() { 445 | //go here for all other uncaught parameters. 446 | fprintf(stderr, "onUncaughtException in line %d\n", __LINE__); 447 | }); 448 | }; 449 | 450 | return &defaultUncaughtExceptionHandler; 451 | } 452 | 453 | void PromiseHolder::onUncaughtException(const any &arg) { 454 | any *onUncaughtException = getUncaughtExceptionHandler(); 455 | if (onUncaughtException == nullptr || onUncaughtException->empty()) { 456 | onUncaughtException = getDefaultUncaughtExceptionHandler(); 457 | } 458 | 459 | try { 460 | onUncaughtException->call(reject(arg)); 461 | } 462 | catch (...) { 463 | fprintf(stderr, "onUncaughtException in line %d\n", __LINE__); 464 | } 465 | } 466 | 467 | void PromiseHolder::handleUncaughtException(const any &onUncaughtException) { 468 | (*getUncaughtExceptionHandler()) = onUncaughtException; 469 | } 470 | 471 | #if PROMISE_MULTITHREAD 472 | std::shared_ptr SharedPromise::obtainLock() const { 473 | while (true) { 474 | std::shared_ptr mutex = this->promiseHolder_->mutex_; 475 | mutex->lock(); 476 | 477 | // pointer to mutex may be changed after locked, 478 | // in this case we should try to lock and test again 479 | if (mutex == this->promiseHolder_->mutex_) 480 | return mutex; 481 | mutex->unlock(); 482 | } 483 | return nullptr; 484 | } 485 | #endif 486 | 487 | Promise &Promise::then(const any &deferOrPromiseOrOnResolved) { 488 | if (deferOrPromiseOrOnResolved.type() == type_id()) { 489 | Defer &defer = deferOrPromiseOrOnResolved.cast(); 490 | Promise promise = defer.getPromise(); 491 | Promise &ret = then([defer](const any &arg) -> any { 492 | defer.resolve(arg); 493 | return nullptr; 494 | }, [defer](const any &arg) ->any { 495 | defer.reject(arg); 496 | return nullptr; 497 | }); 498 | 499 | promise.finally([=]() { 500 | ret.reject(); 501 | }); 502 | 503 | return ret; 504 | } 505 | else if (deferOrPromiseOrOnResolved.type() == type_id()) { 506 | DeferLoop &loop = deferOrPromiseOrOnResolved.cast(); 507 | Promise promise = loop.getPromise(); 508 | 509 | Promise &ret = then([loop](const any &arg) -> any { 510 | (void)arg; 511 | loop.doContinue(); 512 | return nullptr; 513 | }, [loop](const any &arg) ->any { 514 | loop.reject(arg); 515 | return nullptr; 516 | }); 517 | 518 | promise.finally([=]() { 519 | ret.reject(); 520 | }); 521 | 522 | return ret; 523 | } 524 | else if (deferOrPromiseOrOnResolved.type() == type_id()) { 525 | Promise &promise = deferOrPromiseOrOnResolved.cast(); 526 | 527 | std::shared_ptr task; 528 | { 529 | #if PROMISE_MULTITHREAD 530 | std::shared_ptr mutex0 = this->sharedPromise_->obtainLock(); 531 | std::lock_guard lock0(*mutex0, std::adopt_lock_t()); 532 | std::shared_ptr mutex1 = promise.sharedPromise_->obtainLock(); 533 | std::lock_guard lock1(*mutex1, std::adopt_lock_t()); 534 | #endif 535 | 536 | if (promise.sharedPromise_ && promise.sharedPromise_->promiseHolder_) { 537 | join(this->sharedPromise_->promiseHolder_, promise.sharedPromise_->promiseHolder_); 538 | if (this->sharedPromise_->promiseHolder_->pendingTasks_.size() > 0) { 539 | task = this->sharedPromise_->promiseHolder_->pendingTasks_.front(); 540 | } 541 | } 542 | } 543 | if(task) 544 | call(task); 545 | return *this; 546 | } 547 | else { 548 | return then(deferOrPromiseOrOnResolved, any()); 549 | } 550 | } 551 | 552 | Promise &Promise::then(const any &onResolved, const any &onRejected) { 553 | std::shared_ptr task; 554 | { 555 | #if PROMISE_MULTITHREAD 556 | std::shared_ptr mutex = this->sharedPromise_->obtainLock(); 557 | std::lock_guard lock(*mutex, std::adopt_lock_t()); 558 | #endif 559 | 560 | task = std::make_shared(Task { 561 | TaskState::kPending, 562 | sharedPromise_->promiseHolder_, 563 | onResolved, 564 | onRejected 565 | }); 566 | sharedPromise_->promiseHolder_->pendingTasks_.push_back(task); 567 | } 568 | call(task); 569 | return *this; 570 | } 571 | 572 | Promise &Promise::fail(const any &onRejected) { 573 | return then(any(), onRejected); 574 | } 575 | 576 | Promise &Promise::always(const any &onAlways) { 577 | return then(onAlways, onAlways); 578 | } 579 | 580 | Promise &Promise::finally(const any &onFinally) { 581 | return then([onFinally](const any &arg)->any { 582 | return newPromise([onFinally, arg](Defer &defer) { 583 | try { 584 | onFinally.call(arg); 585 | } 586 | catch (bad_any_cast &) {} 587 | defer.resolve(arg); 588 | }); 589 | }, [onFinally](const any &arg)->any { 590 | return newPromise([onFinally, arg](Defer &defer) { 591 | try { 592 | onFinally.call(arg); 593 | } 594 | catch (bad_any_cast &) {} 595 | defer.reject(arg); 596 | }); 597 | }); 598 | } 599 | 600 | 601 | void Promise::resolve(const any &arg) const { 602 | if (!this->sharedPromise_) return; 603 | std::shared_ptr task; 604 | { 605 | #if PROMISE_MULTITHREAD 606 | std::shared_ptr mutex = this->sharedPromise_->obtainLock(); 607 | std::lock_guard lock(*mutex, std::adopt_lock_t()); 608 | #endif 609 | 610 | std::list> &pendingTasks_ = this->sharedPromise_->promiseHolder_->pendingTasks_; 611 | if (pendingTasks_.size() > 0) { 612 | task = pendingTasks_.front(); 613 | } 614 | } 615 | 616 | if (task) { 617 | Defer defer(task); 618 | defer.resolve(arg); 619 | } 620 | } 621 | 622 | void Promise::reject(const any &arg) const { 623 | if (!this->sharedPromise_) return; 624 | std::shared_ptr task; 625 | { 626 | #if PROMISE_MULTITHREAD 627 | std::shared_ptr mutex = this->sharedPromise_->obtainLock(); 628 | std::lock_guard lock(*mutex, std::adopt_lock_t()); 629 | #endif 630 | 631 | std::list> &pendingTasks_ = this->sharedPromise_->promiseHolder_->pendingTasks_; 632 | if (pendingTasks_.size() > 0) { 633 | task = pendingTasks_.front(); 634 | } 635 | } 636 | 637 | if (task) { 638 | Defer defer(task); 639 | defer.reject(arg); 640 | } 641 | } 642 | 643 | void Promise::clear() { 644 | sharedPromise_.reset(); 645 | } 646 | 647 | Promise::operator bool() const { 648 | return sharedPromise_.operator bool(); 649 | } 650 | 651 | Promise newPromise(const std::function &run) { 652 | Promise promise; 653 | promise.sharedPromise_ = std::make_shared(); 654 | promise.sharedPromise_->promiseHolder_ = std::make_shared(); 655 | promise.sharedPromise_->promiseHolder_->owners_.push_back(promise.sharedPromise_); 656 | 657 | // return as is 658 | promise.then(any(), any()); 659 | std::shared_ptr &task = promise.sharedPromise_->promiseHolder_->pendingTasks_.front(); 660 | 661 | Defer defer(task); 662 | try { 663 | run(defer); 664 | } 665 | catch (...) { 666 | defer.reject(std::current_exception()); 667 | } 668 | 669 | return promise; 670 | } 671 | 672 | Promise newPromise() { 673 | Promise promise; 674 | promise.sharedPromise_ = std::make_shared(); 675 | promise.sharedPromise_->promiseHolder_ = std::make_shared(); 676 | promise.sharedPromise_->promiseHolder_->owners_.push_back(promise.sharedPromise_); 677 | 678 | // return as is 679 | promise.then(any(), any()); 680 | return promise; 681 | } 682 | 683 | Promise doWhile(const std::function &run) { 684 | 685 | return newPromise([run](Defer &defer) { 686 | DeferLoop loop(defer); 687 | run(loop); 688 | }).then([run](const any &arg) -> any { 689 | (void)arg; 690 | return doWhile(run); 691 | }, [](const any &arg) -> any { 692 | return newPromise([arg](Defer &defer) { 693 | //printf("arg. type = %s\n", arg.type().name()); 694 | 695 | bool isBreak = false; 696 | if (arg.type() == type_id>()) { 697 | std::vector &args = any_cast &>(arg); 698 | if (args.size() == 2 699 | && args.front().type() == type_id() 700 | && args.back().type() == type_id>()) { 701 | isBreak = true; 702 | defer.resolve(args.back()); 703 | } 704 | } 705 | 706 | if(!isBreak) { 707 | defer.reject(arg); 708 | } 709 | }); 710 | }); 711 | } 712 | 713 | #if 0 714 | Promise reject(const any &arg) { 715 | return newPromise([arg](Defer &defer) { defer.reject(arg); }); 716 | } 717 | 718 | Promise resolve(const any &arg) { 719 | return newPromise([arg](Defer &defer) { defer.resolve(arg); }); 720 | } 721 | #endif 722 | 723 | Promise all(const std::list &promise_list) { 724 | if (promise_list.size() == 0) { 725 | return resolve(); 726 | } 727 | 728 | std::shared_ptr finished = std::make_shared(0); 729 | std::shared_ptr size = std::make_shared(promise_list.size()); 730 | std::shared_ptr> retArr = std::make_shared>(); 731 | retArr->resize(*size); 732 | 733 | return newPromise([=](Defer &defer) { 734 | size_t index = 0; 735 | for (auto promise : promise_list) { 736 | promise.then([=](const any &arg) { 737 | (*retArr)[index] = arg; 738 | if (++(*finished) >= *size) { 739 | defer.resolve(*retArr); 740 | } 741 | }, [=](const any &arg) { 742 | defer.reject(arg); 743 | }); 744 | 745 | ++index; 746 | } 747 | }); 748 | } 749 | 750 | static Promise race(const std::list &promise_list, std::shared_ptr winner) { 751 | return newPromise([=](Defer &defer) { 752 | int index = 0; 753 | for (auto it = promise_list.begin(); it != promise_list.end(); ++it, ++index) { 754 | auto promise = *it; 755 | promise.then([=](const any &arg) { 756 | *winner = index; 757 | defer.resolve(arg); 758 | return arg; 759 | }, [=](const any &arg) { 760 | *winner = index; 761 | defer.reject(arg); 762 | return arg; 763 | }); 764 | } 765 | }); 766 | } 767 | 768 | Promise race(const std::list &promise_list) { 769 | std::shared_ptr winner = std::make_shared(-1); 770 | return race(promise_list, winner); 771 | } 772 | 773 | Promise raceAndReject(const std::list &promise_list) { 774 | std::shared_ptr winner = std::make_shared(-1); 775 | return race(promise_list, winner).finally([promise_list, winner] { 776 | int index = 0; 777 | for (auto it = promise_list.begin(); it != promise_list.end(); ++it, ++index) { 778 | if (index != *winner) { 779 | auto promise = *it; 780 | promise.reject(); 781 | } 782 | } 783 | }); 784 | } 785 | 786 | Promise raceAndResolve(const std::list &promise_list) { 787 | std::shared_ptr winner = std::make_shared(-1); 788 | return race(promise_list, winner).finally([promise_list, winner] { 789 | int index = 0; 790 | for (auto it = promise_list.begin(); it != promise_list.end(); ++it, ++index) { 791 | if (index != *winner) { 792 | auto promise = *it; 793 | promise.resolve(); 794 | } 795 | } 796 | }); 797 | } 798 | 799 | 800 | } // namespace promise 801 | 802 | #endif 803 | -------------------------------------------------------------------------------- /src/promise.cpp: -------------------------------------------------------------------------------- 1 | #include "promise-cpp/promise.hpp" 2 | 3 | #ifndef PROMISE_HEADONLY 4 | #include "promise-cpp/promise_inl.hpp" 5 | #endif 6 | 7 | --------------------------------------------------------------------------------