├── .editorconfig ├── Android.mk ├── LICENSE ├── README.md ├── bin └── MedianFilter.exe ├── build-android-on-windows.bat ├── build-android.bat ├── example ├── log_print.h ├── task_async.cpp ├── task_conflict.cpp ├── task_exception.cpp ├── task_link.cpp ├── task_marshal.cpp ├── task_optimal_move.cpp ├── task_semaphore.h ├── task_thread_safe.cpp ├── task_when_all.cpp ├── task_when_any.cpp └── threadpool_context.h ├── include ├── task.h ├── task_cbnode.inl ├── task_context.h ├── task_detail.inl ├── task_exception.inl ├── task_executor.inl ├── task_invoke_traits.h ├── task_node.inl ├── task_node_android.inl ├── task_node_ios.inl ├── task_node_win32.inl ├── task_utils.inl ├── task_when_all.h ├── task_when_all.inl ├── task_when_allv.inl ├── task_when_any.h ├── task_when_any.inl ├── task_when_anys.inl ├── task_when_anyv.inl └── task_when_node.inl ├── jni ├── Android.mk ├── Application.mk └── libtask.cpp ├── libtask.h ├── mac_proj ├── libtask.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ └── tearshark.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── libtask │ └── main.cpp └── vs_proj ├── libtask.cpp ├── libtask.sln ├── libtask.vcxproj ├── libtask.vcxproj.filters └── targetver.h /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # all files 5 | [*] 6 | indent_style = tab 7 | indent_size = 4 -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := lib_shark_task_static 6 | LOCAL_MODULE_FILENAME := lib_shark_task 7 | LOCAL_SRC_FILES := libs/Android/$(TARGET_ARCH_ABI)/lib_shark_task.a 8 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) 9 | 10 | include $(PREBUILT_STATIC_LIBRARY) 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 TearShark 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lib_shark_task(st) 2 | ============= 3 | 基于C++14语法的任务链 4 | 5 | 6 | # 主要特性 7 | 8 | - 实现一个链式调用的任务链功能,来减少回调地狱的问题; 9 | - 通过std: :future, 获取最后一个调用的结果。如果中途发生了异常,则std: :future: :get()会抛出异常; 10 | - 支持将后续调用绑定到特定的“执行环境”上去调用,从而精确控制执行的时机和线程; 11 | - 支持将一个回调函数包装成一个任务节点。回调参数即任务节点返回值; 12 | - 需求环境: 13 | - 需要也只需要C++14; 14 | - Windows下,使用了VS的_Promise实现,故只支持VS2015/VS2017; 15 | - Android下,由于NDK附带的GCC版本只到4.9,不支持C++14。故需要clang 3.6; 16 | 17 | # 接口说明 18 | 19 | ## make_task 20 | ```C++ 21 | template 22 | task<> make_task(_Ftype && fn); 23 | //构建任务链的首节点。 24 | //_Ftype是一个函数(对象) 25 | 26 | task<> make_task(); 27 | //构建任务链的首节点,这个首节点什么事情都不做 28 | 29 | template 30 | task<> make_task(_Context & ctx, _Ftype && fn) 31 | //构建一个任务链,首节点什么事情都不做,第二个节点将fn放入ctx里运行。从而看起来将首任务投入到ctx里运行。 32 | ``` 33 | 34 | ## marshal_task 35 | ```C++ 36 | template 37 | task<> marshal_task(_Fcb&& fn, _Args&&... args) 38 | //构建任务链的首节点。 39 | //_Fcb是一个基于回调的函数(对象)。 40 | //_Fcb的返回值不再关心。并且_Fcb内部要么抛异常,要么就必须调用回调函数,并且不关心回调函数的返回值 41 | ``` 42 | 43 | ## task 44 | ```C++ 45 | template 46 | struct task 47 | //任务调用链。通过make_task/marshal_task生成,然后不停的then/marshal生成调用链。 48 | ``` 49 | 50 | ## then 51 | ```C++ 52 | template 53 | task<> task::then(_Ftype && fn) 54 | //在上一个链节点后继续运行fn 55 | 56 | template 57 | task<> task::then(_Context & ctx, _Ftype && fn) 58 | //在上一个链节点后,投递到指定的ctx里继续运行fn 59 | 60 | //注: 61 | //_Ftype的入参,是上一个调用链节点的返回值。 62 | //如果上一个调用链节点的返回值是std::tuple<...>类型,则fn的入参,是tuple解包后的参数。目前要求参数个数/类型精确匹配。 63 | ``` 64 | 65 | ## marshal 66 | ```C++ 67 | template 68 | task<> task::marshal(_Fcb&& fn, _Types&&... args) 69 | //在上一个链节点后继续运行fn。 70 | 71 | template 72 | task<> task::marshal(_Context & ctx, _Fcb&& fn, _Types&&... args) 73 | //在上一个链节点后,投递到指定的ctx里继续运行fn 74 | 75 | //注: 76 | //_Fcb是一个基于回调的函数(对象)。其回调函数参数使用内部构造的兼容对象,其他参数是上一个调用链节点的返回值。 77 | //回调函数的入参,作为本任务节点的返回值 78 | ``` 79 | 80 | ## when_all 81 | ```C++ 82 | #include "task_when_all.h" 83 | 84 | template 85 | task<> when_all(_Task& tfirst, _TaskRest&... rest) 86 | //等待多个不同类型的任务完成。 87 | //多个任务的结果,放在一个拼合的tuple<>里。这个拼合的tuple<>,将作为下一个任务节点的入参,或者最后一个节点future>的结果。 88 | 89 | template 90 | task<> when_all(_Iter begin, _Iter end) 91 | //等待一组相同类型的任务完成. 92 | //多个任务的结果类型肯定是一致的,数量运行时才能确定。故结果放在vector<>里。 93 | //如果每个任务返回的是单值,则为vector;如果返回的多值,则为vector>。 94 | ``` 95 | 96 | ## when_any 97 | ```C++ 98 | #include "task_when_any.h" 99 | 100 | template 101 | task<> when_any(_Task& tfirst, _TaskRest&... rest) 102 | //等待多个不同类型的任务之一完成。 103 | 104 | template 105 | task<> when_any(_Iter begin, _Iter end) 106 | //等待多个不同类型的任务之一完成. 107 | //多个任务的结果类型肯定是一致的,故返回值的第一个参数指示是哪一个任务完成,后续参数是结果 108 | ``` 109 | 110 | ## get_future 111 | ```C++ 112 | std::future<> task::get_future() 113 | //返回最后一个链节点的返回值 114 | ``` 115 | 116 | ## get_executor 117 | ```C++ 118 | executor_sptr task::get_executor() 119 | //返回task_executor<_FirstNode>,以便于在指定的task_context里运行 120 | 121 | template 122 | void task::operator()(_Args&&... args) const 123 | //执行整个调用链。args...参数必须跟第一个链节点需要的参数匹配 124 | ``` 125 | 126 | ## task_node 127 | ```C++ 128 | template 129 | struct task_node 130 | //包装任务链节点。通过内嵌promise实现,提供get_future()功能 131 | ``` 132 | 133 | ## task_context 134 | ```C++ 135 | concept task_context 136 | { 137 | void add(const executor_sptr & runner) const; 138 | }; 139 | //代表任务运行环境/线程的虚类,需要外部实现某种运行方案 140 | 141 | extern immediate_task_context imm_context; 142 | //在当前线程,立即运行task_context的实现。注意,需要在某个地方定义imm_context。 143 | 144 | extern async_task_context async_context; 145 | //使用std::thread运行task_context的实现。注意,需要在某个地方定义async_context。 146 | ``` 147 | 148 | ## executor 149 | ```C++ 150 | template 151 | struct task_executor : public executor 152 | //将task包装成执行器,以便于放入指定的task_context里运行 153 | ``` 154 | 155 | -------------------------------------------------------------------------------- /bin/MedianFilter.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tearshark/libst/1c8251856957590be5ad063be7f05f0f5c5430a1/bin/MedianFilter.exe -------------------------------------------------------------------------------- /build-android-on-windows.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call %ROOT_DIR%\build-android.bat 4 | 5 | SET ROOT_DIR=%~dp0 6 | 7 | mkdir %ROOT_DIR%\libs\Android\armeabi 8 | xcopy %ROOT_DIR%\obj\local\armeabi\*.a %ROOT_DIR%\libs\Android\armeabi\ /Y /I /Q 9 | 10 | mkdir %ROOT_DIR%\libs\Android\armeabi-v7a 11 | xcopy %ROOT_DIR%\obj\local\armeabi-v7a\*.a %ROOT_DIR%\libs\Android\armeabi-v7a\ /Y /I /Q 12 | 13 | mkdir %ROOT_DIR%\libs\Android\arm64-v8a 14 | xcopy %ROOT_DIR%\obj\local\arm64-v8a\*.a %ROOT_DIR%\libs\Android\arm64-v8a\ /Y /I /Q 15 | 16 | mkdir %ROOT_DIR%\libs\Android\x86 17 | xcopy %ROOT_DIR%\obj\local\x86\*.a %ROOT_DIR%\libs\Android\x86\ /Y /I /Q 18 | 19 | mkdir %ROOT_DIR%\libs\Android\x86_64 20 | xcopy %ROOT_DIR%\obj\local\x86_64\*.a %ROOT_DIR%\libs\Android\x86_64\ /Y /I /Q 21 | 22 | mkdir %ROOT_DIR%\libs\Android\mips 23 | xcopy %ROOT_DIR%\obj\local\mips\*.a %ROOT_DIR%\libs\Android\mips\ /Y /I /Q 24 | 25 | mkdir %ROOT_DIR%\libs\Android\mips64 26 | xcopy %ROOT_DIR%\obj\local\mips64\*.a %ROOT_DIR%\libs\Android\mips64\ /Y /I /Q 27 | -------------------------------------------------------------------------------- /build-android.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tearshark/libst/1c8251856957590be5ad063be7f05f0f5c5430a1/build-android.bat -------------------------------------------------------------------------------- /example/log_print.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #ifdef _WIN32 5 | #include 6 | inline void __log_print_impl(std::string & str) 7 | { 8 | str += "\r\n"; 9 | ::OutputDebugStringA(str.c_str()); 10 | } 11 | #elif __APPLE__ 12 | inline void __log_print_impl(std::string & str) 13 | { 14 | printf("%s\n", str.c_str()); 15 | } 16 | #elif __ANDROID__ 17 | #include 18 | inline void __log_print_impl(std::string & str) 19 | { 20 | __android_log_write(ANDROID_LOG_INFO, "libtask", str.c_str()); 21 | } 22 | #else 23 | #include 24 | inline void __log_print_impl(std::string & str) 25 | { 26 | std::cout << str << std::endl; 27 | } 28 | #endif 29 | 30 | #ifdef _MSC_VER 31 | #pragma warning(disable : 4996) 32 | #endif 33 | 34 | #include 35 | 36 | namespace lib_shark_task 37 | { 38 | namespace detail 39 | { 40 | template 41 | inline std::string to_string(_Ty&& _value) 42 | { 43 | return std::string{}; 44 | } 45 | inline std::string to_string(const std::string & _value) 46 | { 47 | return _value; 48 | } 49 | inline std::string to_string(std::string & _value) 50 | { 51 | return _value; 52 | } 53 | inline std::string to_string(std::string && _value) 54 | { 55 | return std::forward(_value); 56 | } 57 | 58 | inline std::string to_string(void * _value) 59 | { 60 | char szbuf[128]; 61 | sprintf(szbuf, "0x%p", _value); 62 | return std::string(szbuf); 63 | } 64 | 65 | inline std::string to_string(int _value) 66 | { 67 | char szbuf[128]; 68 | sprintf(szbuf, "%d", _value); 69 | return std::string(szbuf); 70 | } 71 | inline std::string to_string(long _value) 72 | { 73 | char szbuf[128]; 74 | sprintf(szbuf, "%ld", _value); 75 | return std::string(szbuf); 76 | } 77 | inline std::string to_string(long long _value) 78 | { 79 | char szbuf[128]; 80 | sprintf(szbuf, "%lld", _value); 81 | return std::string(szbuf); 82 | } 83 | inline std::string to_string(unsigned int _value) 84 | { 85 | char szbuf[128]; 86 | sprintf(szbuf, "%u", _value); 87 | return std::string(szbuf); 88 | } 89 | inline std::string to_string(unsigned long _value) 90 | { 91 | char szbuf[128]; 92 | sprintf(szbuf, "%lu", _value); 93 | return std::string(szbuf); 94 | } 95 | inline std::string to_string(unsigned long long _value) 96 | { 97 | char szbuf[128]; 98 | sprintf(szbuf, "%llu", _value); 99 | return std::string(szbuf); 100 | } 101 | inline std::string to_string(const char * _value) 102 | { 103 | return std::string(_value); 104 | } 105 | inline std::string to_string(std::thread::id tid) 106 | { 107 | std::stringstream ss; 108 | ss << tid; 109 | return ss.str(); 110 | } 111 | 112 | 113 | inline void log_concat(std::string & str) 114 | { 115 | } 116 | template 117 | inline void log_concat(std::string & str, _Ty&& val, _Rest&&... rest) 118 | { 119 | str += to_string(val); 120 | log_concat(str, std::forward<_Rest>(rest)...); 121 | } 122 | } 123 | } 124 | 125 | #ifdef _MSC_VER 126 | #pragma warning(default : 4996) 127 | #endif 128 | 129 | template 130 | inline void log_print(_Types&&... args) 131 | { 132 | std::string str; 133 | lib_shark_task::detail::log_concat(str, std::forward<_Types>(args)...); 134 | __log_print_impl(str); 135 | } 136 | -------------------------------------------------------------------------------- /example/task_async.cpp: -------------------------------------------------------------------------------- 1 | //测试将任务节点分配到指定的线程池环境里运行 2 | #include "task.h" 3 | #include "task_context.h" 4 | #include "threadpool_context.h" 5 | #include "log_print.h" 6 | 7 | using namespace std::literals; 8 | 9 | void test_task_async() 10 | { 11 | using namespace st; 12 | 13 | threadpool_context pool_context{ 1 }; 14 | 15 | auto t = make_task([] 16 | { 17 | log_print("delay run ", std::this_thread::get_id()); 18 | return std::this_thread::get_id(); 19 | }).then(pool_context, [](std::thread::id tid) 20 | { 21 | std::this_thread::sleep_for(5s); 22 | log_print("run in another thread ", std::this_thread::get_id(), " ", tid == std::this_thread::get_id()); 23 | return 2; 24 | }).then(imm_context, [](int result) 25 | { 26 | log_print("run inplace thread ", std::this_thread::get_id(), " ", result); 27 | return result * 2; 28 | }); 29 | 30 | t(); 31 | 32 | #if 1 33 | auto f = t.get_future(); 34 | log_print("end value is ", f.get()); 35 | #else 36 | log_print("press any key to continue."); 37 | _getch(); 38 | #endif 39 | } 40 | -------------------------------------------------------------------------------- /example/task_conflict.cpp: -------------------------------------------------------------------------------- 1 | //测试get_future() 和 then() 之间的冲突导致运行时的异常 2 | #include "task.h" 3 | #include "task_context.h" 4 | #include "log_print.h" 5 | 6 | using namespace std::literals; 7 | 8 | void test_task_conflict() 9 | { 10 | using namespace st; 11 | 12 | auto t = make_task([] 13 | { 14 | log_print("delay run ", std::this_thread::get_id()); 15 | return 1; 16 | }); 17 | 18 | auto f = t.get_future(); //随后不能再then()了。 19 | 20 | //继续then(),导致异常 21 | t.then(async_context, [](int val) 22 | { 23 | std::this_thread::sleep_for(5s); 24 | log_print("run in another thread ", std::this_thread::get_id()); 25 | return val; 26 | }); 27 | 28 | t(); 29 | 30 | #if 1 31 | log_print("end value is ", f.get()); 32 | #else 33 | //也可以不取future,这样等待任务自然链自然结束 34 | log_print("press any key to continue."); 35 | _getch(); 36 | #endif 37 | } 38 | -------------------------------------------------------------------------------- /example/task_exception.cpp: -------------------------------------------------------------------------------- 1 | //测试任务链将异常传递给future,在future::get()时获得任务链执行过程中的异常 2 | #include "task.h" 3 | #include "task_context.h" 4 | #include "threadpool_context.h" 5 | #include "log_print.h" 6 | 7 | using namespace std::literals; 8 | 9 | namespace std 10 | { 11 | class divide_0_exception : public exception 12 | { 13 | public: 14 | virtual const char* what() const 15 | { 16 | return "divide by zero"; 17 | } 18 | }; 19 | } 20 | 21 | void test_task_exception() 22 | { 23 | using namespace st; 24 | 25 | auto t = make_task([](int val) 26 | { 27 | log_print("delay run ", std::this_thread::get_id()); 28 | if (val == 0) throw std::divide_0_exception(); 29 | return 10 / val; 30 | }).then(async_context, [](int val) 31 | { 32 | std::this_thread::sleep_for(1s); 33 | log_print("run in another thread ", std::this_thread::get_id()); 34 | if (val == 0) throw std::divide_0_exception(); 35 | return 10 / val; 36 | }); 37 | 38 | t(0); //5 : 正常; 20 : then()后的代码出错; 0 : make_task()后的代码出错 39 | 40 | auto f = t.get_future(); 41 | try 42 | { 43 | log_print("end value is ", f.get()); 44 | } 45 | catch (std::exception & ex) 46 | { 47 | log_print(ex.what()); 48 | } 49 | catch (...) 50 | { 51 | log_print("had some exception!"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/task_link.cpp: -------------------------------------------------------------------------------- 1 | //测试基本的任务链。每次then后返回一个新的task对象 2 | #include "task.h" 3 | #include "task_context.h" 4 | #include "log_print.h" 5 | 6 | using namespace std::literals; 7 | 8 | int fn_first(int val) 9 | { 10 | log_print("first"); 11 | return val * 3; 12 | } 13 | std::string fn_second(int val) 14 | { 15 | log_print("second ", val); 16 | return "abcde"s; 17 | } 18 | std::tuple fn_third(std::string str) 19 | { 20 | log_print("third ", str); 21 | return std::make_tuple(2, "cdefg"s); 22 | } 23 | void fn_four(int val, std::string str) 24 | { 25 | log_print("four ", val, " ", str); 26 | } 27 | 28 | void test_task_link() 29 | { 30 | using namespace st; 31 | 32 | task, task_node> t1 = make_task(std::bind(fn_first, 3)); 33 | task, task_node> t2 = t1.then(fn_second); 34 | task, std::string>, task_node> t3 = t2.then(imm_context, fn_third); 35 | task, task_node> t4 = t3.then(imm_context, fn_four); 36 | 37 | auto t = t4.then([] 38 | { 39 | log_print("lambda "); 40 | return 0; 41 | }); 42 | 43 | auto f = t.get_future(); 44 | imm_context.add(t.get_executor()); 45 | 46 | log_print("end value is ", f.get()); 47 | } 48 | -------------------------------------------------------------------------------- /example/task_marshal.cpp: -------------------------------------------------------------------------------- 1 | //测试将回调接口的函数,包装成任务链节点 2 | #include "task.h" 3 | #include "task_context.h" 4 | #include "threadpool_context.h" 5 | #include "log_print.h" 6 | 7 | using namespace std::literals; 8 | 9 | //foo_callback 需要满足以下要求: 10 | //一、返回值没有意义 11 | //二、cb必然会被调用,且只调用一次。除非内部抛了异常 12 | //三、cb没有返回值 13 | void foo_callback(int val, const std::function & cb, std::string str) 14 | { 15 | log_print("foo_callback: ", val, " @", std::this_thread::get_id()); 16 | cb(val * 2, str); 17 | } 18 | 19 | void test_task_marshal() 20 | { 21 | using namespace st; 22 | 23 | threadpool_context pool_context{ 1 }; 24 | 25 | auto t = marshal_task(&foo_callback, 1, st::_cb, "first run "s) 26 | .marshal(pool_context, &foo_callback, std::placeholders::_2, st::_cb, std::placeholders::_3) 27 | .then(async_context, [](int val, std::string str) 28 | { 29 | log_print(str, val, " @", std::this_thread::get_id()); 30 | return val * 2; 31 | }) 32 | ; 33 | 34 | t(); //开始运行任务链 35 | 36 | auto f = t.get_future(); 37 | auto val = f.get(); 38 | log_print("end value is ", val, " @", std::this_thread::get_id()); 39 | } 40 | -------------------------------------------------------------------------------- /example/task_optimal_move.cpp: -------------------------------------------------------------------------------- 1 | //测试任务链内部数据在传递过程中,尽量采用了移动语义(move),而不是拷贝语义 2 | #include "task.h" 3 | #include "task_context.h" 4 | #include "log_print.h" 5 | 6 | using namespace std::literals; 7 | 8 | struct print_move 9 | { 10 | int value; 11 | 12 | print_move() :value(0) 13 | { 14 | log_print(this, " default"); 15 | } 16 | print_move(int val) :value(val) 17 | { 18 | log_print(this, " init ", val); 19 | } 20 | print_move(const print_move & _Right) :value(_Right.value) 21 | { 22 | log_print(this, " copy from ", &_Right); 23 | } 24 | print_move & operator = (const print_move & _Right) 25 | { 26 | if (this != &_Right) 27 | { 28 | value = _Right.value; 29 | log_print(this, " copy assign ", &_Right); 30 | } 31 | return *this; 32 | } 33 | 34 | print_move(print_move && _Right) :value(_Right.value) 35 | { 36 | _Right.value = 0; 37 | log_print(this, " move from ", &_Right); 38 | } 39 | print_move & operator = (print_move && _Right) 40 | { 41 | if (this != &_Right) 42 | { 43 | value = _Right.value; 44 | _Right.value = 0; 45 | log_print(this, " move assign ", &_Right); 46 | } 47 | return *this; 48 | } 49 | }; 50 | 51 | void test_task_optimal_move() 52 | { 53 | using namespace st; 54 | 55 | auto t = make_task([] 56 | { 57 | log_print("delay run ", std::this_thread::get_id()); 58 | return print_move{ 1 }; 59 | }).then(async_context, [](print_move val) 60 | { 61 | std::this_thread::sleep_for(5s); 62 | log_print("run in another thread ", std::this_thread::get_id()); 63 | return val; 64 | }); 65 | 66 | t(); 67 | 68 | #if 1 69 | auto f = t.get_future(); 70 | log_print("end value is ", f.get()); 71 | #else 72 | //也可以不取future,这样等待任务自然链自然结束 73 | log_print("press any key to continue."); 74 | _getch(); 75 | #endif 76 | } 77 | -------------------------------------------------------------------------------- /example/task_semaphore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #if defined(_WIN32) 7 | #include 8 | #elif defined(ANDROID) 9 | #include 10 | #elif defined(__MACH__) 11 | #include 12 | #elif defined(__unix__) 13 | #include 14 | #endif 15 | 16 | namespace std 17 | { 18 | 19 | #if defined(_WIN32) 20 | 21 | class shared_semaphore 22 | { 23 | protected: 24 | void* m_hSema; 25 | public: 26 | operator bool() const 27 | { 28 | return m_hSema != nullptr; 29 | } 30 | 31 | bool acquire() 32 | { 33 | return WaitForSingleObject(m_hSema, INFINITE) == WAIT_OBJECT_0; 34 | } 35 | 36 | bool try_acquire() 37 | { 38 | return WaitForSingleObject(m_hSema, 0) == WAIT_OBJECT_0; 39 | } 40 | 41 | bool try_acquire_for(std::chrono::microseconds dt) 42 | { 43 | return WaitForSingleObject(m_hSema, (unsigned long)(dt.count() / 1000)) == WAIT_OBJECT_0; 44 | } 45 | 46 | void release(int count = 1) 47 | { 48 | ReleaseSemaphore(m_hSema, count, nullptr); 49 | } 50 | 51 | void * native_handle() const 52 | { 53 | return m_hSema; 54 | } 55 | }; 56 | 57 | class semaphore : public shared_semaphore 58 | { 59 | private: 60 | semaphore(const semaphore& other) = delete; 61 | semaphore& operator=(const semaphore& other) = delete; 62 | public: 63 | semaphore(int initialCount = 0, int maxLong = (std::numeric_limits::max)()) 64 | { 65 | assert(initialCount >= 0); 66 | //const long maxLong = 0x7fffffff; 67 | m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr); 68 | } 69 | semaphore(semaphore && _Right) noexcept 70 | { 71 | m_hSema = _Right.m_hSema; 72 | _Right.m_hSema = nullptr; 73 | } 74 | semaphore & operator = (semaphore && _Right) noexcept 75 | { 76 | if (this != &_Right) 77 | { 78 | m_hSema = _Right.m_hSema; 79 | _Right.m_hSema = nullptr; 80 | } 81 | return *this; 82 | } 83 | void swap(semaphore & _Right) 84 | { 85 | void* hSema = m_hSema; 86 | m_hSema = _Right.m_hSema; 87 | _Right.m_hSema = hSema; 88 | } 89 | 90 | ~semaphore() 91 | { 92 | if (m_hSema) 93 | CloseHandle(m_hSema); 94 | } 95 | }; 96 | 97 | #elif defined(__MACH__) 98 | 99 | //--------------------------------------------------------- 100 | // Semaphore (Apple iOS and OSX) 101 | // Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html 102 | //--------------------------------------------------------- 103 | class shared_semaphore 104 | { 105 | protected: 106 | semaphore_t m_hSema; 107 | public: 108 | operator bool() const 109 | { 110 | return m_hSema != nullptr; 111 | } 112 | 113 | void acquire() 114 | { 115 | semaphore_wait(m_hSema); 116 | } 117 | 118 | bool try_acquire() 119 | { 120 | return try_acquire_for(0); 121 | } 122 | 123 | bool try_acquire_for(std::chrono::microseconds dt) 124 | { 125 | mach_timespec_t ts; 126 | 127 | ts.tv_sec = static_cast(dt.count() / 1000000); 128 | ts.tv_nsec = (dt.count() % 1000000) * 1000; 129 | 130 | // added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html 131 | kern_return_t rc = semaphore_timedwait(m_hSema, ts); 132 | return rc != KERN_OPERATION_TIMED_OUT && rc != KERN_ABORTED; 133 | } 134 | 135 | void release() 136 | { 137 | semaphore_signal(m_hSema); 138 | } 139 | 140 | void release(int count) 141 | { 142 | while (count-- > 0) 143 | { 144 | semaphore_signal(m_hSema); 145 | } 146 | } 147 | 148 | semaphore_t native_handle() const 149 | { 150 | return m_hSema; 151 | } 152 | }; 153 | 154 | class semaphore : public shared_semaphore 155 | { 156 | private: 157 | semaphore(const semaphore& other) = delete; 158 | semaphore& operator=(const semaphore& other) = delete; 159 | public: 160 | semaphore(int initialCount = 0) 161 | { 162 | assert(initialCount >= 0); 163 | semaphore_create(mach_task_self(), &m_hSema, SYNC_POLICY_FIFO, initialCount); 164 | } 165 | semaphore(semaphore && _Right) noexcept 166 | : m_hSema(_Right.m_hSema) 167 | { 168 | _Right.m_hSema = nullptr; 169 | } 170 | semaphore & operator = (semaphore && _Right) noexcept 171 | { 172 | if (this != &_Right) 173 | { 174 | m_hSema = _Right.m_hSema; 175 | _Right.m_hSema = nullptr; 176 | } 177 | return *this; 178 | } 179 | void swap(semaphore & _Right) 180 | { 181 | semaphore_t* hSema = m_hSema; 182 | m_hSema = _Right.m_hSema; 183 | _Right.m_hSema = hSema; 184 | } 185 | 186 | ~semaphore() 187 | { 188 | semaphore_destroy(mach_task_self(), m_hSema); 189 | } 190 | }; 191 | 192 | #elif defined(ANDROID) 193 | 194 | //--------------------------------------------------------- 195 | // Semaphore (Android) 196 | //--------------------------------------------------------- 197 | 198 | class shared_semaphore 199 | { 200 | protected: 201 | sem_t * m_hSema; 202 | public: 203 | operator bool() const 204 | { 205 | return m_hSema != nullptr; 206 | } 207 | 208 | void acquire() 209 | { 210 | // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error 211 | int rc; 212 | do { 213 | rc = sem_wait(m_hSema); 214 | } while (rc == -1 && errno == EINTR); 215 | } 216 | 217 | bool try_acquire() 218 | { 219 | int rc; 220 | do { 221 | rc = sem_trywait(m_hSema); 222 | } while (rc == -1 && errno == EINTR); 223 | return !(rc == -1 && errno == EAGAIN); 224 | } 225 | 226 | bool try_acquire_for(std::chrono::microseconds dt) 227 | { 228 | struct timespec ts; 229 | const int usecs_in_1_sec = 1000000; 230 | const int nsecs_in_1_sec = 1000000000; 231 | clock_gettime(CLOCK_REALTIME, &ts); 232 | 233 | ts.tv_sec += dt.count() / usecs_in_1_sec; 234 | ts.tv_nsec += (dt.count() % usecs_in_1_sec) * 1000; 235 | 236 | // sem_timedwait bombs if you have more than 1e9 in tv_nsec 237 | // so we have to clean things up before passing it in 238 | if (ts.tv_nsec >= nsecs_in_1_sec) 239 | { 240 | ts.tv_nsec -= nsecs_in_1_sec; 241 | ++ts.tv_sec; 242 | } 243 | 244 | int rc; 245 | do 246 | { 247 | rc = sem_timedwait(m_hSema, &ts); 248 | } while (rc == -1 && errno == EINTR); 249 | return !(rc == -1 && errno == ETIMEDOUT); 250 | } 251 | 252 | void release() 253 | { 254 | sem_post(m_hSema); 255 | } 256 | 257 | void release(int count) 258 | { 259 | while (count-- > 0) 260 | { 261 | sem_post(m_hSema); 262 | } 263 | } 264 | 265 | sem_t * native_handle() const 266 | { 267 | return m_hSema; 268 | } 269 | }; 270 | 271 | class semaphore : public shared_semaphore 272 | { 273 | private: 274 | semaphore(const semaphore& other) = delete; 275 | semaphore& operator=(const semaphore& other) = delete; 276 | public: 277 | semaphore(int initialCount = 0) 278 | { 279 | assert(initialCount >= 0); 280 | m_hSema = new sem_t; 281 | sem_init(m_hSema, 0, initialCount); 282 | } 283 | semaphore(semaphore && _Right) noexcept 284 | { 285 | m_hSema = _Right.m_hSema; 286 | _Right.m_hSema = nullptr; 287 | } 288 | semaphore & operator = (semaphore && _Right) noexcept 289 | { 290 | if (this != &_Right) 291 | { 292 | m_hSema = _Right.m_hSema; 293 | _Right.m_hSema = nullptr; 294 | } 295 | return *this; 296 | } 297 | void swap(semaphore & _Right) 298 | { 299 | sem_t * hSema = m_hSema; 300 | m_hSema = _Right.m_hSema; 301 | _Right.m_hSema = hSema; 302 | } 303 | 304 | ~semaphore() 305 | { 306 | if (m_hSema != nullptr) 307 | { 308 | sem_destroy(m_hSema); 309 | delete m_hSema; 310 | } 311 | } 312 | }; 313 | 314 | #elif defined(__unix__) 315 | 316 | //--------------------------------------------------------- 317 | // Semaphore (POSIX, Linux) 318 | //--------------------------------------------------------- 319 | 320 | class shared_semaphore 321 | { 322 | protected: 323 | sem_t * m_hSema; 324 | public: 325 | operator bool() const 326 | { 327 | return m_hSema != nullptr; 328 | } 329 | 330 | void acquire() 331 | { 332 | // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error 333 | int rc; 334 | do { 335 | rc = sem_wait(m_hSema); 336 | } while (rc == -1 && errno == EINTR); 337 | } 338 | 339 | bool try_acquire() 340 | { 341 | int rc; 342 | do { 343 | rc = sem_trywait(m_hSema); 344 | } while (rc == -1 && errno == EINTR); 345 | return !(rc == -1 && errno == EAGAIN); 346 | } 347 | 348 | bool try_acquire_for(std::chrono::microseconds dt) 349 | { 350 | struct timespec ts; 351 | const int usecs_in_1_sec = 1000000; 352 | const int nsecs_in_1_sec = 1000000000; 353 | clock_gettime(CLOCK_REALTIME, &ts); 354 | 355 | ts.tv_sec += dt.count() / usecs_in_1_sec; 356 | ts.tv_nsec += (dt.count() % usecs_in_1_sec) * 1000; 357 | 358 | // sem_timedwait bombs if you have more than 1e9 in tv_nsec 359 | // so we have to clean things up before passing it in 360 | if (ts.tv_nsec >= nsecs_in_1_sec) 361 | { 362 | ts.tv_nsec -= nsecs_in_1_sec; 363 | ++ts.tv_sec; 364 | } 365 | 366 | int rc; 367 | do 368 | { 369 | rc = sem_timedwait(m_hSema, &ts); 370 | } while (rc == -1 && errno == EINTR); 371 | return !(rc == -1 && errno == ETIMEDOUT); 372 | } 373 | 374 | void release() 375 | { 376 | sem_post(m_hSema); 377 | } 378 | 379 | void release(int count) 380 | { 381 | while (count-- > 0) 382 | { 383 | sem_post(m_hSema); 384 | } 385 | } 386 | 387 | sem_t * native_handle() const 388 | { 389 | return m_hSema; 390 | } 391 | }; 392 | 393 | class semaphore : public shared_semaphore 394 | { 395 | private: 396 | semaphore(const semaphore& other) = delete; 397 | semaphore& operator=(const semaphore& other) = delete; 398 | public: 399 | semaphore(int initialCount = 0) 400 | { 401 | assert(initialCount >= 0); 402 | m_hSema = new sem_t; 403 | sem_init(m_hSema, 0, initialCount); 404 | } 405 | semaphore(semaphore && _Right) noexcept 406 | { 407 | m_hSema = _Right.m_hSema; 408 | _Right.m_hSema = nullptr; 409 | } 410 | semaphore & operator = (semaphore && _Right) noexcept 411 | { 412 | if (this != &_Right) 413 | { 414 | m_hSema = _Right.m_hSema; 415 | _Right.m_hSema = nullptr; 416 | } 417 | return *this; 418 | } 419 | void swap(semaphore & _Right) 420 | { 421 | sem_t * hSema = m_hSema; 422 | m_hSema = _Right.m_hSema; 423 | _Right.m_hSema = hSema; 424 | } 425 | 426 | ~semaphore() 427 | { 428 | if (m_hSema != nullptr) 429 | { 430 | sem_destroy(m_hSema); 431 | delete m_hSema; 432 | } 433 | } 434 | }; 435 | 436 | #else 437 | #error Unsupported platform! (No semaphore wrapper available) 438 | #endif 439 | 440 | inline void swap(semaphore & _Left, semaphore & _Right) 441 | { 442 | _Left.swap(_Right); 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /example/task_thread_safe.cpp: -------------------------------------------------------------------------------- 1 | //测试任务链节点之间的数据传递和执行是线程安全的 2 | #include "task.h" 3 | #include "log_print.h" 4 | 5 | void test_task_thread_safe() 6 | { 7 | //TODO 8 | } 9 | -------------------------------------------------------------------------------- /example/task_when_all.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "libtask.h" 4 | #include "log_print.h" 5 | 6 | using namespace std::literals; 7 | 8 | //Windows : MSVC OK 9 | //Android : failure 10 | void test_task_when_all_1() 11 | { 12 | using namespace st; 13 | 14 | auto t1 = make_task([]() 15 | { 16 | log_print("first task."); 17 | return std::tuple{ 1, "abc"s }; 18 | }); 19 | auto t2 = make_task([] 20 | { 21 | log_print("second task."); 22 | }); 23 | auto t3 = make_task([] 24 | { 25 | log_print("third task."); 26 | return 2.0; 27 | }); 28 | 29 | auto tall = when_all(t1, t2, t3) 30 | .then([](int v1, std::string, double v2) 31 | { 32 | log_print("all completed."); 33 | return v1 + v2; 34 | }) 35 | ; 36 | 37 | tall(); 38 | 39 | auto f = tall.get_future(); 40 | auto val = f.get(); 41 | 42 | log_print("end value is ", val); 43 | } 44 | 45 | template 46 | static auto create_task_foo(const _Ty & val) 47 | { 48 | return st::make_task(st::async_context, [=] 49 | { 50 | std::this_thread::sleep_for(1ms * (rand() % 100)); 51 | 52 | log_print("task foo, val = ", val); 53 | return val; 54 | }); 55 | } 56 | 57 | void test_task_when_all_2() 58 | { 59 | using namespace st; 60 | 61 | std::list v; 62 | for (int i = 1; i < 10; ++i) 63 | v.emplace_back(create_task_foo(i)); 64 | 65 | auto tall = when_all(v.begin(), v.end()) 66 | .then([](std::vector v) 67 | { 68 | if (v.empty()) 69 | log_print("none task."); 70 | else 71 | log_print(v.size(), " task(s) completed."); 72 | 73 | int val = 0; 74 | for (auto t : v) 75 | val += t; 76 | return val; 77 | }) 78 | ; 79 | 80 | tall(); 81 | 82 | auto f = tall.get_future(); 83 | auto val = f.get(); 84 | log_print("end value is ", val); 85 | } 86 | 87 | void test_task_when_all_break_link() 88 | { 89 | using namespace st; 90 | 91 | std::list v; 92 | for (int i = 1; i < 10; ++i) 93 | v.emplace_back(create_task_foo(i)); 94 | 95 | auto tall = when_all(v.begin(), v.end()) 96 | .then([](std::vector v) 97 | { 98 | if (v.empty()) 99 | log_print("none task."); 100 | else 101 | log_print(v.size(), " task(s) completed."); 102 | 103 | int val = 0; 104 | for (auto t : v) 105 | val += t; 106 | return val; 107 | }) 108 | ; 109 | } 110 | 111 | void test_task_when_all() 112 | { 113 | using namespace st; 114 | 115 | test_task_when_all_1(); 116 | #if LIBTASK_DEBUG_MEMORY 117 | for (int i = 0; i < 100; ++i) 118 | { 119 | test_task_when_all_2(); 120 | assert(g_node_counter.load() == 0); 121 | assert(g_task_counter.load() == 0); 122 | 123 | test_task_when_all_break_link(); 124 | assert(g_node_counter.load() == 0); 125 | assert(g_task_counter.load() == 0); 126 | } 127 | #else 128 | test_task_when_all_2(); 129 | #endif 130 | } 131 | -------------------------------------------------------------------------------- /example/task_when_any.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "libtask.h" 5 | #include "log_print.h" 6 | 7 | using namespace std::literals; 8 | 9 | template 10 | static auto create_task_foo(const _Ty & val) 11 | { 12 | return st::make_task(st::async_context, [=] 13 | { 14 | std::this_thread::sleep_for(1ms * (rand() % 100)); 15 | 16 | log_print("task foo, val = ", val); 17 | return val; 18 | }); 19 | } 20 | 21 | void test_task_when_any_1() 22 | { 23 | srand((int)time(nullptr)); 24 | 25 | using namespace st; 26 | 27 | std::list v; 28 | for (int i = 1; i < 10; ++i) 29 | v.emplace_back(create_task_foo(i)); 30 | 31 | auto tall = when_any(v.begin(), v.end()) 32 | .then([](size_t idx, int val) 33 | { 34 | log_print("task(", idx, ") completed. value is ", val); 35 | return val * idx; 36 | }) 37 | ; 38 | 39 | tall(); 40 | 41 | auto f = tall.get_future(); 42 | auto val = f.get(); 43 | log_print("end value is ", val); 44 | } 45 | 46 | void test_task_when_any_2() 47 | { 48 | srand((int)time(nullptr)); 49 | 50 | using namespace st; 51 | 52 | auto t1 = create_task_foo(1); 53 | auto t2 = create_task_foo(2); 54 | auto t3 = create_task_foo(3); 55 | static_assert(std::is_same::value, ""); 56 | static_assert(std::is_same::value, ""); 57 | 58 | auto tall = when_any(t1, t2, t3) 59 | .then([](size_t idx, int val) 60 | { 61 | log_print("task(", idx, ") completed. value is ", val); 62 | return val * idx; 63 | }) 64 | ; 65 | 66 | tall(); 67 | 68 | auto f = tall.get_future(); 69 | auto val = f.get(); 70 | log_print("end value is ", val); 71 | } 72 | 73 | void test_task_when_any_3() 74 | { 75 | srand((int)time(nullptr)); 76 | 77 | using namespace st; 78 | 79 | task, task_node> t1 = create_task_foo(1); 80 | task, task_node> t2 = create_task_foo(2.0f); 81 | task, task_node> t3 = create_task_foo("abc"s); 82 | static_assert(!std::is_same::value, ""); 83 | static_assert(!std::is_same::value, ""); 84 | static_assert(!std::is_same::value, ""); 85 | 86 | auto tall = when_any(t1, t2, t3) 87 | .then([](size_t idx, std::any val) 88 | { 89 | if (idx == 0) 90 | log_print("task(", idx, ") completed. value is ", std::any_cast(val)); 91 | if (idx == 1) 92 | log_print("task(", idx, ") completed. value is ", std::any_cast(val)); 93 | if (idx == 2) 94 | log_print("task(", idx, ") completed. value is ", std::any_cast(val)); 95 | 96 | return idx; 97 | }) 98 | ; 99 | 100 | tall(); 101 | 102 | auto f = tall.get_future(); 103 | auto val = f.get(); 104 | log_print("end index is ", val); 105 | } 106 | 107 | void test_task_when_any_break_link() 108 | { 109 | srand((int)time(nullptr)); 110 | 111 | using namespace st; 112 | 113 | task, task_node> t1 = create_task_foo(1); 114 | task, task_node> t2 = create_task_foo(2.0f); 115 | task, task_node> t3 = create_task_foo("abc"s); 116 | static_assert(!std::is_same::value, ""); 117 | static_assert(!std::is_same::value, ""); 118 | static_assert(!std::is_same::value, ""); 119 | 120 | auto tall = when_any(t1, t2, t3) 121 | .then([](size_t idx, std::any val) 122 | { 123 | if (idx == 0) 124 | log_print("task(", idx, ") completed. value is ", std::any_cast(val)); 125 | if (idx == 1) 126 | log_print("task(", idx, ") completed. value is ", std::any_cast(val)); 127 | if (idx == 2) 128 | log_print("task(", idx, ") completed. value is ", std::any_cast(val)); 129 | 130 | return idx; 131 | }) 132 | ; 133 | } 134 | 135 | void test_task_when_any() 136 | { 137 | using namespace st; 138 | 139 | test_task_when_any_1(); 140 | test_task_when_any_2(); 141 | #if LIBTASK_DEBUG_MEMORY 142 | for (int i = 0; i < 100; ++i) 143 | { 144 | test_task_when_any_3(); 145 | std::this_thread::sleep_for(100ms); 146 | assert(g_node_counter.load() == 0); 147 | assert(g_task_counter.load() == 0); 148 | 149 | test_task_when_any_break_link(); 150 | assert(g_node_counter.load() == 0); 151 | assert(g_task_counter.load() == 0); 152 | } 153 | #else 154 | test_task_when_any_3(); 155 | #endif 156 | } 157 | -------------------------------------------------------------------------------- /example/threadpool_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "task_semaphore.h" 6 | 7 | struct threadpool_context 8 | { 9 | std::vector th_pool; 10 | std::stack tasks; 11 | std::mutex mtx_task; 12 | std::semaphore cv_task; 13 | 14 | threadpool_context(size_t count = 0) 15 | { 16 | if (count == 0) 17 | count = std::thread::hardware_concurrency(); 18 | for (size_t i = 0; i < count; ++i) 19 | th_pool.emplace_back(&threadpool_context::loop_task, this); 20 | } 21 | ~threadpool_context() 22 | { 23 | { 24 | std::unique_lock lock__(mtx_task); 25 | while (!tasks.empty()) 26 | tasks.pop(); 27 | } 28 | cv_task.release((int)th_pool.size()); 29 | 30 | for (auto & th : th_pool) 31 | th.join(); 32 | } 33 | 34 | void add(const st::executor_sptr & runner) 35 | { 36 | assert(runner != nullptr); 37 | { 38 | std::unique_lock lock__(mtx_task); 39 | tasks.push(runner); 40 | } 41 | cv_task.release(); 42 | } 43 | 44 | private: 45 | void loop_task() 46 | { 47 | st::executor_sptr runner; 48 | for (;;) 49 | { 50 | cv_task.acquire(); 51 | { 52 | std::unique_lock lock__(mtx_task); 53 | if (tasks.empty()) 54 | break; 55 | 56 | runner = tasks.top(); 57 | tasks.pop(); 58 | } 59 | runner->run_once(); 60 | } 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /include/task.h: -------------------------------------------------------------------------------- 1 | //实现一个链式调用的任务链功能,来减少callback hell的问题 2 | //通过std::future, 获取最后一个调用的结果。如果中途发生了异常,则std::future::get()会抛出异常 3 | //支持将后续调用指定到特定的“执行环境”上去调用,从而控制执行的时机和线程 4 | //支持C++14/17。由于想节省一些内存,使用了VS的_Promise实现,故只支持VS2015/VS2017 5 | 6 | #pragma once 7 | 8 | #pragma warning(disable : 4503) //任务链很容易类型名过长,所以,禁止这个警告 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "task_utils.inl" 16 | #include "task_invoke_traits.h" 17 | #include "task_detail.inl" 18 | #include "task_executor.inl" 19 | #include "task_exception.inl" 20 | #include "task_node.inl" 21 | #include "task_cbnode.inl" 22 | 23 | namespace lib_shark_task 24 | { 25 | #if LIBTASK_DEBUG_MEMORY 26 | extern std::atomic g_task_counter; 27 | #endif 28 | 29 | constexpr auto _cb = std::placeholders::_1; 30 | //using _ = decltype(std::ignore); 31 | 32 | template 33 | struct task 34 | { 35 | using node_type = _FirstNode; 36 | using node_type_sptr = std::shared_ptr; 37 | 38 | using last_node = _LastNode; 39 | using last_type = typename last_node::result_type; 40 | using last_node_stpr = std::shared_ptr; 41 | private: 42 | node_type_sptr _Node; //第一个任务节点。其后的then/marshal,这个节点是不变化的 43 | last_node_stpr _Last; //最后一个任务节点。每次then/marshal后,新的task的这个类型和值发生变化 44 | task_set_exception_agent_sptr _Exception; //传递异常的代理接口 45 | mutable std::atomic _Node_executed; //_Node是否已经执行过了 46 | public: 47 | //last不能为引用 48 | //如果last和first是同一个对象(不是说内容指向同一个对象),则这里直接move(first)会造成last指向空对象 49 | task(const task_set_exception_agent_sptr & exp, last_node_stpr last, node_type_sptr && first) 50 | : _Node(std::forward(first)) 51 | , _Last(std::move(last)) 52 | , _Exception(exp) 53 | , _Node_executed(false) 54 | { 55 | assert(_Node != nullptr); 56 | assert(_Last != nullptr); 57 | 58 | #if LIBTASK_DEBUG_MEMORY 59 | long task_counter = ++g_task_counter; 60 | 61 | char buffer[256]; 62 | sprintf_s(buffer, sizeof(buffer), "task ctor:%p, counter=%d\r\n", this, task_counter); 63 | ::OutputDebugStringA(buffer); 64 | #endif 65 | } 66 | 67 | task(task && _Right) noexcept 68 | : _Node(std::move(_Right._Node)) 69 | , _Last(std::move(_Right._Last)) 70 | , _Exception(std::move(_Right._Exception)) 71 | , _Node_executed(_Right._Node_executed.load()) 72 | { 73 | #if LIBTASK_DEBUG_MEMORY 74 | long task_counter = ++g_task_counter; 75 | 76 | char buffer[256]; 77 | sprintf_s(buffer, sizeof(buffer), "task ctor:%p, counter=%d\r\n", this, task_counter); 78 | ::OutputDebugStringA(buffer); 79 | #endif 80 | } 81 | 82 | task & operator = (task && _Right) noexcept 83 | { 84 | if (this != &_Right) 85 | { 86 | _Node = std::move(_Right._Node); 87 | _Last = std::move(_Right._Last); 88 | _Exception = std::move(_Right._Exception); 89 | _Node_executed = _Right._Node_executed.load(); 90 | } 91 | return *this; 92 | } 93 | task(const task & st) = delete; 94 | task & operator = (const task & st) = delete; 95 | 96 | ~task() 97 | { 98 | //某些任务会有循环引用的情况,故如果没执行过,需要主动打断循环引用 99 | //执行过,则由具体的执行代码去打断循环引用 100 | if (!_Node_executed.load()) 101 | { 102 | if (_Node) detail::_Break_link(*_Node); 103 | if (_Last) detail::_Break_link(*_Last); 104 | } 105 | 106 | #if LIBTASK_DEBUG_MEMORY 107 | long task_counter = --g_task_counter; 108 | 109 | char buffer[256]; 110 | sprintf_s(buffer, sizeof(buffer), "task dtor:%p, counter=%d\r\n", this, task_counter); 111 | ::OutputDebugStringA(buffer); 112 | #endif 113 | } 114 | 115 | //开始执行任务链的第一个节点。 116 | //执行完毕后,将第一个任务节点设为nullptr。这样就不能再次调用了(划掉) 117 | //执行完毕后,如果不保存_Node,则when_all/when_any里会出现提前被析构的风险。 118 | // 故后来修改为使用_Node_executed来标记是否执行过,从而实现只能执行一次的逻辑 119 | template 120 | void operator()(_Args&&... args) 121 | { 122 | assert(_Node != nullptr); 123 | if (_Node_executed) 124 | throw std::future_error(std::make_error_code(std::future_errc::promise_already_satisfied)); 125 | 126 | _Node_executed = true; 127 | if (_Node->invoke_thiz(std::forward<_Args>(args)...)) 128 | _Node->invoke_then_if(); 129 | _Node = nullptr; 130 | } 131 | 132 | //获取最后一个任务节点,对应的future。 133 | inline auto get_future() 134 | { 135 | assert(_Last != nullptr); 136 | assert(!_Last->is_retrieved()); 137 | 138 | return _Last->get_future(); 139 | } 140 | 141 | //执行器只能获取一次。 142 | //获取完毕后,自身不能再调用operator()了----毕竟是run_once语义 143 | inline auto get_executor() 144 | { 145 | assert(_Node != nullptr); 146 | if (_Node_executed) 147 | throw std::future_error(std::make_error_code(std::future_errc::promise_already_satisfied)); 148 | 149 | _Node_executed = true; 150 | return std::make_shared>(std::move(_Node)); 151 | } 152 | 153 | //根据下一个任务节点,生成新的任务链对象 154 | template 155 | inline auto then(_Ftype && fn) 156 | { 157 | assert(_Last != nullptr); 158 | assert(!_Last->is_retrieved()); 159 | 160 | using then_result = detail::result_of_t>; 161 | using args_tuple_type = detail::args_tuple_t>; 162 | using last_tuple_type = detail::package_tuple_t; 163 | 164 | static_assert(std::tuple_size::value <= std::tuple_size::value, "parames of '_Ftype' is not match this task node(s)."); 165 | 166 | using next_node_type = detail::unpack_tuple_node_t; 167 | 168 | auto st_next = std::make_shared(fn, _Exception); 169 | _Exception->_Impl = st_next.get(); 170 | _Last->_Set_then_if(detail::_Set_then_helper{ st_next }); 171 | _Last = nullptr; 172 | 173 | return task{_Exception, st_next, std::move(_Node)}; 174 | } 175 | 176 | //根据下一个任务节点,生成新的任务链对象 177 | //指定此任务节点必须在ctx所代表的线程/时机里运行 178 | template 179 | inline auto then(_Context & ctx, _Ftype && fn) 180 | { 181 | assert(_Last != nullptr); 182 | assert(!_Last->is_retrieved()); 183 | 184 | using then_result = detail::result_of_t>; 185 | using args_tuple_type = detail::args_tuple_t>; 186 | using last_tuple_type = detail::package_tuple_t; 187 | 188 | static_assert(std::tuple_size::value <= std::tuple_size::value, ""); 189 | 190 | using next_node_type = detail::unpack_tuple_node_t; 191 | 192 | auto st_next = std::make_shared(std::forward<_Ftype>(fn), _Exception); 193 | _Exception->_Impl = st_next.get(); 194 | _Last->_Set_then_if(detail::_Set_then_ctx_helper<_Context, next_node_type>{ &ctx, st_next }); 195 | _Last = nullptr; 196 | 197 | return task{_Exception, st_next, std::move(_Node)}; 198 | } 199 | 200 | template 201 | inline auto marshal(_Fcb&& fn, _Types&&... args) 202 | { 203 | assert(_Last != nullptr); 204 | assert(!_Last->is_retrieved()); 205 | 206 | using callback_type3 = detail::args_of_t::index, _Fcb>; 207 | using callback_type2 = std::remove_cv_t>; 208 | using callback_type = typename detail::invoke_traits::type; 209 | 210 | using next_node_type = detail::unpack_tuple_cbnode_t; 211 | 212 | auto st_next = std::make_shared(_Exception, std::forward<_Fcb>(fn), std::forward<_Types>(args)...); 213 | _Exception->_Impl = st_next.get(); 214 | _Last->_Set_then_if(detail::_Set_then_helper{ st_next }); 215 | _Last = nullptr; 216 | 217 | return task{_Exception, st_next, std::move(_Node)}; 218 | } 219 | 220 | template 221 | inline auto marshal(_Context & ctx, _Fcb&& fn, _Types&&... args) 222 | { 223 | assert(_Last != nullptr); 224 | assert(!_Last->is_retrieved()); 225 | 226 | using callback_type3 = detail::args_of_t::index, _Fcb>; 227 | using callback_type2 = std::remove_cv_t>; 228 | using callback_type = typename detail::invoke_traits::type; 229 | 230 | using next_node_type = detail::unpack_tuple_cbnode_t; 231 | 232 | auto st_next = std::make_shared(_Exception, std::forward<_Fcb>(fn), std::forward<_Types>(args)...); 233 | _Exception->_Impl = st_next.get(); 234 | _Last->_Set_then_if(detail::_Set_then_ctx_helper<_Context, next_node_type>{ &ctx, st_next }); 235 | _Last = nullptr; 236 | 237 | return task{_Exception, st_next, std::move(_Node)}; 238 | } 239 | 240 | inline task_set_exception_agent_sptr & _Get_exception_agent() 241 | { 242 | return _Exception; 243 | } 244 | 245 | template 246 | inline void _Then_node(const std::shared_ptr<_Nnode> & st_next) 247 | { 248 | assert(_Last != nullptr); 249 | assert(!_Last->is_retrieved()); 250 | using next_node_type = std::remove_reference_t<_Nnode>; 251 | 252 | _Last->_Set_then_if(detail::_Set_then_helper{ st_next }); 253 | } 254 | 255 | template 256 | inline void _Then_node(_Context & ctx, const std::shared_ptr<_Nnode> & st_next) 257 | { 258 | assert(_Last != nullptr); 259 | assert(!_Last->is_retrieved()); 260 | using next_node_type = std::remove_reference_t<_Nnode>; 261 | 262 | _Last->_Set_then_if(detail::_Set_then_ctx_helper<_Context, next_node_type>{ &ctx, st_next }); 263 | } 264 | }; 265 | 266 | namespace detail 267 | { 268 | template 269 | struct is_task2 : public std::false_type {}; 270 | template 271 | struct is_task2> : public std::true_type {}; 272 | template 273 | struct is_task : public is_task2::type> {}; 274 | } 275 | 276 | template 277 | struct _Make_task_0_impl 278 | { 279 | template 280 | static inline auto make(_Ftype && fn) 281 | { 282 | using fun_type_rrt = std::remove_reference_t<_Ftype>; 283 | using ret_type = detail::result_of_t; 284 | using args_tuple_type = typename detail::invoke_traits::args_tuple_type; 285 | 286 | using ret_type_rrt = std::remove_reference_t; 287 | using first_node_type = detail::unpack_tuple_node_t; 288 | 289 | task_set_exception_agent_sptr exp = std::make_shared(); 290 | auto st_next = std::make_shared(std::forward<_Ftype>(fn), exp); 291 | exp->_Impl = st_next.get(); 292 | 293 | return task{exp, st_next, std::move(st_next)}; 294 | } 295 | }; 296 | template<> 297 | struct _Make_task_0_impl<0> 298 | { 299 | template 300 | static inline auto make(_Ftype && fn) 301 | { 302 | using fun_type_rrt = std::remove_reference_t<_Ftype>; 303 | using ret_type = detail::result_of_t; 304 | using args_tuple_type = typename detail::invoke_traits::args_tuple_type; 305 | 306 | using ret_type_rrt = std::remove_reference_t; 307 | using first_node_type = task_node; 308 | 309 | task_set_exception_agent_sptr exp = std::make_shared(); 310 | auto st_next = std::make_shared(std::forward<_Ftype>(fn), exp); 311 | exp->_Impl = st_next.get(); 312 | 313 | return task{exp, st_next, std::move(st_next)}; 314 | } 315 | }; 316 | 317 | //根据一个新的任务节点,生成一个全新的任务链 318 | template 319 | inline auto make_task(_Ftype && fn) 320 | { 321 | using fun_type_rrt = std::remove_reference_t<_Ftype>; 322 | using args_tuple_type = typename detail::invoke_traits::args_tuple_type; 323 | 324 | return _Make_task_0_impl::value>::make(std::forward<_Ftype>(fn)); 325 | } 326 | inline auto make_task() 327 | { 328 | return make_task(detail::dummy_method_{}); 329 | } 330 | template 331 | inline auto make_task(_Context & ctx, _Ftype && fn) 332 | { 333 | return make_task().then(ctx, std::forward<_Ftype>(fn)); 334 | } 335 | 336 | template 337 | inline auto marshal_task(_Fcb&& fn, _Args&&... args) 338 | { 339 | using fun_type_rrt = std::remove_reference_t<_Fcb>; 340 | using args_tuple_type = typename detail::invoke_traits::args_tuple_type; 341 | static_assert(sizeof...(_Args) == std::tuple_size::value, "'args' count must equal argument count of 'fn'"); 342 | 343 | using callback_type3 = detail::args_of_t::index, _Fcb>; 344 | using callback_type2 = std::remove_cv_t>; 345 | using callback_type = typename detail::invoke_traits::type; 346 | 347 | using first_node_type = task_cbnode; 348 | 349 | task_set_exception_agent_sptr exp = std::make_shared(); 350 | auto st_next = std::make_shared(exp, std::forward<_Fcb>(fn), std::forward<_Args>(args)...); 351 | exp->_Impl = st_next.get(); 352 | 353 | return task{exp, st_next, std::move(st_next)}; 354 | } 355 | /* 356 | template 357 | inline auto marshal_task(_Context & ctx, _Fcb&& fn, _Args&&... args) 358 | -> decltype(ctx.add(std::declval()), make_task(detail::dummy_method_{}).marshal(ctx, std::forward<_Ftype>(fn), std::forward<_Args>(args)...)) 359 | { 360 | return make_task(detail::dummy_method_{}).marshal(ctx, std::forward<_Ftype>(fn), std::forward<_Args>(args)...); 361 | } 362 | */ 363 | } 364 | 365 | #if !LIBST_DISABLE_SHORT_NS 366 | namespace st = lib_shark_task; 367 | #endif -------------------------------------------------------------------------------- /include/task_cbnode.inl: -------------------------------------------------------------------------------- 1 | //将一个已有的callback包装成任务链 2 | //典型的,一个callback调用是: 3 | // foo(..., cb) 4 | //其中 5 | // cb = void(args) 6 | //要包装成 7 | // tuple foo2(...) 8 | #pragma once 9 | 10 | namespace lib_shark_task 11 | { 12 | namespace detail 13 | { 14 | typedef decltype(std::placeholders::_1) placeholder_type; 15 | 16 | template 17 | struct get_holder_index2; 18 | 19 | template<> 20 | struct get_holder_index2<> 21 | { 22 | static const size_t index = 0; 23 | }; 24 | 25 | template 26 | struct get_holder_index2<_H1, _Holders...> : public get_holder_index2<_Holders...> 27 | { 28 | }; 29 | 30 | template 31 | struct get_holder_index2 32 | { 33 | static const size_t index = sizeof...(_Holders) + 1; 34 | }; 35 | template 36 | struct get_holder_index2 37 | { 38 | static const size_t index = sizeof...(_Holders) + 1; 39 | }; 40 | template 41 | struct get_holder_index2 42 | { 43 | static const size_t index = sizeof...(_Holders) + 1; 44 | }; 45 | 46 | //_Holders...里面至少有一个类型是std::placeholders::_1,获得_1在_Holders的第几个上 47 | template 48 | struct get_holder_index 49 | { 50 | static_assert(get_holder_index2<_Holders...>::index > 0, "get_holder_index<> have not std::placeholders::_1"); 51 | 52 | static const size_t index = sizeof...(_Holders) - get_holder_index2<_Holders...>::index; 53 | }; 54 | 55 | //static_assert(get_holder_index::index == 0, "place holder in 0"); 56 | //static_assert(get_holder_index::index == 1, "place holder in 1"); 57 | //static_assert(get_holder_index::index == 2, "place holder in 2"); 58 | //static_assert(get_holder_index::index == 1, "place holder in 1"); 59 | ////static_assert(get_holder_index::index == 3, "static_assert cause failed"); 60 | 61 | //生造一个兼容回调参数的函数对象。将这个函数对象给回调函数 62 | template 63 | struct callback_relay; 64 | 65 | template 66 | struct callback_relay<_Stype, void(_Types...)> 67 | { 68 | std::shared_ptr<_Stype> _Assoc_node; 69 | 70 | template 71 | inline void operator()(_Types2&&... args) 72 | { 73 | _Assoc_node->_Do_callback(std::forward<_Types2>(args)...); 74 | } 75 | }; 76 | 77 | template 78 | struct callback_relay<_Stype, void(*)(_Types...)> : public callback_relay<_Stype, void(_Types...)> 79 | { 80 | }; 81 | template 82 | struct callback_relay<_Stype, std::function> : public callback_relay<_Stype, void(_Types...)> 83 | { 84 | }; 85 | template 86 | struct callback_relay<_Stype, callback_relay<_Types...>> : public callback_relay<_Stype, void(_Types...)> 87 | { 88 | }; 89 | } 90 | 91 | //包装回调为任务节点 92 | //_Cbtype回调参数的类型。回调参数是一个函数(对象),其入参作为本任务节点的返回值 93 | //_PrevArgs...是上一个任务节点的返回值(如果上一个节点返回值是std::tuple<>,则_PrevArgs是将tuple接包后的参数列表) 94 | template 95 | struct task_cbnode : public node_result_> 96 | { 97 | using this_type = task_cbnode<_Cbtype, _PrevArgs...>; 98 | 99 | using result_type = detail::args_tuple_t<_Cbtype>; //本节点的结果的类型 100 | using result_tuple = result_type; //本节点的结果打包成tuple<>后的类型 101 | using args_tuple_type = std::tuple<_PrevArgs...>; //本节点的入参打包成tuple<>后的类型 102 | 103 | using base_type = node_result_>; 104 | 105 | template 106 | task_cbnode(const task_set_exception_agent_sptr & exp, _Fx&& _Func, _Types&&... args) 107 | : base_type(exp) 108 | , _Thiz(std::bind(std::forward<_Fx>(_Func), std::forward<_Types>(args)...)) 109 | { 110 | } 111 | task_cbnode(task_cbnode && _Right) = default; 112 | task_cbnode & operator = (task_cbnode && _Right) = default; 113 | task_cbnode(const task_cbnode & _Right) = delete; 114 | task_cbnode & operator = (const task_cbnode & _Right) = delete; 115 | private: 116 | template 117 | void _Do_callback(_Types2&&... args) 118 | { 119 | this->_Set_value(std::make_tuple(std::forward<_Types2>(args)...)); 120 | this->_Ready = true; 121 | invoke_then_if(); 122 | } 123 | public: 124 | template 125 | bool invoke_thiz(_PrevArgs2&&... args) 126 | { 127 | static_assert(sizeof...(_PrevArgs2) >= std::tuple_size::value, ""); 128 | 129 | try 130 | { 131 | task_function fn = this->_Move_thiz(); 132 | 133 | detail::callback_relay cb; 134 | cb._Assoc_node = std::static_pointer_cast(this->shared_from_this()); 135 | 136 | detail::_Apply_then(fn, std::move(cb), std::forward<_PrevArgs2>(args)...); 137 | } 138 | catch (...) 139 | { 140 | this->_Set_Agent_exception(std::current_exception()); 141 | } 142 | 143 | return false; 144 | } 145 | 146 | template 147 | bool invoke_thiz_tuple(_PrevTuple&& args) 148 | { 149 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 150 | 151 | try 152 | { 153 | task_function fn = this->_Move_thiz(); 154 | 155 | detail::callback_relay cb; 156 | cb._Assoc_node = std::static_pointer_cast(this->shared_from_this()); 157 | 158 | detail::_Apply_function::template _Apply_cat(fn, std::move(cb), std::forward<_PrevTuple>(args)); 159 | } 160 | catch (...) 161 | { 162 | this->_Set_Agent_exception(std::current_exception()); 163 | } 164 | 165 | return false; 166 | } 167 | 168 | void invoke_then_if() 169 | { 170 | if (!this->_Ready) 171 | return; 172 | 173 | then_function fn = this->_Move_then(); 174 | if (!fn) 175 | return; 176 | 177 | try 178 | { 179 | detail::_Apply_then(fn, std::move(this->_Get_value())); 180 | } 181 | catch (...) 182 | { 183 | this->_Set_Agent_exception(std::current_exception()); 184 | } 185 | } 186 | 187 | template 188 | void _Set_then_if(_NextFx && fn) 189 | { 190 | this->_Set_retrieved(); 191 | 192 | if (this->_Ready) 193 | { 194 | try 195 | { 196 | detail::_Apply_then2(std::forward<_NextFx>(fn), std::move(this->_Get_value())); 197 | } 198 | catch (...) 199 | { 200 | this->_Set_Agent_exception(std::current_exception()); 201 | } 202 | } 203 | else 204 | { 205 | std::unique_lock _Lock(this->_Mtx()); 206 | this->_Then = then_function{ std::forward<_NextFx>(fn) }; 207 | } 208 | } 209 | 210 | public: 211 | using relay_type = detail::callback_relay; 212 | friend relay_type; 213 | using task_function = std::function; 214 | using then_function = detail::unpack_tuple_fn_t; 215 | protected: 216 | task_function _Thiz; //执行当前任务节点 217 | then_function _Then; //执行下一个任务节点 218 | //取执行当前任务节点的函数,只能取一次。线程安全 219 | inline task_function _Move_thiz() 220 | { 221 | std::unique_lock _Lock(this->_Mtx()); 222 | return std::move(_Thiz); //强迫只能调用一次 223 | } 224 | //取执行下一个任务节点的函数,只能取一次。线程安全 225 | inline then_function _Move_then() 226 | { 227 | std::unique_lock _Lock(this->_Mtx()); 228 | return std::move(_Then); //强迫只能调用一次 229 | } 230 | }; 231 | 232 | template 233 | struct task_cbnode<_Cbtype, std::tuple<_PrevArgs...>> : public task_cbnode<_Cbtype, _PrevArgs...> 234 | { 235 | using task_cbnode<_Cbtype, _PrevArgs...>::task_cbnode; 236 | }; 237 | } 238 | -------------------------------------------------------------------------------- /include/task_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lib_shark_task 4 | { 5 | struct immediate_task_context 6 | { 7 | inline void add(const executor_sptr & runner) const 8 | { 9 | assert(runner != nullptr); 10 | runner->run_once(); 11 | } 12 | }; 13 | extern immediate_task_context imm_context; 14 | 15 | struct async_task_context 16 | { 17 | inline void add(const executor_sptr & runner) const 18 | { 19 | assert(runner != nullptr); 20 | std::thread async_run 21 | { 22 | [=] 23 | { 24 | runner->run_once(); 25 | } 26 | }; 27 | async_run.detach(); 28 | } 29 | }; 30 | extern async_task_context async_context; 31 | } 32 | -------------------------------------------------------------------------------- /include/task_detail.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lib_shark_task 4 | { 5 | template 6 | struct task_node; 7 | template 8 | struct task_cbnode; 9 | 10 | namespace detail 11 | { 12 | struct dummy_method_ 13 | { 14 | void operator()()const 15 | {} 16 | }; 17 | 18 | template 19 | using result_of_t = typename invoke_traits<_Fx>::result_type; //萃取函数(对象)_Fx的返回值 20 | template 21 | using args_tuple_t = typename invoke_traits<_Fx>::args_tuple_type; //萃取函数(对象)_Fx的参数列表,这个参数列表用std::tuple<>来表达 22 | template 23 | using args_of_t = typename invoke_traits<_Fx>::template args_element<_Idx>::type; //萃取函数(对象)_Fx的特定索引的参数类型 24 | 25 | template 26 | inline void _Fill_to_tuple_impl(_Tuple & t) {} 27 | template 28 | inline void _Fill_to_tuple_impl(_Tuple & t, _Ty && val, _Rest&&... args) 29 | { 30 | std::get<_Idx>(t) = std::forward<_Ty>(val); 31 | _Fill_to_tuple_impl<_Idx + 1>(t, std::forward<_Rest>(args)...); 32 | } 33 | template 34 | struct _Copy_to_tuple_impl 35 | { 36 | template 37 | static inline void copy(_T1 & target, _T2&& source) 38 | { 39 | std::get<_Offset + _Idx - 1>(target) = std::get<_Idx - 1>(source); 40 | _Copy_to_tuple_impl<_Idx - 1>::template copy<_Offset>(target, source); 41 | } 42 | template 43 | static inline void move(_T1 & target, _T2&& source) 44 | { 45 | std::get<_Offset + _Idx - 1>(target) = std::move(std::get<_Idx - 1>(source)); 46 | _Copy_to_tuple_impl<_Idx - 1>::template move<_Offset>(target, source); 47 | } 48 | }; 49 | template<> 50 | struct _Copy_to_tuple_impl<0> 51 | { 52 | template 53 | static inline void copy(_T1 & target, _T2&& source) 54 | { 55 | } 56 | template 57 | static inline void move(_T1 & target, _T2&& source) 58 | { 59 | } 60 | }; 61 | 62 | template 63 | struct _Fill_to_tuple_selector 64 | { 65 | template 66 | static inline void _fill(_Tuple & t, _Ty && val, _Rest&&... args) 67 | { 68 | t = std::forward<_Ty>(val); 69 | } 70 | }; 71 | template 72 | struct _Fill_to_tuple_selector> 73 | { 74 | using _Tuple = std::tuple<_Args...>; 75 | template 76 | static inline void _fill(_Tuple & t, _Rest&&... args) 77 | { 78 | _Fill_to_tuple_impl<_Idx>(t, std::forward<_Rest>(args)...); 79 | } 80 | }; 81 | 82 | template 83 | inline void _Fill_to_tuple(_Tuple & t, _Args&&... args) 84 | { 85 | _Fill_to_tuple_selector<_Tuple>::template _fill<_Idx>(t, std::forward<_Args>(args)...); 86 | } 87 | template 88 | inline void _Copy_to_tuple(_Tuple & target, _Tuple2 && source) 89 | { 90 | static_assert(std::tuple_size<_Tuple>::value >= std::tuple_size<_Tuple2>::value +_Idx, "index is out of range, _Idx must less than t1 size - t2 size"); 91 | _Copy_to_tuple_impl::value>::copy<_Idx>(target, source); 92 | } 93 | template 94 | inline void _Move_to_tuple(_Tuple & target, _Tuple2 && source) 95 | { 96 | static_assert(std::tuple_size<_Tuple>::value >= std::tuple_size<_Tuple2>::value +_Idx, "index is out of range, _Idx must less than t1 size - t2 size"); 97 | _Copy_to_tuple_impl::value>::move<_Idx>(target, source); 98 | } 99 | 100 | 101 | //使用上一个节点的返回值,作为下一个节点的参数调用下一个任务节点 102 | //如果上一个节点的返回值是std::tuple<...>,则拆包tuple为变参模板 103 | template 104 | struct _Invoke_then_impl 105 | { 106 | template 107 | struct unpack_tuple_node 108 | { 109 | using type = task_node<_Rtype, _PrevArgs>; 110 | }; 111 | template 112 | struct unpack_tuple_cbnode 113 | { 114 | using type = task_cbnode<_Rtype, _PrevArgs>; 115 | }; 116 | template 117 | struct unpack_tuple_function 118 | { 119 | using type = std::function<_Rtype(_PrevArgs)>; 120 | }; 121 | 122 | #pragma warning(push) 123 | #pragma warning(disable: 4100) // TRANSITION, VSO#181496, unreferenced formal parameter 124 | template 125 | static inline decltype(auto) _Apply_impl(_Fx&& f, const _PrevArgs& args, _Rest&&... rest) 126 | { 127 | return std::forward<_Fx>(f)(args); 128 | } 129 | template 130 | static inline decltype(auto) _Apply_impl(_Fx&& f, _PrevArgs& args, _Rest&&... rest) 131 | { 132 | return std::forward<_Fx>(f)(std::forward<_PrevArgs>(args)); 133 | } 134 | template 135 | static inline decltype(auto) _Apply_impl(_Fx&& f, _PrevArgs&& args, _Rest&&... rest) 136 | { 137 | return std::forward<_Fx>(f)(std::forward<_PrevArgs>(args)); 138 | } 139 | #pragma warning(pop) 140 | template 141 | static inline decltype(auto) Invoke(_Fx&& f, _PrevArgs2&&... args) 142 | { 143 | return _Apply_impl(std::forward<_Fx>(f), std::forward<_PrevArgs2>(args)...); 144 | //return std::forward<_Fx>(f)(std::forward<_PrevArgs2>(args)...); 145 | } 146 | }; 147 | template<> 148 | struct _Invoke_then_impl 149 | { 150 | template 151 | struct unpack_tuple_node 152 | { 153 | using type = task_node<_Rtype>; 154 | }; 155 | template 156 | struct unpack_tuple_cbnode 157 | { 158 | using type = task_cbnode<_Rtype>; 159 | }; 160 | template 161 | struct unpack_tuple_function 162 | { 163 | using type = std::function<_Rtype()>; 164 | }; 165 | 166 | template 167 | static inline decltype(auto) Invoke(_Fx & f) 168 | { 169 | return f(); 170 | } 171 | }; 172 | template 173 | struct _Invoke_then_impl> 174 | { 175 | using tuple_type = std::tuple<_PrevArgs...>; 176 | 177 | template 178 | struct unpack_tuple_node 179 | { 180 | using type = task_node<_Rtype, _PrevArgs...>; 181 | }; 182 | template 183 | struct unpack_tuple_cbnode 184 | { 185 | using type = task_cbnode<_Rtype, _PrevArgs...>; 186 | }; 187 | template 188 | struct unpack_tuple_function 189 | { 190 | using type = std::function<_Rtype(_PrevArgs...)>; 191 | }; 192 | 193 | #pragma warning(push) 194 | #pragma warning(disable: 4100) // TRANSITION, VSO#181496, unreferenced formal parameter 195 | template 196 | static inline decltype(auto) _Apply_impl2(_Fx&& f, _PrevArgs&&... args, _Rest&&... rest) 197 | { 198 | return std::forward<_Fx>(f)(std::forward<_PrevArgs>(args)...); 199 | } 200 | 201 | template 202 | static inline decltype(auto) _Apply_impl(_Fx& f, _Tuple&& t, std::index_sequence<_Idx...>) 203 | { 204 | return _Apply_impl2(std::forward<_Fx>(f), std::get<_Idx>(std::forward<_Tuple>(t))...); 205 | } 206 | #pragma warning(pop) 207 | 208 | template 209 | static inline decltype(auto) Invoke(_Fx&& f, _Tuple&& args) 210 | { 211 | //return _Apply_impl(std::forward<_Fx>(f), std::forward<_Tuple>(args), std::make_index_sequence::value>{}); 212 | return std::apply(std::forward<_Fx>(f), std::forward<_Tuple>(args)); 213 | } 214 | }; 215 | 216 | template 217 | inline void _Invoke_then(_Fx&& f, _PrevArgs&& val) 218 | { 219 | using value_type = std::remove_reference_t<_PrevArgs>; 220 | _Invoke_then_impl::template Invoke<_Fx>(std::forward<_Fx>(f), std::forward<_PrevArgs>(val)); 221 | } 222 | template 223 | using unpack_tuple_fn_t = typename _Invoke_then_impl<_PrevArgs>::template unpack_tuple_function<_Rtype>::type; 224 | template 225 | using unpack_tuple_node_t = typename _Invoke_then_impl<_PrevArgs>::template unpack_tuple_node<_Rtype>::type; 226 | template 227 | using unpack_tuple_cbnode_t = typename _Invoke_then_impl<_PrevArgs>::template unpack_tuple_cbnode<_Rtype>::type; 228 | 229 | template 230 | struct _Apply_function; 231 | 232 | template 233 | struct _Apply_function> 234 | { 235 | using function_type = std::function<_Rx(_PrevArgs...)>; 236 | 237 | template 238 | static inline decltype(auto) _Apply_impl2(_Fx&& f, _PrevArgs&&... args, _Rest&&... rest) 239 | { 240 | return std::forward<_Fx>(f)(std::forward<_PrevArgs>(args)...); 241 | } 242 | 243 | template 244 | static inline decltype(auto) _Apply_impl(_Fx&& f, _Tuple&& t, std::index_sequence<_Idx...>) 245 | { 246 | return _Apply_impl2(std::forward<_Fx>(f), std::get<_Idx>(std::forward<_Tuple>(t))...); 247 | } 248 | template 249 | static inline decltype(auto) _Apply_cat_impl(_Fx&& f, _Ty1&& t1, _Tuple&& t, std::index_sequence<_Idx...>) 250 | { 251 | return _Apply_impl2(std::forward<_Fx>(f), std::forward<_Ty1>(t1), std::get<_Idx>(std::forward<_Tuple>(t))...); 252 | } 253 | 254 | //-------------------std::tuple<>版本----------------------------------------------------------------------- 255 | template 256 | static inline decltype(auto) _Apply(_Fx&& f, std::tuple<_Tuple...>&& t) 257 | { 258 | return _Apply_impl(std::forward<_Fx>(f), std::forward>(t), std::make_index_sequence{}); 259 | } 260 | template 261 | static inline decltype(auto) _Apply(_Fx&& f, std::tuple<_Tuple...>& t) 262 | { 263 | return _Apply_impl(std::forward<_Fx>(f), t, std::make_index_sequence{}); 264 | } 265 | template 266 | static inline decltype(auto) _Apply(_Fx&& f, const std::tuple<_Tuple...>& t) 267 | { 268 | return _Apply_impl(std::forward<_Fx>(f), t, std::make_index_sequence{}); 269 | } 270 | 271 | template 272 | static inline decltype(auto) _Apply_cat(_Fx&& f, _Ty1&& t1, std::tuple<_Tuple...>&& t) 273 | { 274 | return _Apply_cat_impl(std::forward<_Fx>(f), std::forward<_Ty1>(t1), std::forward>(t), std::make_index_sequence{}); 275 | } 276 | template 277 | static inline decltype(auto) _Apply_cat(_Fx&& f, _Ty1&& t1, std::tuple<_Tuple...>& t) 278 | { 279 | return _Apply_cat_impl(std::forward<_Fx>(f), std::forward<_Ty1>(t1), t, std::make_index_sequence{}); 280 | } 281 | template 282 | static inline decltype(auto) _Apply_cat(_Fx&& f, _Ty1&& t1, const std::tuple<_Tuple...>& t) 283 | { 284 | return _Apply_cat_impl(std::forward<_Fx>(f), std::forward<_Ty1>(t1), t, std::make_index_sequence{}); 285 | } 286 | //-------------------std::tuple<>版本----------------------------------------------------------------------- 287 | 288 | //-------------------变参版本------------------------------------------------------------------------------- 289 | template 290 | static inline decltype(auto) _Apply(_Fx&& f, _PrevArgs2&&... t) 291 | { 292 | return _Apply_impl2(std::forward<_Fx>(f), std::forward<_PrevArgs2>(t)...); 293 | } 294 | //-------------------变参版本------------------------------------------------------------------------------- 295 | }; 296 | template 297 | inline decltype(auto) _Apply_then(_Fx&& f, _PrevArgs2&&... args) 298 | { 299 | using function_type = std::remove_reference_t<_Fx>; 300 | return _Apply_function::template _Apply(std::forward<_Fx>(f), std::forward<_PrevArgs2>(args)...); 301 | } 302 | template 303 | inline decltype(auto) _Apply_then2(_Fx2&& f, _PrevArgs2&&... args) 304 | { 305 | using function_type = std::remove_reference_t<_Fx>; 306 | return _Apply_function::template _Apply(std::forward<_Fx2>(f), std::forward<_PrevArgs2>(args)...); 307 | } 308 | 309 | template 310 | struct package_tuple 311 | { 312 | using type = std::tuple<_Tuple>; 313 | }; 314 | template 315 | struct package_tuple> 316 | { 317 | using type = std::tuple<_Args...>; 318 | }; 319 | template 320 | using package_tuple_t = typename package_tuple<_Tuple>::type; 321 | 322 | template 323 | struct add_shared_ptr 324 | { 325 | using type = std::shared_ptr<_Ty>; 326 | }; 327 | template 328 | struct add_shared_ptr> 329 | { 330 | using type = std::shared_ptr<_Ty>; 331 | }; 332 | template 333 | using add_shared_ptr_t = typename add_shared_ptr<_Ty>::type; 334 | 335 | template 336 | struct add_tuple_shared_ptr 337 | { 338 | using type = typename add_shared_ptr<_Tuple>::type; 339 | }; 340 | template 341 | struct add_tuple_shared_ptr> 342 | { 343 | using type = std::tuple...>; 344 | }; 345 | template 346 | using add_tuple_shared_ptr_t = typename add_tuple_shared_ptr<_Ty>::type; 347 | 348 | template 349 | struct __has_member_function_break_link 350 | { 351 | private: 352 | template 353 | static auto check_(int) -> decltype(std::declval().break_link(), std::true_type()); 354 | template 355 | static std::false_type check_(...); 356 | public: 357 | using type = decltype(check_<_Ty>(0)); 358 | static const bool value = std::is_same_v; 359 | }; 360 | template 361 | void _Break_link_impl(_Ty & node, std::true_type) 362 | { 363 | node.break_link(); 364 | } 365 | template 366 | void _Break_link_impl(_Ty & node, std::false_type) 367 | { 368 | } 369 | template 370 | void _Break_link(_Ty & node) 371 | { 372 | _Break_link_impl(node, typename __has_member_function_break_link<_Ty>::type{}); 373 | } 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /include/task_exception.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lib_shark_task 4 | { 5 | struct task_set_exception 6 | { 7 | friend struct task_set_exception_agent; 8 | private: 9 | virtual void _Set_exception(std::exception_ptr && val) = 0; 10 | }; 11 | 12 | struct task_set_exception_agent 13 | { 14 | std::atomic _Impl{ nullptr }; 15 | inline void _Set_exception(std::exception_ptr && val) 16 | { 17 | auto exp = _Impl.load(); 18 | exp->_Set_exception(std::forward(val)); 19 | } 20 | }; 21 | using task_set_exception_agent_sptr = std::shared_ptr; 22 | } 23 | -------------------------------------------------------------------------------- /include/task_executor.inl: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | namespace lib_shark_task 5 | { 6 | struct executor 7 | { 8 | virtual ~executor() {} 9 | 10 | virtual void run_once() = 0; 11 | }; 12 | using executor_sptr = std::shared_ptr; 13 | 14 | template 15 | struct task_executor : public executor 16 | { 17 | using node_type = _St; 18 | using result_type = typename node_type::result_type; 19 | using args_tuple_type = typename node_type::args_tuple_type; 20 | 21 | private: 22 | std::shared_ptr _State; 23 | public: 24 | args_tuple_type _Parames; 25 | 26 | template 27 | task_executor(const std::shared_ptr & st, _Rest&&... args) 28 | : _State(st) 29 | , _Parames{std::forward<_Rest>(args)...} 30 | { 31 | assert(_State != nullptr); 32 | } 33 | 34 | task_executor(const task_executor & st) = default; 35 | task_executor(task_executor && st) = default; 36 | task_executor & operator = (const task_executor & st) = default; 37 | task_executor & operator = (task_executor && st) = default; 38 | 39 | virtual void run_once() override 40 | { 41 | assert(_State != nullptr); 42 | 43 | if (_State->invoke_thiz_tuple(std::move(_Parames))) 44 | _State->invoke_then_if(); 45 | } 46 | }; 47 | 48 | template 49 | struct method_executor : public executor 50 | { 51 | using method_type = _Ft; 52 | using result_type = detail::result_of_t; 53 | using args_tuple_type = detail::args_tuple_t; 54 | 55 | private: 56 | bool _Is_running = false; 57 | method_type _Method; 58 | public: 59 | args_tuple_type _Parames; 60 | 61 | template 62 | method_executor(const method_type & st, _Rest&&... args) 63 | : _Method(st) 64 | , _Parames{ std::forward<_Rest>(args)... } 65 | { 66 | } 67 | 68 | method_executor(const method_executor & st) = default; 69 | method_executor(method_executor && st) = default; 70 | method_executor & operator = (const method_executor & st) = default; 71 | method_executor & operator = (method_executor && st) = default; 72 | 73 | virtual void run_once() override 74 | { 75 | if (!_Is_running) 76 | { 77 | _Is_running = true; 78 | std::apply(_Method, std::move(_Parames)); 79 | } 80 | } 81 | }; 82 | 83 | 84 | namespace detail 85 | { 86 | template 87 | struct _Set_then_helper 88 | { 89 | std::shared_ptr<_Stype> _Next; 90 | 91 | template 92 | auto operator ()(_Args&&... args) const 93 | { 94 | if (_Next->invoke_thiz(std::forward<_Args>(args)...)) 95 | _Next->invoke_then_if(); 96 | } 97 | }; 98 | 99 | 100 | template 101 | struct _Set_then_ctx_helper 102 | { 103 | _Context * _Ctx; 104 | std::shared_ptr<_Stype> _Next; 105 | 106 | template 107 | auto operator ()(_Args&&... args) const 108 | { 109 | using executor_type = task_executor<_Stype>; 110 | auto exe = std::make_shared(_Next, std::forward<_Args>(args)...); 111 | 112 | _Ctx->add(exe); 113 | } 114 | }; 115 | 116 | 117 | template 118 | struct _Set_method_ctx_helper 119 | { 120 | using method_type = _Ftype; 121 | using executor_type = method_executor; 122 | using executor_type_ptr = std::shared_ptr; 123 | 124 | _Context * _Ctx; 125 | executor_type_ptr _Next; 126 | 127 | template 128 | _Set_method_ctx_helper(_Context * c, method_type && st, _Rest&&... args) 129 | : _Ctx(c) 130 | , _Next(std::make_shared<>(std::forward(st), std::forward<_Rest>(args)...)) 131 | { 132 | } 133 | 134 | template 135 | auto operator ()(_Args&&... args) const 136 | { 137 | _Ctx->add(_Next); 138 | } 139 | }; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /include/task_invoke_traits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lib_shark_task 4 | { 5 | namespace detail 6 | { 7 | template 8 | struct invoke_traits; 9 | 10 | //函数_Ret(_Args...)的特征 11 | template 12 | struct native_invoke_traits 13 | { 14 | using result_type = _Ret; //返回值 15 | using type = _Ret(_Args...); //函数类型 16 | using args_tuple_type = std::tuple<_Args...>; //将函数参作包装成tuple的类型 17 | using std_function_type = std::function; //对应的std::function<>类 18 | 19 | using funptr_type = std::true_type; //是函数指针 20 | using memfun_type = std::false_type; //不是成员函数 21 | using functor_type = std::false_type; //不是仿函数 22 | 23 | enum { args_size = sizeof...(_Args) }; //函数参数个数 24 | 25 | //通过指定的索引获取函数参数的类型 26 | template 27 | struct args_element 28 | { 29 | static_assert(_Idx < args_size, "index is out of range, index must less than sizeof _Args"); 30 | using type = typename std::tuple_element<_Idx, args_tuple_type>::type; 31 | }; 32 | 33 | //调用此类函数的辅助方法 34 | template 35 | static inline _Ret Invoke_(const _Fx & f, _Args&&... args, _Rest...) 36 | { 37 | return f(std::forward<_Args>(args)...); 38 | } 39 | }; 40 | 41 | //普通函数. 42 | template 43 | struct invoke_traits<_Ret(_Args...)> : public native_invoke_traits<_Ret, _Args...> 44 | { 45 | }; 46 | 47 | //函数指针. 48 | template 49 | struct invoke_traits<_Ret(*)(_Args...)> : public native_invoke_traits<_Ret, _Args...> 50 | { 51 | }; 52 | 53 | //std::function<> 54 | template 55 | struct invoke_traits > : public native_invoke_traits<_Ret, _Args...> 56 | { 57 | }; 58 | template 59 | struct invoke_traits &> : public native_invoke_traits<_Ret, _Args...> 60 | { 61 | }; 62 | template 63 | struct invoke_traits &&> : public native_invoke_traits<_Ret, _Args...> 64 | { 65 | }; 66 | 67 | 68 | //成员函数,普通版本 69 | template 70 | struct invoke_traits<_Ret(_Ctype::*)(_Args...)> : public native_invoke_traits<_Ret, _Args...> 71 | { 72 | using memfun_type = std::true_type; 73 | using callee_type = _Ctype; 74 | using this_args_type = callee_type & ; 75 | 76 | typedef _Ret(_Ctype::*pointer_type)(_Args...); 77 | template 78 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 79 | { 80 | return (obj.*f)(std::forward<_Args>(args)...); 81 | } 82 | }; 83 | 84 | //成员函数,const版本 85 | template 86 | struct invoke_traits<_Ret(_Ctype::*)(_Args...) &> : public native_invoke_traits<_Ret, _Args...> 87 | { 88 | using memfun_type = std::true_type; 89 | using callee_type = _Ctype; 90 | using this_args_type = callee_type & ; 91 | 92 | typedef _Ret(_Ctype::*pointer_type)(_Args...); 93 | template 94 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 95 | { 96 | return (obj.*f)(std::forward<_Args>(args)...); 97 | } 98 | }; 99 | 100 | template 101 | struct invoke_traits<_Ret(_Ctype::*)(_Args...) &&> : public native_invoke_traits<_Ret, _Args...> 102 | { 103 | using memfun_type = std::true_type; 104 | using callee_type = _Ctype; 105 | using this_args_type = callee_type && ; 106 | 107 | typedef _Ret(_Ctype::*pointer_type)(_Args...); 108 | template 109 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 110 | { 111 | return (obj.*f)(std::forward<_Args>(args)...); 112 | } 113 | }; 114 | 115 | template 116 | struct invoke_traits<_Ret(_Ctype::*)(_Args...) const> : public native_invoke_traits<_Ret, _Args...> 117 | { 118 | using memfun_type = std::true_type; 119 | using callee_type = _Ctype; 120 | using this_args_type = const callee_type &; 121 | 122 | typedef _Ret(_Ctype::*pointer_type)(_Args...) const; 123 | template 124 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 125 | { 126 | return (obj.*f)(std::forward<_Args>(args)...); 127 | } 128 | }; 129 | 130 | //成员函数,volatile版本 131 | template 132 | struct invoke_traits<_Ret(_Ctype::*)(_Args...) volatile> : public native_invoke_traits<_Ret, _Args...> 133 | { 134 | using memfun_type = std::true_type; 135 | using callee_type = _Ctype; 136 | using this_args_type = volatile callee_type &; 137 | 138 | typedef _Ret(_Ctype::*pointer_type)(_Args...) volatile; 139 | template 140 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 141 | { 142 | return (obj.*f)(std::forward<_Args>(args)...); 143 | } 144 | }; 145 | 146 | //成员函数,const volatile版本 147 | template 148 | struct invoke_traits<_Ret(_Ctype::*)(_Args...) const volatile> : public native_invoke_traits<_Ret, _Args...> 149 | { 150 | using memfun_type = std::true_type; 151 | using callee_type = _Ctype; 152 | using this_args_type = const volatile callee_type &; 153 | 154 | typedef _Ret(_Ctype::*pointer_type)(_Args...) const volatile; 155 | template 156 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 157 | { 158 | return (obj.*f)(std::forward<_Args>(args)...); 159 | } 160 | }; 161 | 162 | template 163 | struct invoke_traits<_Ret(_Ctype::*)(_Args...) const &> : public native_invoke_traits<_Ret, _Args...> 164 | { 165 | using memfun_type = std::true_type; 166 | using callee_type = _Ctype; 167 | using this_args_type = const callee_type &; 168 | 169 | typedef _Ret(_Ctype::*pointer_type)(_Args...); 170 | template 171 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 172 | { 173 | return (obj.*f)(std::forward<_Args>(args)...); 174 | } 175 | }; 176 | 177 | template 178 | struct invoke_traits<_Ret(_Ctype::*)(_Args...) volatile &> : public native_invoke_traits<_Ret, _Args...> 179 | { 180 | using memfun_type = std::true_type; 181 | using callee_type = _Ctype; 182 | using this_args_type = volatile callee_type & ; 183 | 184 | typedef _Ret(_Ctype::*pointer_type)(_Args...); 185 | template 186 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 187 | { 188 | return (obj.*f)(std::forward<_Args>(args)...); 189 | } 190 | }; 191 | 192 | template 193 | struct invoke_traits<_Ret(_Ctype::*)(_Args...) volatile &&> : public native_invoke_traits<_Ret, _Args...> 194 | { 195 | using memfun_type = std::true_type; 196 | using callee_type = _Ctype; 197 | using this_args_type = volatile callee_type && ; 198 | 199 | typedef _Ret(_Ctype::*pointer_type)(_Args...); 200 | template 201 | static inline _Ret Invoke_(const pointer_type & f, this_args_type obj, _Args&&... args, _Rest...) 202 | { 203 | return (obj.*f)(std::forward<_Args>(args)...); 204 | } 205 | }; 206 | 207 | //函数对象. 208 | template 209 | struct invoke_traits 210 | { 211 | using base_type = invoke_traits; 212 | 213 | using result_type = typename base_type::result_type; 214 | using type = typename base_type::type; 215 | using args_tuple_type = typename base_type::args_tuple_type; 216 | using std_function_type = typename base_type::std_function_type; 217 | 218 | using functor_type = std::true_type; 219 | using callee_type = typename base_type::callee_type; 220 | using this_args_type = typename base_type::this_args_type; 221 | 222 | enum { args_size = base_type::args_size }; 223 | 224 | template 225 | struct args_element 226 | { 227 | static_assert(_Idx < args_size, "index is out of range, index must less than sizeof _Args"); 228 | using type = typename std::tuple_element<_Idx, args_tuple_type>::type; 229 | }; 230 | 231 | template 232 | static inline decltype(auto) Invoke_(this_args_type obj, _Rest&&... args) 233 | { 234 | return base_type::template Invoke_(&_Tobj::operator(), obj, std::forward<_Rest>(args)...); 235 | } 236 | }; 237 | 238 | #ifdef _WIN32 239 | #ifdef _MSC_VER 240 | template 241 | using binder_type = std::_Binder<_Ret, _Fx, _Types...>; 242 | #else 243 | #error "Unknown Compiler on Windows" 244 | #endif 245 | #elif __APPLE__ 246 | #error "Unknown platform" 247 | #if TARGET_IPHONE_SIMULATOR // iOS Simulator 248 | #error "Unknown platform" 249 | #elif TARGET_OS_IPHONE // iOS device 250 | #error "Unknown platform" 251 | #elif TARGET_OS_MAC // Other kinds of Mac OS 252 | #error "Unknown platform" 253 | #else 254 | #error "Unknown Apple platform" 255 | #endif 256 | #elif __ANDROID__ // android 257 | template 258 | using binder_type = std::__bind_r<_Rp, _Fp, _BoundArgs...>; 259 | 260 | template 261 | using binder2_type = std::__bind<_Fp, _BoundArgs...>; 262 | 263 | //std::bind() 264 | template 265 | struct invoke_traits > : public invoke_traits::type> 266 | { 267 | /* 268 | using bind_type = binder2_type<_Fx, _Types...>; 269 | 270 | using result_type = typename bind_type::result_type; 271 | using type = result_type(_Types...); 272 | using args_tuple_type = std::tuple<_Types...>; //将函数参作包装成tuple的类型 273 | using std_function_type = std::function; //对应的std::function<>类 274 | 275 | using funptr_type = std::false_type; //不是函数指针 276 | using memfun_type = std::false_type; //不是成员函数 277 | using functor_type = std::true_type; //是仿函数 278 | 279 | using callee_type = bind_type; 280 | using this_args_type = bind_type; 281 | 282 | enum { args_size = sizeof...(_Types) }; //函数参数个数 283 | 284 | //通过指定的索引获取函数参数的类型 285 | template 286 | struct args_element 287 | { 288 | static_assert(_Idx < args_size, "index is out of range, index must less than sizeof _Args"); 289 | using type = typename std::tuple_element<_Idx, args_tuple_type>::type; 290 | }; 291 | 292 | //调用此类函数的辅助方法 293 | template 294 | static inline decltype(auto) Invoke_(const _Fx2 & f, _Types&&... args, _Rest...) 295 | { 296 | return f(std::forward<_Types>(args)...); 297 | } 298 | */ 299 | }; 300 | template 301 | struct invoke_traits &> : public invoke_traits > 302 | { 303 | }; 304 | template 305 | struct invoke_traits &&> : public invoke_traits > 306 | { 307 | }; 308 | 309 | #elif __linux__ // linux 310 | #error "Unknown platform" 311 | #elif __unix__ // all unices not caught above // Unix 312 | #error "Unknown platform" 313 | #elif defined(_POSIX_VERSION) // POSIX 314 | #error "Unknown platform" 315 | #else 316 | #error "Unknown platform" 317 | #endif 318 | 319 | //std::bind() 320 | template 321 | struct invoke_traits > : public invoke_traits::type> 322 | { 323 | /* 324 | using bind_type = binder_type<_Ret, _Fx, _Types...>; 325 | 326 | using result_type = typename bind_type::result_type; 327 | using type = result_type(_Types...); 328 | using args_tuple_type = std::tuple<_Types...>; //将函数参作包装成tuple的类型 329 | using std_function_type = std::function; //对应的std::function<>类 330 | 331 | using funptr_type = std::false_type; //不是函数指针 332 | using memfun_type = std::false_type; //不是成员函数 333 | using functor_type = std::true_type; //是仿函数 334 | 335 | using callee_type = bind_type; 336 | using this_args_type = bind_type; 337 | 338 | enum { args_size = sizeof...(_Types) }; //函数参数个数 339 | 340 | //通过指定的索引获取函数参数的类型 341 | template 342 | struct args_element 343 | { 344 | static_assert(_Idx < args_size, "index is out of range, index must less than sizeof _Args"); 345 | using type = typename std::tuple_element<_Idx, args_tuple_type>::type; 346 | }; 347 | 348 | //调用此类函数的辅助方法 349 | template 350 | static inline decltype(auto) Invoke_(const _Fx2 & f, _Types&&... args, _Rest...) 351 | { 352 | return f(std::forward<_Types>(args)...); 353 | } 354 | */ 355 | }; 356 | template 357 | struct invoke_traits &> : public invoke_traits > 358 | { 359 | }; 360 | template 361 | struct invoke_traits &&> : public invoke_traits > 362 | { 363 | }; 364 | } 365 | 366 | } 367 | -------------------------------------------------------------------------------- /include/task_node.inl: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #ifdef _WIN32 5 | #include "task_node_win32.inl" 6 | #elif __APPLE__ 7 | #include "TargetConditionals.h" 8 | #if TARGET_IPHONE_SIMULATOR // iOS Simulator 9 | #include "task_node_ios.inl" 10 | #elif TARGET_OS_IPHONE // iOS device 11 | #include "task_node_ios.inl" 12 | #elif TARGET_OS_MAC // Other kinds of Mac OS 13 | #include "task_node_ios.inl" 14 | #else 15 | #error "Unknown Apple platform" 16 | #endif 17 | #elif __ANDROID__ // android 18 | #include "task_node_android.inl" 19 | #elif __linux__ // linux 20 | #include "task_node_linux.inl" 21 | #elif __unix__ // all unices not caught above // Unix 22 | #include "task_node_unix.inl" 23 | #elif defined(_POSIX_VERSION) // POSIX 24 | #include "task_node_posix.inl" 25 | #else 26 | #error "Unknown platform" 27 | #endif 28 | 29 | namespace lib_shark_task 30 | { 31 | //在task_state_result基础上,提供操作执行当前任务节点,执行下一个任务节点的函数 32 | template> 33 | struct node_impl : public node_result_<_Rtype> 34 | { 35 | using task_function = std::remove_reference_t<_Taskf>; 36 | using then_function = std::remove_reference_t<_Thenf>; 37 | protected: 38 | task_function _Thiz; //执行当前任务节点 39 | then_function _Then; //执行下一个任务节点 40 | public: 41 | node_impl(task_function && fn, const task_set_exception_agent_sptr & exp) 42 | : node_result_<_Rtype>(exp) 43 | , _Thiz(std::forward(fn)) 44 | { 45 | } 46 | node_impl(const task_function & fn, const task_set_exception_agent_sptr & exp) 47 | : node_result_<_Rtype>(exp) 48 | , _Thiz(fn) 49 | { 50 | } 51 | node_impl(const task_set_exception_agent_sptr & exp) 52 | : node_result_<_Rtype>(exp) 53 | { 54 | } 55 | 56 | node_impl(node_impl && _Right) = delete; 57 | node_impl & operator = (node_impl && _Right) = delete; 58 | node_impl(const node_impl & _Right) = delete; 59 | node_impl & operator = (const node_impl & _Right) = delete; 60 | 61 | void break_link() 62 | { 63 | _Thiz = nullptr; 64 | _Then = nullptr; 65 | } 66 | 67 | void invoke_then_if() 68 | { 69 | if (!this->_Ready) 70 | return; 71 | 72 | then_function fn = this->_Move_then(); 73 | if (!fn) 74 | return; 75 | 76 | try 77 | { 78 | detail::_Apply_then(fn, std::move(this->_Get_value())); 79 | } 80 | catch (...) 81 | { 82 | this->_Set_Agent_exception(std::current_exception()); 83 | } 84 | } 85 | 86 | template 87 | void _Set_then_if(_NextFx && fn) 88 | { 89 | this->_Set_retrieved(); 90 | 91 | if (this->_Ready) 92 | { 93 | try 94 | { 95 | detail::_Apply_then2(std::forward<_NextFx>(fn), std::move(this->_Get_value())); 96 | } 97 | catch (...) 98 | { 99 | this->_Set_Agent_exception(std::current_exception()); 100 | } 101 | } 102 | else 103 | { 104 | std::unique_lock _Lock(this->_Mtx()); 105 | this->_Then = then_function{ std::forward<_NextFx>(fn) }; 106 | } 107 | } 108 | protected: 109 | //取执行当前任务节点的函数,只能取一次。线程安全 110 | inline task_function _Move_thiz() 111 | { 112 | std::unique_lock _Lock(this->_Mtx()); 113 | return std::move(_Thiz); //强迫只能调用一次 114 | } 115 | //取执行下一个任务节点的函数,只能取一次。线程安全 116 | inline then_function _Move_then() 117 | { 118 | std::unique_lock _Lock(this->_Mtx()); 119 | return std::move(_Then); //强迫只能调用一次 120 | } 121 | }; 122 | 123 | //任务节点 124 | //_Rtype是本节点的返回值类型 125 | //_PrevArgs...是上一个任务节点的返回值(如果上一个节点返回值是std::tuple<>,则_PrevArgs是将tuple接包后的参数列表) 126 | template 127 | struct task_node : public node_impl, 128 | std::function(_PrevArgs...)>, 129 | detail::unpack_tuple_fn_t> > 130 | { 131 | using result_type = std::remove_reference_t<_Rtype>; //本节点的结果的类型 132 | using result_tuple = detail::package_tuple_t; //本节点的结果打包成tuple<>后的类型 133 | using args_tuple_type = std::tuple<_PrevArgs...>; //本节点的入参打包成tuple<>后的类型 134 | 135 | using base_type = node_impl, 136 | std::function(_PrevArgs...)>, 137 | detail::unpack_tuple_fn_t> >; 138 | using task_function = typename base_type::task_function; 139 | using then_function = typename base_type::then_function; 140 | using base_type::base_type; 141 | 142 | template 143 | bool invoke_thiz(_PrevArgs2&&... args) 144 | { 145 | static_assert(sizeof...(_PrevArgs2) >= std::tuple_size::value, ""); 146 | 147 | try 148 | { 149 | task_function fn = this->_Move_thiz(); 150 | this->_Set_value(detail::_Apply_then(fn, std::forward<_PrevArgs2>(args)...)); 151 | this->_Ready = true; 152 | } 153 | catch (...) 154 | { 155 | this->_Set_Agent_exception(std::current_exception()); 156 | } 157 | 158 | return this->_Ready; 159 | } 160 | 161 | template 162 | bool invoke_thiz_tuple(_PrevTuple&& args) 163 | { 164 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 165 | 166 | try 167 | { 168 | task_function fn = this->_Move_thiz(); 169 | this->_Set_value(detail::_Apply_then(fn, std::forward<_PrevTuple>(args))); 170 | this->_Ready = true; 171 | } 172 | catch (...) 173 | { 174 | this->_Set_Agent_exception(std::current_exception()); 175 | } 176 | 177 | return this->_Ready; 178 | } 179 | }; 180 | 181 | template 182 | struct task_node : public node_impl> 183 | { 184 | using result_type = void; //本节点的结果的类型 185 | using result_tuple = std::tuple<>; //本节点的结果打包成tuple<>后的类型 186 | using args_tuple_type = std::tuple<_PrevArgs...>; //本节点的入参打包成tuple<>后的类型 187 | 188 | using base_type = node_impl>; 189 | using task_function = typename base_type::task_function; 190 | using then_function = typename base_type::then_function; 191 | using base_type::base_type; 192 | 193 | template 194 | bool invoke_thiz(_PrevArgs2&&... args) 195 | { 196 | static_assert(sizeof...(_PrevArgs2) >= std::tuple_size::value, ""); 197 | 198 | try 199 | { 200 | task_function fn = this->_Move_thiz(); 201 | detail::_Apply_then(fn, std::forward<_PrevArgs2>(args)...); 202 | this->_Set_value(0); 203 | this->_Ready = true; 204 | } 205 | catch (...) 206 | { 207 | this->_Set_Agent_exception(std::current_exception()); 208 | } 209 | 210 | return this->_Ready; 211 | } 212 | 213 | template 214 | bool invoke_thiz_tuple(_PrevTuple&& args) 215 | { 216 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 217 | 218 | try 219 | { 220 | task_function fn = this->_Move_thiz(); 221 | detail::_Apply_then(fn, std::forward<_PrevTuple>(args)); 222 | this->_Set_value(0); 223 | this->_Ready = true; 224 | } 225 | catch (...) 226 | { 227 | this->_Set_Agent_exception(std::current_exception()); 228 | } 229 | return this->_Ready; 230 | } 231 | }; 232 | 233 | template 234 | struct task_node<_Rtype, std::tuple<_PrevArgs...>> : public task_node<_Rtype, _PrevArgs...> 235 | { 236 | using task_node<_Rtype, _PrevArgs...>::task_node; 237 | }; 238 | } 239 | -------------------------------------------------------------------------------- /include/task_node_android.inl: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | namespace lib_shark_task 5 | { 6 | #ifdef ANDROID 7 | namespace detail 8 | { 9 | template 10 | struct __assoc_state_hack : public std::__assoc_state<_Rtype> 11 | { 12 | std::mutex & _Mtx() 13 | { 14 | return this->__mut_; 15 | } 16 | _Rtype _Get_value() 17 | { 18 | std::unique_lock __lk(this->__mut_); 19 | 20 | this->__state_ |= std::__assoc_state<_Rtype>::__future_attached; 21 | return std::move(*reinterpret_cast<_Rtype*>(&this->__value_)); 22 | } 23 | _Rtype & _Peek_value() 24 | { 25 | return *reinterpret_cast<_Rtype*>(&this->__value_); 26 | } 27 | }; 28 | 29 | //这是std::future的孪生兄弟 30 | //由于std::future只有私有的从__assoc_state初始化的构造函数 31 | //故由孪生兄弟来提供公开的从std::__assoc_state初始化的构造函数 32 | //然后使用C++的联合构造出std::future 33 | template 34 | struct future_twin 35 | { 36 | std::__assoc_state<_Rp>* __state_; 37 | 38 | //保持跟std::future的构造函数行为一致 39 | explicit future_twin(std::__assoc_state<_Rp>* __state) 40 | : __state_(__state) 41 | { 42 | #ifndef _LIBCPP_NO_EXCEPTIONS 43 | if (__state_->__has_future_attached()) 44 | throw std::future_error(std::make_error_code(std::future_errc::future_already_retrieved)); 45 | #endif 46 | __state_->__add_shared(); 47 | __state_->__set_future_attached(); 48 | } 49 | }; 50 | 51 | //使用C++的联合来黑客std::future的构造函数 52 | template 53 | union future_hack 54 | { 55 | //保证future_twin与std::future的尺寸一致,这样来保证其二进制数据一致 56 | static_assert(sizeof(std::future<_Rp>) == sizeof(future_twin<_Rp>), ""); 57 | 58 | std::future<_Rp> __f_stl; 59 | future_twin<_Rp> __f_hack; 60 | 61 | explicit future_hack(std::__assoc_state<_Rp>* __state) 62 | { 63 | new(&__f_hack) future_twin<_Rp>(__state); 64 | } 65 | ~future_hack() 66 | { 67 | __f_stl.~future(); 68 | } 69 | 70 | //将构造得到的std::future移动出去。剩下的就是无用的数据了,爱咋咋地 71 | std::future<_Rp> move() 72 | { 73 | return std::move(__f_stl); 74 | } 75 | }; 76 | } 77 | 78 | //实现存取任务节点的结果。 79 | //存取结果值 80 | //返回对应的future对象 81 | //提供mutex实例 82 | template 83 | struct node_result_ : public std::enable_shared_from_this> 84 | , public task_set_exception 85 | { 86 | protected: 87 | detail::__assoc_state_hack<_Rtype>* _State; //通过clang的future内部的__assoc_state来实现future功能。今后考虑自己做future 88 | task_set_exception_agent_sptr _Exception; //传递异常的代理接口 89 | std::atomic _Ready{ false }; //结果是否已经准备好了,线程安全 90 | 91 | public: 92 | node_result_(const task_set_exception_agent_sptr & exp) 93 | : _State(new detail::__assoc_state_hack<_Rtype>) 94 | , _Exception(exp) 95 | { 96 | } 97 | ~node_result_() 98 | { 99 | if (_State) 100 | _State->__release_shared(); 101 | } 102 | node_result_(node_result_ && _Right) = default; 103 | node_result_ & operator = (node_result_ && _Right) = default; 104 | node_result_(const node_result_ & _Right) = delete; 105 | node_result_ & operator = (const node_result_ & _Right) = delete; 106 | 107 | //此函数只应该被调用一次。 108 | //并且外部没有调用过 _Set_then_if() 109 | //内部不能调用过_Get_value() 110 | inline std::future<_Rtype> get_future() 111 | { 112 | assert(!is_retrieved()); 113 | 114 | detail::future_hack<_Rtype> f{ _State }; 115 | return f.move(); 116 | } 117 | 118 | //结果是否已经准备好了,线程安全 119 | inline bool is_ready() const 120 | { 121 | return _Ready; 122 | } 123 | 124 | //是否已经调用过get_future/_Set_then_if/_Get_value之一 125 | inline bool is_retrieved() const 126 | { 127 | return _State->__has_future_attached(); 128 | } 129 | private: 130 | //task_set_exception 接口的实现 131 | virtual void _Set_exception(std::exception_ptr && val) override 132 | { 133 | _State->set_exception(std::forward(val)); 134 | } 135 | protected: 136 | inline std::__assoc_state<_Rtype> * _Ptr() const 137 | { 138 | return _State; 139 | } 140 | //设定调用过get_future/_Set_then_if/_Get_value之一 141 | void _Set_retrieved() 142 | { 143 | if (_State->__has_future_attached()) 144 | throw std::future_error(std::make_error_code(std::future_errc::no_state)); 145 | _State->__set_future_attached(); 146 | } 147 | //获取存的值,只能调用一次 148 | inline _Rtype _Get_value() 149 | { 150 | return _State->_Get_value(); 151 | } 152 | inline _Rtype & _Peek_value() 153 | { 154 | return _State->_Peek_value(); 155 | } 156 | //设置存的值 157 | template 158 | inline void _Set_value(_Ty2 && val) 159 | { 160 | _State->set_value(std::forward<_Ty2>(val)); 161 | } 162 | inline void _Set_value() 163 | { 164 | _State->__assoc_sub_state::set_value(); 165 | } 166 | //设置异常 167 | inline void _Set_Agent_exception(std::exception_ptr && val) 168 | { 169 | _Exception->_Set_exception(std::forward(val)); 170 | } 171 | //对外提供的mutex实例 172 | inline std::mutex & _Mtx() const 173 | { 174 | return _State->_Mtx(); 175 | } 176 | }; 177 | 178 | #endif 179 | } 180 | -------------------------------------------------------------------------------- /include/task_node_ios.inl: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | namespace lib_shark_task 5 | { 6 | #ifdef __APPLE__ 7 | //实现存取任务节点的结果。 8 | //存取结果值 9 | //返回对应的future对象 10 | //提供mutex实例 11 | template 12 | struct node_result_ : public std::enable_shared_from_this> 13 | , public task_set_exception 14 | { 15 | protected: 16 | std::_State_manager<_Rtype> _State; //通过VS的future内部的_State_manager来实现future功能。今后考虑自己做future 17 | task_set_exception_agent_sptr _Exception; //传递异常的代理接口 18 | std::atomic _Ready = false; //结果是否已经准备好了,线程安全 19 | bool _Future_retrieved = false; //是否已经调用过get_future(),或者已经调用过_Set_then_if 20 | 21 | public: 22 | node_result_(const task_set_exception_agent_sptr & exp) 23 | : _State(new std::_Associated_state<_Rtype>, true) 24 | , _Exception(exp) 25 | { 26 | } 27 | node_result_(node_result_ && _Right) = default; 28 | node_result_ & operator = (node_result_ && _Right) = default; 29 | node_result_(const node_result_ & _Right) = delete; 30 | node_result_ & operator = (const node_result_ & _Right) = delete; 31 | 32 | //此函数只应该被调用一次。 33 | //并且外部没有调用过 _Set_then_if() 34 | //内部不能调用过_Get_value() 35 | inline std::future<_Rtype> get_future() 36 | { 37 | assert(!is_retrieved()); 38 | 39 | _Set_retrieved(); 40 | return (std::future<_Rtype>(_State, std::_Nil())); 41 | } 42 | 43 | //结果是否已经准备好了,线程安全 44 | inline bool is_ready() const 45 | { 46 | return _Ready; 47 | } 48 | 49 | //是否已经调用过get_future/_Set_then_if/_Get_value之一 50 | inline bool is_retrieved() const 51 | { 52 | std::unique_lock _Lock(_Mtx()); 53 | 54 | return _Ptr()->_Already_retrieved() || _Future_retrieved; 55 | } 56 | private: 57 | //task_set_exception 接口的实现 58 | virtual void _Set_exception(std::exception_ptr && val) override 59 | { 60 | _Ptr()->_Set_exception(std::forward(val), false); 61 | } 62 | protected: 63 | inline std::_Associated_state<_Rtype> * _Ptr() const 64 | { 65 | return _State._Ptr(); 66 | } 67 | //设定调用过get_future/_Set_then_if/_Get_value之一 68 | void _Set_retrieved() 69 | { 70 | std::unique_lock _Lock(_Mtx()); 71 | 72 | if (!_State.valid()) 73 | std::_Throw_future_error( 74 | std::make_error_code(std::future_errc::no_state)); 75 | if (_Future_retrieved) 76 | std::_Throw_future_error( 77 | std::make_error_code(std::future_errc::future_already_retrieved)); 78 | _Future_retrieved = true; 79 | } 80 | //获取存的值,只能调用一次 81 | inline _Rtype && _Get_value() 82 | { 83 | return std::move(_Ptr()->_Get_value(true)); 84 | } 85 | inline _Rtype & _Peek_value() 86 | { 87 | return _Ptr()->_Result; 88 | } 89 | //设置存的值 90 | template 91 | inline void _Set_value(_Ty2 && val) 92 | { 93 | _Ptr()->_Set_value(std::forward<_Ty2>(val), false); 94 | } 95 | //设置异常 96 | inline void _Set_Agent_exception(std::exception_ptr && val) 97 | { 98 | _Exception->_Set_exception(std::forward(val)); 99 | } 100 | //对外提供的mutex实例 101 | inline std::mutex & _Mtx() const 102 | { 103 | return _Ptr()->_Mtx; 104 | } 105 | }; 106 | 107 | #endif 108 | } 109 | -------------------------------------------------------------------------------- /include/task_node_win32.inl: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | #include 4 | #include 5 | 6 | namespace lib_shark_task 7 | { 8 | #if defined(_MSC_VER) 9 | 10 | #if LIBTASK_DEBUG_MEMORY 11 | extern std::atomic g_node_counter; 12 | #endif 13 | 14 | //实现存取任务节点的结果。 15 | //存取结果值 16 | //返回对应的future对象 17 | //提供mutex实例 18 | template 19 | struct node_result_ : public std::enable_shared_from_this> 20 | , public task_set_exception 21 | { 22 | protected: 23 | std::_State_manager<_Rtype> _State; //通过VS的future内部的_State_manager来实现future功能。今后考虑自己做future 24 | task_set_exception_agent_sptr _Exception; //传递异常的代理接口 25 | std::atomic _Ready = false; //结果是否已经准备好了,线程安全 26 | bool _Future_retrieved = false; //是否已经调用过get_future(),或者已经调用过_Set_then_if 27 | 28 | public: 29 | node_result_(const task_set_exception_agent_sptr & exp) 30 | : _State(new std::_Associated_state<_Rtype>, true) 31 | , _Exception(exp) 32 | { 33 | #if LIBTASK_DEBUG_MEMORY 34 | long node_counter = ++g_node_counter; 35 | 36 | char buffer[256]; 37 | sprintf_s(buffer, sizeof(buffer), "node ctor:%p, counter=%d\r\n", this, node_counter); 38 | ::OutputDebugStringA(buffer); 39 | #endif 40 | } 41 | #if LIBTASK_DEBUG_MEMORY 42 | ~node_result_() 43 | { 44 | long node_counter = --g_node_counter; 45 | 46 | char buffer[256]; 47 | sprintf_s(buffer, sizeof(buffer), "node dtor:%p, counter=%d\r\n", this, node_counter); 48 | ::OutputDebugStringA(buffer); 49 | } 50 | #endif 51 | node_result_(node_result_ && _Right) = delete; 52 | node_result_ & operator = (node_result_ && _Right) = delete; 53 | node_result_(const node_result_ & _Right) = delete; 54 | node_result_ & operator = (const node_result_ & _Right) = delete; 55 | 56 | //此函数只应该被调用一次。 57 | //并且外部没有调用过 _Set_then_if() 58 | //内部不能调用过_Get_value() 59 | inline std::future<_Rtype> get_future() 60 | { 61 | assert(!is_retrieved()); 62 | 63 | _Set_retrieved(); 64 | return (std::future<_Rtype>(_State, std::_Nil())); 65 | } 66 | 67 | //结果是否已经准备好了,线程安全 68 | inline bool is_ready() const 69 | { 70 | return _Ready; 71 | } 72 | 73 | //是否已经调用过get_future/_Set_then_if/_Get_value之一 74 | inline bool is_retrieved() const 75 | { 76 | std::unique_lock _Lock(_Mtx()); 77 | 78 | return _Ptr()->_Already_retrieved() || _Future_retrieved; 79 | } 80 | private: 81 | //task_set_exception 接口的实现 82 | virtual void _Set_exception(std::exception_ptr && val) override 83 | { 84 | _Ptr()->_Set_exception(std::forward(val), false); 85 | } 86 | protected: 87 | inline std::_Associated_state<_Rtype> * _Ptr() const 88 | { 89 | return _State._Ptr(); 90 | } 91 | //设定调用过get_future/_Set_then_if/_Get_value之一 92 | void _Set_retrieved() 93 | { 94 | std::unique_lock _Lock(_Mtx()); 95 | 96 | if (!_State.valid()) 97 | std::_Throw_future_error( 98 | std::make_error_code(std::future_errc::no_state)); 99 | if (_Future_retrieved) 100 | std::_Throw_future_error( 101 | std::make_error_code(std::future_errc::future_already_retrieved)); 102 | _Future_retrieved = true; 103 | } 104 | //获取存的值,只能调用一次 105 | inline _Rtype && _Get_value() 106 | { 107 | return std::move(_Ptr()->_Get_value(true)); 108 | } 109 | inline _Rtype & _Peek_value() 110 | { 111 | return _Ptr()->_Result; 112 | } 113 | //设置存的值 114 | template 115 | inline void _Set_value(_Ty2 && val) 116 | { 117 | _Ptr()->_Set_value(std::forward<_Ty2>(val), false); 118 | } 119 | inline void _Set_value() 120 | { 121 | _Ptr()->_Set_value(false); 122 | } 123 | //设置异常 124 | inline void _Set_Agent_exception(std::exception_ptr && val) 125 | { 126 | _Exception->_Set_exception(std::forward(val)); 127 | } 128 | //对外提供的mutex实例 129 | inline std::mutex & _Mtx() const 130 | { 131 | return _Ptr()->_Mtx; 132 | } 133 | }; 134 | #else 135 | #error "Unknown Compiler on Windows" 136 | #endif 137 | } 138 | -------------------------------------------------------------------------------- /include/task_utils.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if !_HAS_CXX17 4 | #include 5 | 6 | namespace std 7 | { 8 | #pragma warning(push) 9 | #pragma warning(disable: 4100) // TRANSITION, VSO#181496, unreferenced formal parameter 10 | template 11 | constexpr decltype(auto) _Apply_impl(_Callable&& _Obj, _Tuple&& _Tpl, index_sequence<_Indices...>) 12 | { // invoke _Obj with the elements of _Tpl 13 | return (invoke(forward<_Callable>(_Obj), get<_Indices>(forward<_Tuple>(_Tpl))...)); 14 | } 15 | #pragma warning(pop) 16 | 17 | template 18 | constexpr decltype(auto) apply(_Callable&& _Obj, _Tuple&& _Tpl) 19 | { // invoke _Obj with the elements of _Tpl 20 | return (_Apply_impl(forward<_Callable>(_Obj), forward<_Tuple>(_Tpl), make_index_sequence>::value>{})); 21 | } 22 | } 23 | #endif 24 | 25 | namespace std 26 | { 27 | template 28 | inline void tuple_for_each_(_Tp && t, const _Fx& f, index_sequence) 29 | { 30 | (void)initializer_list{ (f(get(t)), 0)...}; 31 | } 32 | //遍历tuple里的所有元素 33 | template 34 | inline void for_each(_Tp && t, const _Fx& f) 35 | { 36 | using tuple_noref = remove_reference_t<_Tp>; 37 | using tuple_type = remove_cv_t; 38 | 39 | tuple_for_each_(forward<_Tp>(t), f, 40 | make_index_sequence::value>{}); 41 | } 42 | } 43 | 44 | template 45 | struct DEBUG_TYPE; -------------------------------------------------------------------------------- /include/task_when_all.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "task_when_node.inl" 3 | #include "task_when_all.inl" 4 | #include "task_when_allv.inl" 5 | 6 | namespace lib_shark_task 7 | { 8 | namespace detail 9 | { 10 | template 11 | void when_all_impl2(_Anode * all_node, size_t node_idx) 12 | { 13 | } 14 | 15 | template 16 | void when_all_impl2(_Anode * all_node, size_t node_idx, _Task && tf, _TaskRest&&... rest) 17 | { 18 | when_wait_one_impl<_Idx>(all_node, node_idx, tf); 19 | 20 | using tuple_type = decltype(declval_task_last_node_result_tuple<_Task>()); 21 | when_all_impl2<_Idx + std::tuple_size::value>(all_node, ++node_idx, std::forward<_TaskRest>(rest)...); 22 | } 23 | 24 | template 25 | void when_all_impl(_Anode * all_node, _Ttuple & tasks, std::index_sequence) 26 | { 27 | when_all_impl2<0>(all_node, 0, std::get(tasks)...); 28 | } 29 | } 30 | 31 | //等待多个任务完成 32 | //多个任务的结果,放在一个拼合的tuple<>里 33 | //首先做一个全新的task 34 | // task_all_node::invoke_thiz 主要负责调用所有的任务,以便于开始任务 35 | // 为每个任务造一个task_when_one。 36 | // task_when_one 负责将结果放入到 task_all_node的拼合tuple<>里,然后通知 task_all_node 有一个任务完成 37 | // task_all_node 在所有任务完成后,调用invoke_then_if 38 | // 39 | template 40 | auto when_all(_Task&& tfirst, _TaskRest&&... rest) 41 | { 42 | static_assert(detail::is_task<_Task>::value, "use 'make_task' or 'marshal_task' to create a task"); 43 | (void)std::initializer_list{detail::check_task_type<_TaskRest>()...}; 44 | 45 | using cated_task_t = std::tuple, std::remove_reference_t<_TaskRest>...>; 46 | using cated_result_t = decltype(std::tuple_cat(detail::declval_task_last_node_result_tuple<_Task>(), detail::declval_task_last_node_result_tuple<_TaskRest>()...)); 47 | 48 | task_set_exception_agent_sptr exp = tfirst._Get_exception_agent(); 49 | 50 | using first_node_type = task_all_node; 51 | auto st_first = std::make_shared(exp, std::move(tfirst), std::move(rest)...); 52 | exp->_Impl = st_first.get(); 53 | 54 | detail::when_all_impl(st_first.get(), *st_first->_All_tasks, std::make_index_sequence::value>{}); 55 | 56 | return task{exp, st_first, std::move(st_first)}; 57 | } 58 | 59 | //等待多个任务完成 60 | //多个任务的结果类型肯定是一致的,数量运行时才能确定。故结果放在vector<>里 61 | //首先做一个全新的task 62 | // task_allv_node::invoke_thiz 主要负责调用所有的任务,以便于开始任务。在任务为空时,当作全部任务已经完成处理。 63 | // 为每个任务造一个task_when_one。 64 | // task_when_one 负责将结果放入到 task_allv_node的vector<>里,然后通知 task_allv_node 有一个任务完成 65 | // task_allv_node 在所有任务完成后,调用invoke_then_if 66 | // 67 | template())>::value, decltype(*std::declval<_Iter>())>> 68 | auto when_all(_Iter _First, _Iter _Last) 69 | { 70 | using task_type = typename std::remove_reference<_Fty>::type; 71 | static_assert(detail::is_task::value, "_Iter must be a iterator of task container"); 72 | using result_type = decltype(detail::declval_task_last_node_result_tuple()); 73 | 74 | return detail::when_iter >(_First, _Last); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /include/task_when_all.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "task_when_node.inl" 3 | 4 | namespace lib_shark_task 5 | { 6 | //when_all(_Task1, _Task2, ...) 7 | //适用于等待一组不同类型的任务全部完成。 8 | //由于返回结果类型不同,将这些任务的返回值,按照先后顺序,打包成一个std::tuple。 9 | // 10 | //_All_tasks 由于任务的类型不同,导致只能用一个std::tuple<>来保存 11 | //因此,invoke_thiz/invoke_thiz_tuple采用for_each(std::tuple<>)语法来调用_All_tasks 12 | // 13 | //由于返回结果不同 14 | //_Set_value_partial/_Set_value_partial_t 将结果,按照_Idx指示放在std::tuple对应位置上。 15 | // 16 | //_On_result 检测所有结果已经获得,则调用invoke_then_if 17 | template 18 | struct task_all_node : public node_impl, std::function, std::function> 19 | { 20 | using base_type = node_impl, std::function, std::function>; 21 | using this_type = task_all_node<_Ttuple, _ResultArgs...>; 22 | 23 | using element_type = std::tuple<_ResultArgs...>; //等待的节点的结果类型 24 | using result_type = std::tuple<_ResultArgs...>; //本节点的结果的类型 25 | using result_tuple = result_type; //本节点的结果打包成tuple<>后的类型 26 | using args_tuple_type = std::tuple<>; //本节点的入参打包成tuple<>后的类型 27 | 28 | using task_tuple = detail::package_tuple_t<_Ttuple>; 29 | std::shared_ptr _All_tasks; 30 | 31 | template 32 | task_all_node(const task_set_exception_agent_sptr & exp, _Tasks&&... ts) 33 | : base_type(exp) 34 | , _All_tasks(std::make_shared(std::forward<_Tasks>(ts)...)) 35 | , _Result_count(std::tuple_size<_Ttuple>::value) 36 | { 37 | } 38 | task_all_node(task_all_node && _Right) = default; 39 | task_all_node & operator = (task_all_node && _Right) = default; 40 | task_all_node(const task_all_node & _Right) = delete; 41 | task_all_node & operator = (const task_all_node & _Right) = delete; 42 | 43 | private: 44 | std::atomic _Result_count; 45 | public: 46 | template 47 | void _Set_value_partial(size_t, _PrevArgs2&&... args) 48 | { 49 | std::unique_lock _Lock(this->_Mtx()); 50 | detail::_Fill_to_tuple<_Idx>(this->_Peek_value(), std::forward<_PrevArgs2>(args)...); 51 | } 52 | template 53 | void _Set_value_partial_t(size_t, _PrevTuple&& args) 54 | { 55 | std::unique_lock _Lock(this->_Mtx()); 56 | detail::_Move_to_tuple<_Idx>(this->_Peek_value(), std::forward<_PrevTuple>(args)); 57 | } 58 | 59 | void _On_result(size_t) 60 | { 61 | if (--_Result_count == 0) 62 | { 63 | this->_Set_value(); 64 | this->_Ready = true; 65 | this->invoke_then_if(); 66 | } 67 | } 68 | 69 | void break_link() 70 | { 71 | base_type::break_link(); 72 | _All_tasks = nullptr; 73 | } 74 | 75 | template 76 | bool invoke_thiz(Args2&&... args) 77 | { 78 | static_assert(sizeof...(Args2) >= std::tuple_size::value, ""); 79 | 80 | try 81 | { 82 | std::unique_lock _Lock(this->_Mtx()); 83 | auto all_task = std::move(_All_tasks); 84 | _Lock.unlock(); 85 | 86 | for_each(*all_task, [&](auto & ts) 87 | { 88 | ts(args...); 89 | }); 90 | } 91 | catch (...) 92 | { 93 | this->_Set_Agent_exception(std::current_exception()); 94 | } 95 | 96 | return false; 97 | } 98 | 99 | template 100 | bool invoke_thiz_tuple(_PrevTuple&& args) 101 | { 102 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 103 | 104 | try 105 | { 106 | std::unique_lock _Lock(this->_Mtx()); 107 | auto all_task = std::move(_All_tasks); 108 | _Lock.unlock(); 109 | 110 | for_each(*all_task, [&](auto & ts) 111 | { 112 | std::apply(ts, args); 113 | }); 114 | } 115 | catch (...) 116 | { 117 | this->_Set_Agent_exception(std::current_exception()); 118 | } 119 | 120 | return false; 121 | } 122 | }; 123 | template 124 | struct task_all_node<_Ttuple, std::tuple<_ResultArgs...>> : public task_all_node<_Ttuple, _ResultArgs...> 125 | { 126 | using task_all_node<_Ttuple, _ResultArgs...>::task_all_node; 127 | }; 128 | } 129 | -------------------------------------------------------------------------------- /include/task_when_allv.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "task_when_node.inl" 4 | 5 | namespace lib_shark_task 6 | { 7 | //when_all(_Iter begin, _Iter end) 8 | //适用于通过迭代子等待一组任务全部完成。 9 | //因此,所有的任务的返回值必然相同,结果是std::vector。 10 | // 11 | //_All_tasks 由于任务的类型相同,所以使用一个std::vector<>来保存。 12 | //因此,invoke_thiz/invoke_thiz_tuple采用ranged for(std::vector<>)语法来调用_All_tasks 13 | // 14 | //由于返回结果相同 15 | //_Set_value_partial/_Set_value_partial_t 将结果,按照idx指示放在std::vector对应位置上。 16 | // 17 | //_On_result 检测所有结果已经获得,则调用invoke_then_if 18 | template 19 | struct task_allv_node : public node_impl, std::function, std::function)>> 20 | { 21 | using base_type = node_impl, std::function, std::function)>>; 22 | using this_type = task_allv_node<_Ttype, _ResultArgs>; 23 | 24 | using element_type = _ResultArgs; 25 | using result_type = std::vector<_ResultArgs>; //本节点的结果的类型 26 | using result_tuple = std::tuple; //本节点的结果打包成tuple<>后的类型 27 | using args_tuple_type = std::tuple<>; //本节点的入参打包成tuple<>后的类型 28 | 29 | using task_vector = std::vector<_Ttype>; 30 | task_vector _All_tasks; 31 | 32 | template 33 | task_allv_node(const task_set_exception_agent_sptr & exp, _Iter _First, _Iter _Last) 34 | : base_type(exp) 35 | { 36 | _All_tasks.reserve(std::distance(_First, _Last)); 37 | for (; _First != _Last; ++_First) 38 | _All_tasks.emplace_back(std::move(*_First)); 39 | 40 | _Result_count = _All_tasks.size(); 41 | this->_Peek_value().resize(_Result_count); 42 | } 43 | task_allv_node(task_allv_node && _Right) = default; 44 | task_allv_node & operator = (task_allv_node && _Right) = default; 45 | task_allv_node(const task_allv_node & _Right) = delete; 46 | task_allv_node & operator = (const task_allv_node & _Right) = delete; 47 | 48 | private: 49 | std::atomic _Result_count; 50 | public: 51 | template 52 | void _Set_value_partial(size_t idx, _PrevArgs2&&... args) 53 | { 54 | assert(idx < this->_Peek_value().size()); 55 | 56 | std::unique_lock _Lock(this->_Mtx()); 57 | detail::_Fill_to_tuple<_Idx>(this->_Peek_value()[idx], std::forward<_PrevArgs2>(args)...); 58 | } 59 | template 60 | void _Set_value_partial_t(size_t idx, _PrevTuple&& args) 61 | { 62 | assert(idx < this->_Peek_value().size()); 63 | 64 | std::unique_lock _Lock(this->_Mtx()); 65 | this->_Peek_value()[idx] = std::forward<_PrevTuple>(args); 66 | } 67 | 68 | void _On_result(size_t) 69 | { 70 | if (--_Result_count == 0) 71 | { 72 | this->_Set_value(); 73 | this->_Ready = true; 74 | this->invoke_then_if(); 75 | } 76 | } 77 | 78 | void break_link() 79 | { 80 | base_type::break_link(); 81 | _All_tasks.clear(); 82 | } 83 | 84 | template 85 | bool invoke_thiz(Args2&&... args) 86 | { 87 | static_assert(sizeof...(Args2) >= std::tuple_size::value, ""); 88 | 89 | try 90 | { 91 | std::unique_lock _Lock(this->_Mtx()); 92 | task_vector all_task = std::move(_All_tasks); 93 | _Lock.unlock(); 94 | 95 | if (all_task.size() > 0) 96 | { 97 | for (auto & ts : all_task) 98 | ts(args...); 99 | } 100 | else 101 | { 102 | this->_Set_value(); 103 | this->_Ready = true; 104 | return true; 105 | } 106 | } 107 | catch (...) 108 | { 109 | this->_Set_Agent_exception(std::current_exception()); 110 | } 111 | 112 | return false; 113 | } 114 | 115 | template 116 | bool invoke_thiz_tuple(_PrevTuple&& args) 117 | { 118 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 119 | 120 | try 121 | { 122 | std::unique_lock _Lock(this->_Mtx()); 123 | task_vector all_task = std::move(_All_tasks); 124 | _Lock.unlock(); 125 | 126 | if (all_task.size() > 0) 127 | { 128 | for (auto & ts : all_task) 129 | std::apply(ts, args); 130 | } 131 | else 132 | { 133 | this->_Set_value(); 134 | this->_Ready = true; 135 | return true; 136 | } 137 | } 138 | catch (...) 139 | { 140 | this->_Set_Agent_exception(std::current_exception()); 141 | } 142 | 143 | return false; 144 | } 145 | }; 146 | template 147 | struct task_allv_node<_Ttuple, std::tuple<_ResultArgs...>> : public task_allv_node<_Ttuple, _ResultArgs...> 148 | { 149 | using task_allv_node<_Ttuple, _ResultArgs...>::task_allv_node; 150 | }; 151 | 152 | } 153 | -------------------------------------------------------------------------------- /include/task_when_any.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "task_when_node.inl" 3 | #include "task_when_any.inl" 4 | #include "task_when_anyv.inl" 5 | #include "task_when_anys.inl" 6 | 7 | namespace lib_shark_task 8 | { 9 | namespace detail 10 | { 11 | template 12 | struct is_all_same; 13 | 14 | template<> 15 | struct is_all_same<> 16 | { 17 | static const bool value = true; 18 | }; 19 | template 20 | struct is_all_same<_Ty> 21 | { 22 | static const bool value = true; 23 | }; 24 | template 25 | struct is_all_same<_First, _Second, _Rest...> 26 | { 27 | static const bool value = std::is_same<_First, _Second>::value && is_all_same<_First, _Rest...>::value; 28 | }; 29 | 30 | template 31 | struct is_tuple_all_same 32 | { 33 | static const bool value = true; 34 | }; 35 | template 36 | struct is_tuple_all_same> 37 | { 38 | static const bool value = is_all_same<_Types...>::value; 39 | }; 40 | 41 | 42 | template 43 | void when_any_impl2(_Anode * all_node, size_t node_idx) 44 | { 45 | } 46 | template 47 | void when_any_impl2(_Anode * all_node, size_t node_idx, _Task && tf, _TaskRest&&... rest) 48 | { 49 | when_wait_one_impl<0>(all_node, node_idx, tf); 50 | when_any_impl2(all_node, ++node_idx, std::forward<_TaskRest>(rest)...); 51 | } 52 | 53 | template 54 | void when_any_impl(_Anode * all_node, _Ttuple & tasks, std::index_sequence) 55 | { 56 | when_any_impl2(all_node, 0, std::get(tasks)...); 57 | } 58 | 59 | //等待多个任务之一完成 60 | //多个任务的结果,类型不完全相同,因此,采用std::any来存结果,最终结果是std::tuple。 61 | //size_t 指示是哪一个任务完成;std::any 是对应的任务的结果,需要通过std::any_cast<>()来获取。 62 | //首先做一个全新的task 63 | // task_any_node::invoke_thiz 主要负责调用所有的任务,以便于开始任务 64 | // 为每个任务造一个task_when_one。 65 | // task_when_one 负责将结果放入到 task_any_node的std::any里,然后通知 task_any_node 有一个任务完成 66 | // task_any_node 在所有任务完成后,调用invoke_then_if 67 | // 68 | template 69 | struct when_any_selector 70 | { 71 | template 72 | static auto when_any(_Task& tfirst, _TaskRest&... rest) 73 | { 74 | using cated_task_t = std::tuple, std::remove_reference_t<_TaskRest>...>; 75 | 76 | task_set_exception_agent_sptr exp = tfirst._Get_exception_agent(); 77 | 78 | using first_node_type = task_any_node; 79 | auto st_first = std::make_shared(exp, std::move(tfirst), std::move(rest)...); 80 | exp->_Impl = st_first.get(); 81 | 82 | when_any_impl(st_first.get(), *st_first->_All_tasks, std::make_index_sequence::value>{}); 83 | 84 | return task{exp, st_first, std::move(st_first)}; 85 | } 86 | }; 87 | 88 | //等待多个任务之一完成 89 | //如果所有任务的返回类型都相同,可以简化为返回(size_t, result_type...) 90 | //首先做一个全新的task 91 | // task_anys_node::invoke_thiz 主要负责调用所有的任务,以便于开始任务 92 | // 为每个任务造一个task_when_one。 93 | // task_when_one 负责将结果放入到 task_anys_node的拼合tuple<>里,然后通知 task_anys_node 有一个任务完成 94 | // task_anys_node 在所有任务完成后,调用invoke_then_if 95 | // 96 | template<> 97 | struct when_any_selector 98 | { 99 | template 100 | static auto when_any(_Task& tfirst, _TaskRest&... rest) 101 | { 102 | using cated_task_t = std::tuple, std::remove_reference_t<_TaskRest>...>; 103 | using result_type = decltype(declval_task_last_node_result_tuple<_Task>()); 104 | 105 | task_set_exception_agent_sptr exp = tfirst._Get_exception_agent(); 106 | 107 | using first_node_type = task_anys_node; 108 | auto st_first = std::make_shared(exp, std::move(tfirst), std::move(rest)...); 109 | exp->_Impl = st_first.get(); 110 | 111 | when_any_impl(st_first.get(), *st_first->_All_tasks, std::make_index_sequence::value>{}); 112 | 113 | return task{exp, st_first, std::move(st_first)}; 114 | } 115 | }; 116 | } 117 | 118 | //等待多个任务之一完成 119 | //如果所有任务的返回类型都相同,则采用 task版本。其行为参考 task_anys_node 说明 120 | //多个任务的结果,类型未必相同,因此,采用std::any来存结果,最终结果是std::tuple。 121 | //size_t 指示是哪一个任务完成;std::any 是对应的任务的结果,需要通过std::any_cast<>()来获取。 122 | //首先做一个全新的task 123 | // task_any_node::invoke_thiz 主要负责调用所有的任务,以便于开始任务 124 | // 为每个任务造一个task_when_one。 125 | // task_when_one 负责将结果放入到 task_any_node的拼合tuple<>里,然后通知 task_any_node 有一个任务完成 126 | // task_any_node 在所有任务完成后,调用invoke_then_if 127 | // 128 | template 129 | auto when_any(_Task&& tfirst, _TaskRest&&... rest) 130 | { 131 | static_assert(detail::is_task<_Task>::value, "use 'make_task' or 'marshal_task' to create a task"); 132 | (void)std::initializer_list{detail::check_task_type<_TaskRest>()...}; 133 | 134 | using cated_result_t = decltype(std::tuple_cat(detail::declval_task_last_node_result_tuple<_Task>(), detail::declval_task_last_node_result_tuple<_TaskRest>()...)); 135 | using selector_type = detail::when_any_selector::value>; 136 | 137 | return selector_type::when_any(std::forward<_Task>(tfirst), std::forward<_TaskRest>(rest)...); 138 | } 139 | 140 | //等待多个任务之一完成 141 | //多个任务的结果类型肯定是一致的,故返回值的第一个参数指示是哪一个任务完成,后续参数是结果 142 | //首先做一个全新的task 143 | // task_anyv_node::invoke_thiz 主要负责调用所有的任务,以便于开始任务。在任务为空时,当作全部任务已经完成处理。 144 | // 为每个任务造一个task_when_one。 145 | // task_when_one 负责将结果放入到 task_anyv_node的tuple里,然后通知 task_anyv_node 有一个任务完成 146 | // task_anyv_node 在所有任务完成后,调用invoke_then_if 147 | // 148 | template())>::value, decltype(*std::declval<_Iter>())>> 149 | auto when_any(_Iter _First, _Iter _Last) 150 | { 151 | using task_type = typename std::remove_reference<_Fty>::type; 152 | static_assert(detail::is_task::value, "_Iter must be a iterator of task container"); 153 | using result_type = decltype(detail::declval_task_last_node_result_tuple()); 154 | 155 | return detail::when_iter >(_First, _Last); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /include/task_when_any.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "task_when_node.inl" 4 | 5 | namespace lib_shark_task 6 | { 7 | namespace detail 8 | { 9 | template 10 | struct make_any_unpack_tuple_impl 11 | { 12 | static std::any make(_Types&&... args) 13 | { 14 | return std::make_any(std::forward<_Types>(args)...); 15 | } 16 | }; 17 | template<> 18 | struct make_any_unpack_tuple_impl<> 19 | { 20 | static std::any make() 21 | { 22 | return std::any{}; 23 | } 24 | }; 25 | template 26 | struct make_any_unpack_tuple_impl<_Ty> 27 | { 28 | static std::any make(_Ty && val) 29 | { 30 | return std::make_any::type>(std::forward<_Ty>(val)); 31 | } 32 | }; 33 | template 34 | struct make_any_unpack_tuple_impl> 35 | { 36 | static std::any make(std::tuple<_Ty> && val) 37 | { 38 | return std::make_any(std::get<0>(std::forward>(val))); 39 | } 40 | }; 41 | 42 | template 43 | std::any make_any_unpack(_Types&&... args) 44 | { 45 | return make_any_unpack_tuple_impl<_Types...>::make(std::forward<_Types>(args)...); 46 | } 47 | template 48 | std::any make_any_unpack_tuple_2(_Tuple&& args, std::index_sequence<_Idx...>) 49 | { 50 | return make_any_unpack(std::get<_Idx>(std::forward<_Tuple>(args))...); 51 | } 52 | template 53 | std::any make_any_unpack_tuple(std::tuple<_Types...>&& args) 54 | { 55 | return make_any_unpack_tuple_2(std::forward>(args), std::make_index_sequence{}); 56 | } 57 | } 58 | 59 | template 60 | struct task_any_node : public node_impl, std::function, std::function> 61 | { 62 | using base_type = node_impl, std::function, std::function>; 63 | using this_type = task_when_one<_Ttuple>; 64 | 65 | using element_type = std::tuple; 66 | using result_type = std::tuple; //本节点的结果的类型 67 | using result_tuple = result_type; //本节点的结果打包成tuple<>后的类型 68 | using args_tuple_type = std::tuple<>; //本节点的入参打包成tuple<>后的类型 69 | 70 | using task_tuple = detail::package_tuple_t<_Ttuple>; 71 | std::shared_ptr _All_tasks; 72 | 73 | template 74 | task_any_node(const task_set_exception_agent_sptr & exp, _Tasks&&... ts) 75 | : base_type(exp) 76 | , _All_tasks(std::make_shared(std::forward<_Tasks>(ts)...)) 77 | { 78 | } 79 | task_any_node(task_any_node && _Right) = default; 80 | task_any_node & operator = (task_any_node && _Right) = default; 81 | task_any_node(const task_any_node & _Right) = delete; 82 | task_any_node & operator = (const task_any_node & _Right) = delete; 83 | 84 | private: 85 | std::atomic _Result_retrieved{ false }; 86 | public: 87 | template 88 | void _Set_value_partial(size_t idx, _PrevArgs2&&... args) 89 | { 90 | std::unique_lock _Lock(this->_Mtx()); 91 | if (!_Result_retrieved) 92 | { 93 | _Result_retrieved = true; 94 | std::get<0>(this->_Peek_value()) = idx; 95 | std::get<1>(this->_Peek_value()) = detail::make_any_unpack(std::forward<_PrevArgs2>(args)...); 96 | } 97 | } 98 | template 99 | void _Set_value_partial_t(size_t idx, _PrevTuple&& args) 100 | { 101 | std::unique_lock _Lock(this->_Mtx()); 102 | if (!_Result_retrieved) 103 | { 104 | _Result_retrieved = true; 105 | std::get<0>(this->_Peek_value()) = idx; 106 | std::get<1>(this->_Peek_value()) = detail::make_any_unpack_tuple(std::forward<_PrevTuple>(args)); 107 | } 108 | } 109 | 110 | void _On_result(size_t idx) 111 | { 112 | std::unique_lock _Lock(this->_Mtx()); 113 | 114 | if (_Result_retrieved && idx == std::get<0>(this->_Peek_value())) 115 | { 116 | _Lock.unlock(); 117 | 118 | this->_Set_value(); 119 | this->_Ready = true; 120 | this->invoke_then_if(); 121 | } 122 | } 123 | 124 | void break_link() 125 | { 126 | base_type::break_link(); 127 | _All_tasks = nullptr; 128 | } 129 | 130 | template 131 | bool invoke_thiz(Args2&&... args) 132 | { 133 | static_assert(sizeof...(Args2) >= std::tuple_size::value, ""); 134 | 135 | try 136 | { 137 | std::unique_lock _Lock(this->_Mtx()); 138 | auto all_task = std::move(_All_tasks); 139 | _Lock.unlock(); 140 | 141 | for_each(*all_task, [&](auto & ts) 142 | { 143 | ts(args...); 144 | }); 145 | } 146 | catch (...) 147 | { 148 | this->_Set_Agent_exception(std::current_exception()); 149 | } 150 | 151 | return false; 152 | } 153 | 154 | template 155 | bool invoke_thiz_tuple(_PrevTuple&& args) 156 | { 157 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 158 | 159 | try 160 | { 161 | std::unique_lock _Lock(this->_Mtx()); 162 | auto all_task = std::move(_All_tasks); 163 | _Lock.unlock(); 164 | 165 | for_each(*all_task, [&](auto & ts) 166 | { 167 | std::apply(ts, args); 168 | }); 169 | } 170 | catch (...) 171 | { 172 | this->_Set_Agent_exception(std::current_exception()); 173 | } 174 | 175 | return false; 176 | } 177 | }; 178 | } 179 | -------------------------------------------------------------------------------- /include/task_when_anys.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "task_when_node.inl" 4 | 5 | namespace lib_shark_task 6 | { 7 | //when_any(_Task1, _Task2, ...) 8 | //当所有的任务的返回值都相同的时候,可以将结果简化为(size_t, result_type...)。 9 | //task_anys_node就是用于这种情况 10 | // 11 | //_All_tasks 由于任务的类型不同,导致只能用一个std::tuple<>来保存 12 | //因此,invoke_thiz/invoke_thiz_tuple采用for_each(std::tuple<>)语法来调用_All_tasks 13 | // 14 | //由于返回结果相同 15 | //_Set_value_partial/_Set_value_partial_t 将idx存到get<0>(result_type)里,其他参数放在result_type后续的参数里 16 | // 17 | //_On_result 检测结果已经获得,并且之前保存的get<0>(result_type)与idx相同,则调用invoke_then_if 18 | template 19 | struct task_anys_node : public node_impl, std::function, std::function> 20 | { 21 | using base_type = node_impl, std::function, std::function>; 22 | using this_type = task_when_one<_Ttuple, _ResultArgs...>; 23 | 24 | using element_type = std::tuple; 25 | using result_type = std::tuple; //本节点的结果的类型 26 | using result_tuple = result_type; //本节点的结果打包成tuple<>后的类型 27 | using args_tuple_type = std::tuple<>; //本节点的入参打包成tuple<>后的类型 28 | 29 | using task_tuple = detail::package_tuple_t<_Ttuple>; 30 | std::shared_ptr _All_tasks; 31 | 32 | using task_function = typename base_type::task_function; 33 | using then_function = typename base_type::then_function; 34 | 35 | template 36 | task_anys_node(const task_set_exception_agent_sptr & exp, _Tasks&&... ts) 37 | : base_type(exp) 38 | , _All_tasks(std::make_shared(std::forward<_Tasks>(ts)...)) 39 | { 40 | } 41 | task_anys_node(task_anys_node && _Right) = default; 42 | task_anys_node & operator = (task_anys_node && _Right) = default; 43 | task_anys_node(const task_anys_node & _Right) = delete; 44 | task_anys_node & operator = (const task_anys_node & _Right) = delete; 45 | 46 | private: 47 | std::atomic _Result_retrieved{ false }; 48 | public: 49 | template 50 | void _Set_value_partial(size_t idx, _PrevArgs2&&... args) 51 | { 52 | std::unique_lock _Lock(this->_Mtx()); 53 | if (!_Result_retrieved) 54 | { 55 | _Result_retrieved = true; 56 | detail::_Fill_to_tuple<0>(this->_Peek_value(), idx, std::forward<_PrevArgs2>(args)...); 57 | } 58 | } 59 | template 60 | void _Set_value_partial_t(size_t idx, _PrevTuple&& args) 61 | { 62 | std::unique_lock _Lock(this->_Mtx()); 63 | if (!_Result_retrieved) 64 | { 65 | _Result_retrieved = true; 66 | std::get<0>(this->_Peek_value()) = idx; 67 | detail::_Move_to_tuple<1>(this->_Peek_value(), std::forward<_PrevTuple>(args)); 68 | } 69 | } 70 | 71 | void _On_result(size_t idx) 72 | { 73 | std::unique_lock _Lock(this->_Mtx()); 74 | 75 | if (_Result_retrieved && idx == std::get<0>(this->_Peek_value())) 76 | { 77 | _Lock.unlock(); 78 | 79 | this->_Set_value(); 80 | this->_Ready = true; 81 | this->invoke_then_if(); 82 | } 83 | } 84 | 85 | void break_link() 86 | { 87 | base_type::break_link(); 88 | _All_tasks = nullptr; 89 | } 90 | 91 | template 92 | bool invoke_thiz(Args2&&... args) 93 | { 94 | static_assert(sizeof...(Args2) >= std::tuple_size::value, ""); 95 | 96 | try 97 | { 98 | std::unique_lock _Lock(this->_Mtx()); 99 | auto all_task = std::move(_All_tasks); 100 | _Lock.unlock(); 101 | 102 | for_each(*all_task, [&](auto & ts) 103 | { 104 | ts(args...); 105 | }); 106 | } 107 | catch (...) 108 | { 109 | this->_Set_Agent_exception(std::current_exception()); 110 | } 111 | 112 | return false; 113 | } 114 | 115 | template 116 | bool invoke_thiz_tuple(_PrevTuple&& args) 117 | { 118 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 119 | 120 | try 121 | { 122 | std::unique_lock _Lock(this->_Mtx()); 123 | auto all_task = std::move(_All_tasks); 124 | _Lock.unlock(); 125 | 126 | for_each(*all_task, [&](auto & ts) 127 | { 128 | std::apply(ts, args); 129 | }); 130 | } 131 | catch (...) 132 | { 133 | this->_Set_Agent_exception(std::current_exception()); 134 | } 135 | 136 | return false; 137 | } 138 | }; 139 | template 140 | struct task_anys_node<_Ttuple, std::tuple<_ResultArgs...>> : public task_anys_node<_Ttuple, _ResultArgs...> 141 | { 142 | using task_anys_node<_Ttuple, _ResultArgs...>::task_anys_node; 143 | }; 144 | } 145 | -------------------------------------------------------------------------------- /include/task_when_anyv.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "task_when_node.inl" 3 | 4 | namespace lib_shark_task 5 | { 6 | //when_any(_Iter begin, _Iter end) 7 | //适用于通过迭代子等待一组任务返回任意之一。 8 | //因此,所有的任务的返回值必然相同,结果是(size_t, result_type...)。 9 | // 10 | //_All_tasks 由于任务的类型相同,所以使用一个std::vector<>来保存。 11 | //因此,invoke_thiz/invoke_thiz_tuple采用ranged for(std::vector<>)语法来调用_All_tasks 12 | // 13 | //由于返回结果相同 14 | //_Set_value_partial/_Set_value_partial_t 将idx存到get<0>(result_type)里,其他参数放在result_type后续的参数里 15 | // 16 | //_On_result 检测结果已经获得,并且之前保存的get<0>(result_type)与idx相同,则调用invoke_then_if 17 | template 18 | struct task_anyv_node : public node_impl, std::function, std::function> 19 | { 20 | using base_type = node_impl, std::function, std::function>; 21 | using this_type = task_anyv_node<_Ttype, _ResultArgs...>; 22 | 23 | using element_type = std::tuple; 24 | using result_type = std::tuple; //本节点的结果的类型 25 | using result_tuple = result_type; //本节点的结果打包成tuple<>后的类型 26 | using args_tuple_type = std::tuple<>; //本节点的入参打包成tuple<>后的类型 27 | 28 | using task_vector = std::vector<_Ttype>; 29 | task_vector _All_tasks; 30 | 31 | using task_function = typename base_type::task_function; 32 | using then_function = typename base_type::then_function; 33 | 34 | template 35 | task_anyv_node(const task_set_exception_agent_sptr & exp, _Iter _First, _Iter _Last) 36 | : base_type(exp) 37 | { 38 | _All_tasks.reserve(std::distance(_First, _Last)); 39 | for (; _First != _Last; ++_First) 40 | _All_tasks.emplace_back(std::move(*_First)); 41 | } 42 | task_anyv_node(task_anyv_node && _Right) = default; 43 | task_anyv_node & operator = (task_anyv_node && _Right) = default; 44 | task_anyv_node(const task_anyv_node & _Right) = delete; 45 | task_anyv_node & operator = (const task_anyv_node & _Right) = delete; 46 | 47 | private: 48 | std::atomic _Result_retrieved{ false }; 49 | public: 50 | template 51 | void _Set_value_partial(size_t idx, _PrevArgs2&&... args) 52 | { 53 | std::unique_lock _Lock(this->_Mtx()); 54 | if (!_Result_retrieved) 55 | { 56 | _Result_retrieved = true; 57 | detail::_Fill_to_tuple<0>(this->_Peek_value(), idx, std::forward<_PrevArgs2>(args)...); 58 | } 59 | } 60 | template 61 | void _Set_value_partial_t(size_t idx, _PrevTuple&& args) 62 | { 63 | std::unique_lock _Lock(this->_Mtx()); 64 | if (!_Result_retrieved) 65 | { 66 | _Result_retrieved = true; 67 | std::get<0>(this->_Peek_value()) = idx; 68 | detail::_Move_to_tuple<1>(this->_Peek_value(), std::forward<_PrevTuple>(args)); 69 | } 70 | } 71 | 72 | void _On_result(size_t idx) 73 | { 74 | std::unique_lock _Lock(this->_Mtx()); 75 | 76 | if (_Result_retrieved && idx == std::get<0>(this->_Peek_value())) 77 | { 78 | _Lock.unlock(); 79 | 80 | this->_Set_value(); 81 | this->_Ready = true; 82 | this->invoke_then_if(); 83 | } 84 | } 85 | 86 | void break_link() 87 | { 88 | base_type::break_link(); 89 | _All_tasks.clear(); 90 | } 91 | 92 | template 93 | bool invoke_thiz(Args2&&... args) 94 | { 95 | static_assert(sizeof...(Args2) >= std::tuple_size::value, ""); 96 | 97 | try 98 | { 99 | std::unique_lock _Lock(this->_Mtx()); 100 | task_vector all_task = std::move(_All_tasks); 101 | _Lock.unlock(); 102 | 103 | if (all_task.size() > 0) 104 | { 105 | for (auto & ts : all_task) 106 | ts(args...); 107 | } 108 | else 109 | { 110 | this->_Set_value(); 111 | this->_Ready = true; 112 | return true; 113 | } 114 | } 115 | catch (...) 116 | { 117 | this->_Set_Agent_exception(std::current_exception()); 118 | } 119 | 120 | return false; 121 | } 122 | 123 | template 124 | bool invoke_thiz_tuple(_PrevTuple&& args) 125 | { 126 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 127 | 128 | try 129 | { 130 | std::unique_lock _Lock(this->_Mtx()); 131 | task_vector all_task = std::move(_All_tasks); 132 | _Lock.unlock(); 133 | 134 | if (all_task.size() > 0) 135 | { 136 | for (auto & ts : all_task) 137 | std::apply(ts, args); 138 | } 139 | else 140 | { 141 | this->_Set_value(); 142 | this->_Ready = true; 143 | return true; 144 | } 145 | } 146 | catch (...) 147 | { 148 | this->_Set_Agent_exception(std::current_exception()); 149 | } 150 | 151 | return false; 152 | } 153 | }; 154 | template 155 | struct task_anyv_node<_Ttuple, std::tuple<_ResultArgs...>> : public task_anyv_node<_Ttuple, _ResultArgs...> 156 | { 157 | using task_anyv_node<_Ttuple, _ResultArgs...>::task_anyv_node; 158 | }; 159 | 160 | } 161 | -------------------------------------------------------------------------------- /include/task_when_node.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lib_shark_task 4 | { 5 | namespace detail 6 | { 7 | template 8 | decltype(auto) declval_task_last_node() 9 | { 10 | using task_type = std::decay_t<_Task>; 11 | static_assert(detail::is_task::value, "make sure '_Task' is a task"); 12 | return std::declval(); 13 | } 14 | template 15 | auto declval_task_last_node_result_tuple() 16 | { 17 | using task_type = std::decay_t<_Task>; 18 | static_assert(detail::is_task::value, "make sure '_Task' is a task"); 19 | using node_type = typename task_type::last_node; 20 | return std::declval(); 21 | } 22 | template 23 | int check_task_type() 24 | { 25 | static_assert(detail::is_task<_Task>::value, "use 'make_task' or 'marshal_task' to create a task"); 26 | return 0; 27 | } 28 | } 29 | 30 | template 31 | struct when_node_args 32 | { 33 | using notify_node = _Anode; 34 | using cated_tuple = _Ctuple; 35 | static const size_t index = _Idx; 36 | }; 37 | 38 | //_Ndone : when_node_args<>类型 39 | //_PrevArgs : 上一个节点的返回值 40 | //最后要将_PrevArgs...合并到 when_node_args<>::cated_tuple 里面去,然后通知 when_node_args<>::notify_node::_On_result() 41 | //task_when_one 用于when_all/when_any。 42 | //并且由于when_any的节点的生存期管理问题,必须出现循环引用。由task<>来负责断开循环引用 43 | template 44 | struct task_when_one : public node_impl> 45 | { 46 | using base_type = node_impl>; 47 | using this_type = task_when_one<_Ndone, _PrevArgs...>; 48 | 49 | using result_type = void; //本节点的结果的类型 50 | using result_tuple = std::tuple<>; //本节点的结果打包成tuple<>后的类型 51 | using args_tuple_type = std::tuple<_PrevArgs...>; //本节点的入参打包成tuple<>后的类型 52 | 53 | using notify_node = typename _Ndone::notify_node; 54 | using cated_tuple = typename _Ndone::cated_tuple; 55 | static const size_t cated_tuple_index = _Ndone::index; //本节点在tuple中的索引 56 | private: 57 | std::shared_ptr _Notify; //本节点执行完毕后,通知的下一个节点 58 | size_t _Index; //本节点在vector中的索引 59 | public: 60 | task_when_one(const task_set_exception_agent_sptr & exp, notify_node * nn, size_t idx) 61 | : base_type(exp) 62 | , _Notify(std::static_pointer_cast(nn->shared_from_this())) 63 | , _Index(idx) 64 | { 65 | } 66 | task_when_one(task_when_one && _Right) = default; 67 | task_when_one & operator = (task_when_one && _Right) = default; 68 | task_when_one(const task_when_one & _Right) = delete; 69 | task_when_one & operator = (const task_when_one & _Right) = delete; 70 | 71 | void break_link() 72 | { 73 | base_type::break_link(); 74 | _Notify = nullptr; 75 | } 76 | 77 | template 78 | bool invoke_thiz(_PrevArgs2&&... args) 79 | { 80 | static_assert(sizeof...(_PrevArgs2) >= std::tuple_size::value, ""); 81 | 82 | try 83 | { 84 | _Notify->template _Set_value_partial(_Index, std::forward<_PrevArgs2>(args)...); 85 | this->_Set_value(0); 86 | this->_Ready = true; 87 | } 88 | catch (...) 89 | { 90 | this->_Set_Agent_exception(std::current_exception()); 91 | } 92 | 93 | return this->_Ready; 94 | } 95 | 96 | template 97 | bool invoke_thiz_tuple(_PrevTuple&& args) 98 | { 99 | static_assert(std::tuple_size<_PrevTuple>::value >= std::tuple_size::value, ""); 100 | 101 | try 102 | { 103 | _Notify->template _Set_value_partial_t(_Index, std::forward<_PrevTuple>(args)); 104 | this->_Set_value(0); 105 | this->_Ready = true; 106 | } 107 | catch (...) 108 | { 109 | this->_Set_Agent_exception(std::current_exception()); 110 | } 111 | 112 | return this->_Ready; 113 | } 114 | 115 | void invoke_then_if() 116 | { 117 | if (_Notify == nullptr) 118 | throw std::future_error(std::make_error_code(std::future_errc::future_already_retrieved)); 119 | 120 | _Notify->_On_result(_Index); 121 | _Notify = nullptr; 122 | } 123 | }; 124 | 125 | template 126 | struct task_when_one<_Ndone, std::tuple<_PrevArgs...>> : public task_when_one<_Ndone, _PrevArgs...> 127 | { 128 | using task_when_one<_Ndone, _PrevArgs...>::task_when_one; 129 | }; 130 | 131 | namespace detail 132 | { 133 | template 134 | void when_wait_one_impl(_Anode * all_node, size_t node_idx, _Task & tf) 135 | { 136 | using tuple_type = decltype(declval_task_last_node_result_tuple<_Task>()); 137 | 138 | using task_type = std::remove_reference_t<_Task>; 139 | using result_tuple = typename _Anode::element_type; 140 | using node_args_type = when_node_args<_Anode, result_tuple, _Idx>; 141 | 142 | using next_node_type = task_when_one; 143 | 144 | task_set_exception_agent_sptr exp = tf._Get_exception_agent(); 145 | exp->_Impl = all_node; 146 | 147 | auto st_next = std::make_shared(exp, all_node, node_idx); 148 | tf.template _Then_node(st_next); 149 | } 150 | 151 | template 152 | void when_iter_impl(_Anode * all_node, _Cont & c) 153 | { 154 | size_t idx = 0; 155 | for (auto & t : c) 156 | when_wait_one_impl<0>(all_node, idx++, t); 157 | } 158 | 159 | template 160 | auto when_iter(_Iter _First, _Iter _Last) 161 | { 162 | task_set_exception_agent_sptr exp; 163 | if (_First != _Last) 164 | exp = _First->_Get_exception_agent(); 165 | else 166 | exp = std::make_shared(); 167 | 168 | using first_node_type = _Node; 169 | auto st_first = std::make_shared(exp, _First, _Last); 170 | exp->_Impl = st_first.get(); 171 | 172 | when_iter_impl(st_first.get(), st_first->_All_tasks); 173 | 174 | return task{exp, st_first, std::move(st_first)}; 175 | } 176 | 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := lib_shark_task_static 6 | LOCAL_MODULE_FILENAME := lib_shark_task 7 | 8 | LOCAL_SRC_FILES := $(LOCAL_PATH)/libtask.cpp \ 9 | $(LOCAL_PATH)/../example/task_async.cpp \ 10 | $(LOCAL_PATH)/../example/task_conflict.cpp \ 11 | $(LOCAL_PATH)/../example/task_exception.cpp \ 12 | $(LOCAL_PATH)/../example/task_link.cpp \ 13 | $(LOCAL_PATH)/../example/task_marshal.cpp \ 14 | $(LOCAL_PATH)/../example/task_optimal_move.cpp \ 15 | $(LOCAL_PATH)/../example/task_thread_safe.cpp \ 16 | $(LOCAL_PATH)/../example/task_when_all.cpp \ 17 | $(LOCAL_PATH)/../example/task_when_any.cpp \ 18 | 19 | 20 | 21 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.. 22 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/../include $(LOCAL_PATH)/../example 23 | 24 | include $(BUILD_STATIC_LIBRARY) 25 | -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tearshark/libst/1c8251856957590be5ad063be7f05f0f5c5430a1/jni/Application.mk -------------------------------------------------------------------------------- /jni/libtask.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "task.h" 3 | #include "task_context.h" 4 | 5 | namespace lib_shark_task 6 | { 7 | immediate_task_context imm_context; 8 | async_task_context async_context; 9 | } 10 | 11 | void test_task_link(); 12 | void test_task_async(); 13 | void test_task_optimal_move(); 14 | void test_task_conflict(); 15 | void test_task_exception(); 16 | void test_task_marshal(); 17 | 18 | void test_task_thread_safe(); 19 | void test_task_when_all(); 20 | void test_task_when_any(); 21 | 22 | int test_main() 23 | { 24 | //test_task_link(); 25 | //test_task_async(); 26 | //test_task_optimal_move(); 27 | ////test_task_conflict(); 28 | //test_task_exception(); 29 | //test_task_marshal(); 30 | //test_task_thread_safe(); 31 | test_task_when_all(); 32 | test_task_when_any(); 33 | 34 | return 0; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /libtask.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "include/task.h" 4 | #include "include/task_context.h" 5 | #include "include/task_when_all.h" 6 | #include "include/task_when_any.h" 7 | -------------------------------------------------------------------------------- /mac_proj/libtask.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B65A6EFD20B8DDD500EBF5DD /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B65A6EFC20B8DDD500EBF5DD /* main.cpp */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | B65A6EF720B8DDD500EBF5DD /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | B65A6EF920B8DDD500EBF5DD /* libtask */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = libtask; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | B65A6EFC20B8DDD500EBF5DD /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | B65A6EF620B8DDD500EBF5DD /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | B65A6EF020B8DDD500EBF5DD = { 42 | isa = PBXGroup; 43 | children = ( 44 | B65A6EFB20B8DDD500EBF5DD /* libtask */, 45 | B65A6EFA20B8DDD500EBF5DD /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | B65A6EFA20B8DDD500EBF5DD /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | B65A6EF920B8DDD500EBF5DD /* libtask */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | B65A6EFB20B8DDD500EBF5DD /* libtask */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | B65A6EFC20B8DDD500EBF5DD /* main.cpp */, 61 | ); 62 | path = libtask; 63 | sourceTree = ""; 64 | }; 65 | /* End PBXGroup section */ 66 | 67 | /* Begin PBXNativeTarget section */ 68 | B65A6EF820B8DDD500EBF5DD /* libtask */ = { 69 | isa = PBXNativeTarget; 70 | buildConfigurationList = B65A6F0020B8DDD500EBF5DD /* Build configuration list for PBXNativeTarget "libtask" */; 71 | buildPhases = ( 72 | B65A6EF520B8DDD500EBF5DD /* Sources */, 73 | B65A6EF620B8DDD500EBF5DD /* Frameworks */, 74 | B65A6EF720B8DDD500EBF5DD /* CopyFiles */, 75 | ); 76 | buildRules = ( 77 | ); 78 | dependencies = ( 79 | ); 80 | name = libtask; 81 | productName = libtask; 82 | productReference = B65A6EF920B8DDD500EBF5DD /* libtask */; 83 | productType = "com.apple.product-type.tool"; 84 | }; 85 | /* End PBXNativeTarget section */ 86 | 87 | /* Begin PBXProject section */ 88 | B65A6EF120B8DDD500EBF5DD /* Project object */ = { 89 | isa = PBXProject; 90 | attributes = { 91 | LastUpgradeCheck = 0830; 92 | ORGANIZATIONNAME = tearshark; 93 | TargetAttributes = { 94 | B65A6EF820B8DDD500EBF5DD = { 95 | CreatedOnToolsVersion = 8.3.3; 96 | DevelopmentTeam = Y497YMU6VN; 97 | ProvisioningStyle = Automatic; 98 | }; 99 | }; 100 | }; 101 | buildConfigurationList = B65A6EF420B8DDD500EBF5DD /* Build configuration list for PBXProject "libtask" */; 102 | compatibilityVersion = "Xcode 3.2"; 103 | developmentRegion = English; 104 | hasScannedForEncodings = 0; 105 | knownRegions = ( 106 | en, 107 | ); 108 | mainGroup = B65A6EF020B8DDD500EBF5DD; 109 | productRefGroup = B65A6EFA20B8DDD500EBF5DD /* Products */; 110 | projectDirPath = ""; 111 | projectRoot = ""; 112 | targets = ( 113 | B65A6EF820B8DDD500EBF5DD /* libtask */, 114 | ); 115 | }; 116 | /* End PBXProject section */ 117 | 118 | /* Begin PBXSourcesBuildPhase section */ 119 | B65A6EF520B8DDD500EBF5DD /* Sources */ = { 120 | isa = PBXSourcesBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | B65A6EFD20B8DDD500EBF5DD /* main.cpp in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | B65A6EFE20B8DDD500EBF5DD /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_ANALYZER_NONNULL = YES; 135 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 136 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 137 | CLANG_CXX_LIBRARY = "libc++"; 138 | CLANG_ENABLE_MODULES = YES; 139 | CLANG_ENABLE_OBJC_ARC = YES; 140 | CLANG_WARN_BOOL_CONVERSION = YES; 141 | CLANG_WARN_CONSTANT_CONVERSION = YES; 142 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 143 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 144 | CLANG_WARN_EMPTY_BODY = YES; 145 | CLANG_WARN_ENUM_CONVERSION = YES; 146 | CLANG_WARN_INFINITE_RECURSION = YES; 147 | CLANG_WARN_INT_CONVERSION = YES; 148 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 149 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 150 | CLANG_WARN_UNREACHABLE_CODE = YES; 151 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 152 | CODE_SIGN_IDENTITY = "-"; 153 | COPY_PHASE_STRIP = NO; 154 | DEBUG_INFORMATION_FORMAT = dwarf; 155 | ENABLE_STRICT_OBJC_MSGSEND = YES; 156 | ENABLE_TESTABILITY = YES; 157 | GCC_C_LANGUAGE_STANDARD = gnu99; 158 | GCC_DYNAMIC_NO_PIC = NO; 159 | GCC_NO_COMMON_BLOCKS = YES; 160 | GCC_OPTIMIZATION_LEVEL = 0; 161 | GCC_PREPROCESSOR_DEFINITIONS = ( 162 | "DEBUG=1", 163 | "$(inherited)", 164 | ); 165 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 166 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 167 | GCC_WARN_UNDECLARED_SELECTOR = YES; 168 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 169 | GCC_WARN_UNUSED_FUNCTION = YES; 170 | GCC_WARN_UNUSED_VARIABLE = YES; 171 | MACOSX_DEPLOYMENT_TARGET = 10.12; 172 | MTL_ENABLE_DEBUG_INFO = YES; 173 | ONLY_ACTIVE_ARCH = YES; 174 | SDKROOT = macosx; 175 | }; 176 | name = Debug; 177 | }; 178 | B65A6EFF20B8DDD500EBF5DD /* Release */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | CLANG_ANALYZER_NONNULL = YES; 183 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 185 | CLANG_CXX_LIBRARY = "libc++"; 186 | CLANG_ENABLE_MODULES = YES; 187 | CLANG_ENABLE_OBJC_ARC = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_CONSTANT_CONVERSION = YES; 190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 191 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 192 | CLANG_WARN_EMPTY_BODY = YES; 193 | CLANG_WARN_ENUM_CONVERSION = YES; 194 | CLANG_WARN_INFINITE_RECURSION = YES; 195 | CLANG_WARN_INT_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 198 | CLANG_WARN_UNREACHABLE_CODE = YES; 199 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 200 | CODE_SIGN_IDENTITY = "-"; 201 | COPY_PHASE_STRIP = NO; 202 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 203 | ENABLE_NS_ASSERTIONS = NO; 204 | ENABLE_STRICT_OBJC_MSGSEND = YES; 205 | GCC_C_LANGUAGE_STANDARD = gnu99; 206 | GCC_NO_COMMON_BLOCKS = YES; 207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 209 | GCC_WARN_UNDECLARED_SELECTOR = YES; 210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 211 | GCC_WARN_UNUSED_FUNCTION = YES; 212 | GCC_WARN_UNUSED_VARIABLE = YES; 213 | MACOSX_DEPLOYMENT_TARGET = 10.12; 214 | MTL_ENABLE_DEBUG_INFO = NO; 215 | SDKROOT = macosx; 216 | }; 217 | name = Release; 218 | }; 219 | B65A6F0120B8DDD500EBF5DD /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | CLANG_CXX_LANGUAGE_STANDARD = "c++14"; 223 | DEVELOPMENT_TEAM = Y497YMU6VN; 224 | HEADER_SEARCH_PATHS = ( 225 | ../, 226 | ../include, 227 | ); 228 | PRODUCT_NAME = "$(TARGET_NAME)"; 229 | }; 230 | name = Debug; 231 | }; 232 | B65A6F0220B8DDD500EBF5DD /* Release */ = { 233 | isa = XCBuildConfiguration; 234 | buildSettings = { 235 | CLANG_CXX_LANGUAGE_STANDARD = "c++14"; 236 | DEVELOPMENT_TEAM = Y497YMU6VN; 237 | HEADER_SEARCH_PATHS = ( 238 | ../, 239 | ../include, 240 | ); 241 | PRODUCT_NAME = "$(TARGET_NAME)"; 242 | }; 243 | name = Release; 244 | }; 245 | /* End XCBuildConfiguration section */ 246 | 247 | /* Begin XCConfigurationList section */ 248 | B65A6EF420B8DDD500EBF5DD /* Build configuration list for PBXProject "libtask" */ = { 249 | isa = XCConfigurationList; 250 | buildConfigurations = ( 251 | B65A6EFE20B8DDD500EBF5DD /* Debug */, 252 | B65A6EFF20B8DDD500EBF5DD /* Release */, 253 | ); 254 | defaultConfigurationIsVisible = 0; 255 | defaultConfigurationName = Release; 256 | }; 257 | B65A6F0020B8DDD500EBF5DD /* Build configuration list for PBXNativeTarget "libtask" */ = { 258 | isa = XCConfigurationList; 259 | buildConfigurations = ( 260 | B65A6F0120B8DDD500EBF5DD /* Debug */, 261 | B65A6F0220B8DDD500EBF5DD /* Release */, 262 | ); 263 | defaultConfigurationIsVisible = 0; 264 | defaultConfigurationName = Release; 265 | }; 266 | /* End XCConfigurationList section */ 267 | }; 268 | rootObject = B65A6EF120B8DDD500EBF5DD /* Project object */; 269 | } 270 | -------------------------------------------------------------------------------- /mac_proj/libtask.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /mac_proj/libtask.xcodeproj/project.xcworkspace/xcuserdata/tearshark.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tearshark/libst/1c8251856957590be5ad063be7f05f0f5c5430a1/mac_proj/libtask.xcodeproj/project.xcworkspace/xcuserdata/tearshark.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /mac_proj/libtask/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // main.cpp 4 | // libtask 5 | // 6 | // Created by tearshark on 2018/5/26. 7 | // Copyright © 2018年 tearshark. All rights reserved. 8 | // 9 | 10 | #include "task.h" 11 | #include "task_context.h" 12 | 13 | namespace lib_shark_task 14 | { 15 | immediate_task_context imm_context; 16 | async_task_context async_context; 17 | } 18 | 19 | void test_task_link(); 20 | void test_task_async(); 21 | void test_task_optimal_move(); 22 | void test_task_conflict(); 23 | void test_task_exception(); 24 | void test_task_marshal(); 25 | 26 | void test_task_thread_safe(); 27 | void test_task_when_all(); 28 | void test_task_when_any(); 29 | 30 | int main(int argc, const char * argv[]) 31 | { 32 | //test_task_link(); 33 | //test_task_async(); 34 | //test_task_optimal_move(); 35 | ////test_task_conflict(); 36 | //test_task_exception(); 37 | //test_task_marshal(); 38 | //test_task_thread_safe(); 39 | //test_task_when_all(); 40 | test_task_when_any(); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /vs_proj/libtask.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "task.h" 4 | #include "task_context.h" 5 | 6 | namespace lib_shark_task 7 | { 8 | immediate_task_context imm_context; 9 | async_task_context async_context; 10 | #if LIBTASK_DEBUG_MEMORY 11 | std::atomic g_node_counter{ 0 }; 12 | std::atomic g_task_counter{ 0 }; 13 | #endif 14 | } 15 | 16 | void test_task_link(); 17 | void test_task_async(); 18 | void test_task_optimal_move(); 19 | void test_task_conflict(); 20 | void test_task_exception(); 21 | void test_task_marshal(); 22 | 23 | void test_task_thread_safe(); 24 | void test_task_when_all(); 25 | void test_task_when_any(); 26 | 27 | int main() 28 | { 29 | //test_task_link(); 30 | //test_task_async(); 31 | //test_task_optimal_move(); 32 | //test_task_conflict(); 33 | //test_task_exception(); 34 | //test_task_marshal(); 35 | //test_task_thread_safe(); 36 | test_task_when_all(); 37 | test_task_when_any(); 38 | 39 | return 0; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /vs_proj/libtask.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2000 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtask", "libtask.vcxproj", "{FD137E57-E96F-485A-B37D-F977188E8DA4}" 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 | {FD137E57-E96F-485A-B37D-F977188E8DA4}.Debug|x64.ActiveCfg = Debug|x64 17 | {FD137E57-E96F-485A-B37D-F977188E8DA4}.Debug|x64.Build.0 = Debug|x64 18 | {FD137E57-E96F-485A-B37D-F977188E8DA4}.Debug|x86.ActiveCfg = Debug|Win32 19 | {FD137E57-E96F-485A-B37D-F977188E8DA4}.Debug|x86.Build.0 = Debug|Win32 20 | {FD137E57-E96F-485A-B37D-F977188E8DA4}.Release|x64.ActiveCfg = Release|x64 21 | {FD137E57-E96F-485A-B37D-F977188E8DA4}.Release|x64.Build.0 = Release|x64 22 | {FD137E57-E96F-485A-B37D-F977188E8DA4}.Release|x86.ActiveCfg = Release|Win32 23 | {FD137E57-E96F-485A-B37D-F977188E8DA4}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4CADEFB8-D910-475D-A754-267621D9C9E2} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /vs_proj/libtask.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 | 15.0 23 | {FD137E57-E96F-485A-B37D-F977188E8DA4} 24 | Win32Proj 25 | libtask 26 | 10.0.17763.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | NotUsing 88 | Level3 89 | Disabled 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | ./;../;../include 94 | 4503;4819 95 | stdcpplatest 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | NotUsing 105 | Level3 106 | Disabled 107 | true 108 | _DEBUG;_CONSOLE;LIBTASK_DEBUG_MEMORY;%(PreprocessorDefinitions) 109 | true 110 | ./;../;../include 111 | 4503;4819 112 | stdcpplatest 113 | 114 | 115 | Console 116 | true 117 | 118 | 119 | 120 | 121 | NotUsing 122 | Level3 123 | MaxSpeed 124 | true 125 | true 126 | true 127 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 128 | true 129 | ./;../;../include 130 | 4503;4819 131 | stdcpplatest 132 | 133 | 134 | Console 135 | true 136 | true 137 | true 138 | 139 | 140 | 141 | 142 | NotUsing 143 | Level3 144 | MaxSpeed 145 | true 146 | true 147 | true 148 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 149 | true 150 | ./;../;../include 151 | 4503;4819 152 | stdcpplatest 153 | 154 | 155 | Console 156 | true 157 | true 158 | true 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /vs_proj/libtask.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;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 | {64ec0d5b-e409-4784-bc96-f64553280a0d} 18 | 19 | 20 | {adb6d292-ba69-4e5c-8086-7a10e657a1cd} 21 | 22 | 23 | 24 | 25 | Header Files 26 | 27 | 28 | libst 29 | 30 | 31 | libst 32 | 33 | 34 | libst 35 | 36 | 37 | example 38 | 39 | 40 | libst 41 | 42 | 43 | libst 44 | 45 | 46 | Header Files 47 | 48 | 49 | example 50 | 51 | 52 | example 53 | 54 | 55 | 56 | 57 | Source Files 58 | 59 | 60 | example 61 | 62 | 63 | example 64 | 65 | 66 | example 67 | 68 | 69 | example 70 | 71 | 72 | example 73 | 74 | 75 | example 76 | 77 | 78 | example 79 | 80 | 81 | example 82 | 83 | 84 | example 85 | 86 | 87 | 88 | 89 | libst 90 | 91 | 92 | libst 93 | 94 | 95 | libst 96 | 97 | 98 | libst 99 | 100 | 101 | libst 102 | 103 | 104 | libst 105 | 106 | 107 | libst 108 | 109 | 110 | libst 111 | 112 | 113 | libst 114 | 115 | 116 | libst 117 | 118 | 119 | libst 120 | 121 | 122 | libst 123 | 124 | 125 | libst 126 | 127 | 128 | libst 129 | 130 | 131 | libst 132 | 133 | 134 | -------------------------------------------------------------------------------- /vs_proj/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tearshark/libst/1c8251856957590be5ad063be7f05f0f5c5430a1/vs_proj/targetver.h --------------------------------------------------------------------------------