{"sha":"fa815b4531904cb9a68b11a0026964e0b197c9de","node_id":"MDY6Q29tbWl0MzU2ODEzNzY6ZmE4MTViNDUzMTkwNGNiOWE2OGIxMWEwMDI2OTY0ZTBiMTk3YzlkZQ==","commit":{"author":{"name":"Felix Petriconi","email":"FelixPetriconi@users.noreply.github.com","date":"2018-12-12T16:29:32Z"},"committer":{"name":"GitHub","email":"noreply@github.com","date":"2018-12-12T16:29:32Z"},"message":"Merge pull request #217 from stlab/develop\n\nRelease 1.4.0","tree":{"sha":"198ddde45f9a72ea67972b1283a788cde97d500c","url":"https://api.github.com/repos/stlab/stlab/git/trees/198ddde45f9a72ea67972b1283a788cde97d500c"},"url":"https://api.github.com/repos/stlab/stlab/git/commits/fa815b4531904cb9a68b11a0026964e0b197c9de","comment_count":0,"verification":{"verified":true,"reason":"valid","signature":"-----BEGIN PGP SIGNATURE-----\n\nwsBcBAABCAAQBQJcETdsCRBK7hj4Ov3rIwAAdHIIADJwUj24jc2kn67C7FxbFAR5\nS+Yn/xIJ/c+RlNPxZXuCvNGkY4yNcAu+aU1Kdbo9Z4lI6EOE3vyCSvsMafqWjKiO\nH/qnaqBSKschBYclu3ZFfO+++kbVv5C4yNRG9P2u6busyLSBKZqMvJoZ40ePcNsw\nvX9Z+bDP5J1Ax2lN9qIBY/XVlRTFOor21RCwZUIEpsG5urN3vIteCzaIikHGikfg\n1gUXocn/S2PueJ5joPNxw/nCT3kR/37i3dpJQu2tnjMRPKtxnGNuDiY+D+120k4M\nD5qUs5kgtfUWM1s9z4807SVm3O1IYa4tOoD+VOYSR1yvfKJAoEeipwHCvUoZSwQ=\n=cD8I\n-----END PGP SIGNATURE-----\n","payload":"tree 198ddde45f9a72ea67972b1283a788cde97d500c\nparent 278d1b0088d6b00143a658034c32ff12ae8227c8\nparent 16410eb8b070781d77217376c986c91c01a4e46c\nauthor Felix Petriconi <FelixPetriconi@users.noreply.github.com> 1544632172 +0100\ncommitter GitHub <noreply@github.com> 1544632172 +0100\n\nMerge pull request #217 from stlab/develop\n\nRelease 1.4.0","verified_at":"2024-11-07T19:04:39Z"}},"url":"https://api.github.com/repos/stlab/stlab/commits/fa815b4531904cb9a68b11a0026964e0b197c9de","html_url":"https://github.com/stlab/stlab/commit/fa815b4531904cb9a68b11a0026964e0b197c9de","comments_url":"https://api.github.com/repos/stlab/stlab/commits/fa815b4531904cb9a68b11a0026964e0b197c9de/comments","author":{"login":"FelixPetriconi","id":612377,"node_id":"MDQ6VXNlcjYxMjM3Nw==","avatar_url":"https://avatars.githubusercontent.com/u/612377?v=4","gravatar_id":"","url":"https://api.github.com/users/FelixPetriconi","html_url":"https://github.com/FelixPetriconi","followers_url":"https://api.github.com/users/FelixPetriconi/followers","following_url":"https://api.github.com/users/FelixPetriconi/following{/other_user}","gists_url":"https://api.github.com/users/FelixPetriconi/gists{/gist_id}","starred_url":"https://api.github.com/users/FelixPetriconi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/FelixPetriconi/subscriptions","organizations_url":"https://api.github.com/users/FelixPetriconi/orgs","repos_url":"https://api.github.com/users/FelixPetriconi/repos","events_url":"https://api.github.com/users/FelixPetriconi/events{/privacy}","received_events_url":"https://api.github.com/users/FelixPetriconi/received_events","type":"User","user_view_type":"public","site_admin":false},"committer":{"login":"web-flow","id":19864447,"node_id":"MDQ6VXNlcjE5ODY0NDQ3","avatar_url":"https://avatars.githubusercontent.com/u/19864447?v=4","gravatar_id":"","url":"https://api.github.com/users/web-flow","html_url":"https://github.com/web-flow","followers_url":"https://api.github.com/users/web-flow/followers","following_url":"https://api.github.com/users/web-flow/following{/other_user}","gists_url":"https://api.github.com/users/web-flow/gists{/gist_id}","starred_url":"https://api.github.com/users/web-flow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/web-flow/subscriptions","organizations_url":"https://api.github.com/users/web-flow/orgs","repos_url":"https://api.github.com/users/web-flow/repos","events_url":"https://api.github.com/users/web-flow/events{/privacy}","received_events_url":"https://api.github.com/users/web-flow/received_events","type":"User","user_view_type":"public","site_admin":false},"parents":[{"sha":"278d1b0088d6b00143a658034c32ff12ae8227c8","url":"https://api.github.com/repos/stlab/stlab/commits/278d1b0088d6b00143a658034c32ff12ae8227c8","html_url":"https://github.com/stlab/stlab/commit/278d1b0088d6b00143a658034c32ff12ae8227c8"},{"sha":"16410eb8b070781d77217376c986c91c01a4e46c","url":"https://api.github.com/repos/stlab/stlab/commits/16410eb8b070781d77217376c986c91c01a4e46c","html_url":"https://github.com/stlab/stlab/commit/16410eb8b070781d77217376c986c91c01a4e46c"}],"stats":{"total":4373,"additions":3134,"deletions":1239},"files":[{"sha":"9cd8da5c7a960ce4fde432d466a712086cee8b18","filename":".travis.yml","status":"modified","additions":9,"deletions":19,"changes":28,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/.travis.yml","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/.travis.yml","contents_url":"https://api.github.com/repos/stlab/stlab/contents/.travis.yml?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -1,5 +1,5 @@\n sudo: required\n-dist: trusty\n+dist: xenial\n #group: deprecated-2017Q4\n language: cpp\n \n@@ -10,12 +10,12 @@ cache:\n addons:\n   apt:\n     sources:\n-      - sourceline: deb http://apt.llvm.org/precise/ llvm-toolchain-precise-3.8 main\n-        key_url: http://apt.llvm.org/llvm-snapshot.gpg.key\n       - ubuntu-toolchain-r-test\n+#      - sourceline: deb http://apt.llvm.org/xenial/ llvm-toolchain-precise-7.0 main\n+#        key_url: http://apt.llvm.org/llvm-snapshot.gpg.key\n     packages:\n-      - g++-5\n-      - clang-3.8\n+      - g++-7\n+#      - clang-7.0\n \n branches:\n   except: /pr\\/.*/\n@@ -27,11 +27,10 @@ matrix:\n     #\n     - os: osx\n       compiler: clang\n-      osx_image: xcode9\n+      osx_image: xcode10.1\n       before_install:\n         - brew update\n         - brew upgrade python\n-        - brew uninstall libyaml\n         - pip3 install --no-cache-dir pyyaml\n         - pip3 install --user conan\n         - export PATH=$PATH:/Users/travis/Library/Python/3.7/bin\n@@ -43,11 +42,10 @@ matrix:\n \n     - os: osx\n       compiler: clang\n-      osx_image: xcode9\n+      osx_image: xcode10.1\n       before_install:\n         - brew update\n         - brew upgrade python\n-        - brew uninstall libyaml\n         - pip3 install --no-cache-dir pyyaml\n         - pip3 install --user conan\n         - export PATH=$PATH:/Users/travis/Library/Python/3.7/bin\n@@ -157,16 +155,8 @@ matrix:\n         - pip install --user conan\n         - ./enhance_conan.sh\n         - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-\n-      addons:\n-        apt:\n-          sources:\n-            - sourceline: deb http://apt.llvm.org/precise/ llvm-toolchain-precise-3.8 main\n-              key_url: http://apt.llvm.org/llvm-snapshot.gpg.key\n-            - ubuntu-toolchain-r-test\n-          packages:\n-            - g++-5\n-            - clang-3.8\n-        coverity_scan:\n+\n+      coverity_scan:\n           project:\n             name: \"stlab/libraries\"\n             description: \"Build submitted via Travis CI\""},{"sha":"6a1a899323784cc0de573e7865d894665b0034d5","filename":".travis/build.sh","status":"modified","additions":11,"deletions":5,"changes":16,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/.travis%2Fbuild.sh","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/.travis%2Fbuild.sh","contents_url":"https://api.github.com/repos/stlab/stlab/contents/.travis%2Fbuild.sh?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -2,13 +2,19 @@\n set -x\n \n if [ \"$TRAVIS_OS_NAME\" = \"linux\" ]; then\n+  #\n+  # It is nevessary to patch gcc std.variant because of a clang bug which is fixed in clang 8.0\n+  # further details are here: https://stackoverflow.com/questions/46506387/getstring-for-variants-fail-under-clang-but-not-g\n+  #\n+  sudo .travis/patches/patch_std_variant.sh\n+\n   sudo update-alternatives \\\n-    --install /usr/bin/gcc gcc /usr/bin/gcc-5 90 \\\n-    --slave /usr/bin/g++ g++ /usr/bin/g++-5 \\\n-    --slave /usr/bin/gcov gcov /usr/bin/gcov-5\n+    --install /usr/bin/gcc gcc /usr/bin/gcc-7 90 \\\n+    --slave /usr/bin/g++ g++ /usr/bin/g++-7 \\\n+    --slave /usr/bin/gcov gcov /usr/bin/gcov-7\n   sudo update-alternatives \\\n-    --install /usr/bin/clang clang /usr/bin/clang-3.8 90 \\\n-    --slave /usr/bin/clang++ clang++ /usr/bin/clang++-3.8\n+    --install /usr/bin/clang clang /usr/bin/clang-7 90 \\\n+    --slave /usr/bin/clang++ clang++ /usr/bin/clang++-7\n   sudo update-alternatives --config gcc\n   sudo update-alternatives --config clang\n   if [ \"$CXX\" = \"clang++\" ]; then"},{"sha":"1f0a7bb08e1c7bea6351a811747702490b04eacd","filename":".travis/patches/patch_std_variant.sh","status":"added","additions":16,"deletions":0,"changes":16,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/.travis%2Fpatches%2Fpatch_std_variant.sh","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/.travis%2Fpatches%2Fpatch_std_variant.sh","contents_url":"https://api.github.com/repos/stlab/stlab/contents/.travis%2Fpatches%2Fpatch_std_variant.sh?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -0,0 +1,16 @@\n+#!/bin/bash\n+\n+HERE=$(pwd)\n+\n+if [ ! -d /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/ ]; then\n+    echo \"You must have a destination directory (gcc 7.3.0 includes) in order to patch!\"\n+    exit 1;\n+fi\n+\n+pushd /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/\n+\n+patch -uN -p0 -g0 < \"$HERE/.travis/patches/std_variant.patch\" && echo \"Patching of std.variant succeeded. Happy coding!\"\n+\n+popd\n+\n+exit 0"},{"sha":"e6ea12539f4f12b494fc1ab9a393252e713a69bc","filename":".travis/patches/std_variant.patch","status":"added","additions":48,"deletions":0,"changes":48,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/.travis%2Fpatches%2Fstd_variant.patch","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/.travis%2Fpatches%2Fstd_variant.patch","contents_url":"https://api.github.com/repos/stlab/stlab/contents/.travis%2Fpatches%2Fstd_variant.patch?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -0,0 +1,48 @@\n+--- variant\t2018/03/26 12:03:53\t258853\n++++ variant\t2018/03/26 13:09:10\t258854\n+@@ -223,13 +223,17 @@\n+ \n+   template<size_t _Np, typename _Union>\n+     constexpr decltype(auto) __get(in_place_index_t<_Np>, _Union&& __u)\n+-    { return __get(in_place_index<_Np-1>, std::forward<_Union>(__u)._M_rest); }\n++    {\n++      return __variant::__get(in_place_index<_Np-1>,\n++\t\t\t      std::forward<_Union>(__u)._M_rest);\n++    }\n+ \n+   // Returns the typed storage for __v.\n+   template<size_t _Np, typename _Variant>\n+     constexpr decltype(auto) __get(_Variant&& __v)\n+     {\n+-      return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u);\n++      return __variant::__get(std::in_place_index<_Np>,\n++\t\t\t      std::forward<_Variant>(__v)._M_u);\n+     }\n+ \n+   // Various functions as \"vtable\" entries, where those vtables are used by\n+@@ -358,10 +362,9 @@\n+ \n+   template <typename... _Types>\n+   using __select_index =\n+-    typename __select_int::_Select_int_base<sizeof...(_Types)+1,\n++    typename __select_int::_Select_int_base<sizeof...(_Types) + 1,\n+ \t\t\t\t\t    unsigned char,\n+-\t\t\t\t\t    unsigned short>\n+-    ::type::value_type;\n++\t\t\t\t\t    unsigned short>::type::value_type;\n+ \n+   template<typename... _Types>\n+     struct _Variant_storage<false, _Types...>\n+@@ -1304,6 +1307,12 @@\n+ \n+ #undef _VARIANT_RELATION_FUNCTION_TEMPLATE\n+ \n++#ifdef __clang__\n++    public:\n++      using _Base::_M_u; // See https://bugs.llvm.org/show_bug.cgi?id=31852\n++    private:\n++#endif\n++\n+       template<size_t _Np, typename _Vp>\n+ \tfriend constexpr decltype(auto) __detail::__variant::__get(_Vp&& __v);\n+ \n\\ No newline at end of file"},{"sha":"f062ecf911a211422b0e9ae1a7c77c108cdd376a","filename":"CHANGES.md","status":"modified","additions":19,"deletions":0,"changes":19,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/CHANGES.md","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/CHANGES.md","contents_url":"https://api.github.com/repos/stlab/stlab/contents/CHANGES.md?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -1,3 +1,22 @@\n+## v1.4.0 - 2018 - December - \n+- Fixed Issues\n+    - [#208](https://github.com/stlab/libraries/issues/208): g++8.2.0 compiling error\n+    - [#206](https://github.com/stlab/libraries/issues/206): channels does not allow processes as lvalues, only as rvalue\n+    - [#204](https://github.com/stlab/libraries/issues/204): when_all does not support move-only types\n+    - [#192](https://github.com/stlab/libraries/issues/192): Issues with executor_t and when_all()\n+    - [#188](https://github.com/stlab/libraries/issues/188): question: channels and slow processes\n+\n+- Enhancements\n+    - [#212](https://github.com/stlab/libraries/issues/212): Join of future with move only type is missing\n+    - [#199](https://github.com/stlab/libraries/issues/199): add operator|() as continuable operator\n+    - It is now possible to create with channel<void>() a receiver<void> (not a pair<sender<void>, receiver<void>>)\n+\n+- Library Changes\n+    - Now C++17 is required to use the channels (This change was necessary, because the code became really messy to dispatch between copyable, move-only and void types)\n+\n+- Additional Comments\n+    - It is necessary to patch std::variant for the combination of clang version <= 7 and stdlibc++. For further details see https://stackoverflow.com/questions/46506387/getstring-for-variants-fail-under-clang-but-not-g and ./travis/patches/patch_std_variant.sh. This is because the clang bug https://bugs.llvm.org/show_bug.cgi?id=31852\n+\n ## v1.3.3 - 2018 - October - 25\n \n - Fixed Issues "},{"sha":"60267ac149bc6ea269d49c6dd3f2a54b366901ce","filename":"CMakeLists.txt","status":"modified","additions":10,"deletions":2,"changes":12,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/CMakeLists.txt","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/CMakeLists.txt","contents_url":"https://api.github.com/repos/stlab/stlab/contents/CMakeLists.txt?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -37,6 +37,14 @@ option( stlab.boost_variant \"Prefer Boost::variant to std::variant\" OFF )\n option( stlab.boost_optional \"Prefer Boost::optional to std::optional\" OFF )\n option( stlab.coroutines \"Leverage the coroutine TS in stlab\" OFF )\n \n+#\n+# On apple we have to force the usage of boost.varianr, because Apple's\n+# implementation of C++17 is not complete\n+#\n+if(APPLE AND (CMAKE_CXX_COMPILER_ID STREQUAL \"AppleClang\" OR CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\"))\n+  set( stlab.boost_variant ON )\n+endif()\n+\n mark_as_advanced( stlab.coroutines stlab.boost_variant stlab.boost_optional )\n \n #\n@@ -49,11 +57,11 @@ add_library( stlab INTERFACE )\n add_library( stlab::stlab ALIAS stlab )\n \n #\n-# stlab requires C++ 14 support, at a minimum. Setting the `cxx_std_14` compile\n+# stlab requires C++ 17 support, at a minimum. Setting the `cxx_std_17` compile\n # features ensures that the corresponding C++ standard flag is populated in\n # targets linking to stlab.\n #\n-target_compile_features( stlab INTERFACE cxx_std_14 )\n+target_compile_features( stlab INTERFACE cxx_std_17 )\n \n #\n # The include directory for stlab can be expected to vary between build"},{"sha":"613cf23427661632b223b0ed7a3691c810d6ee97","filename":"cmake/stlab/development/Clang.cmake","status":"modified","additions":1,"deletions":1,"changes":2,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/cmake%2Fstlab%2Fdevelopment%2FClang.cmake","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/cmake%2Fstlab%2Fdevelopment%2FClang.cmake","contents_url":"https://api.github.com/repos/stlab/stlab/contents/cmake%2Fstlab%2Fdevelopment%2FClang.cmake?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -1,4 +1,4 @@\n-set( stlab_Clang_base_flags -Wall -Wextra -Wpedantic -Werror -ftemplate-backtrace-limit=0 )\n+set( stlab_Clang_base_flags -Wall -Wextra -Wpedantic -Werror -ftemplate-backtrace-limit=0 -DBOOST_NO_AUTO_PTR=1 -D_GLIBCXX_USE_CXX11_ABI=0)\n set( stlab_Clang_debug_flags -gdwarf-3 )\n set( stlab_Clang_coverage_flags --coverage )\n set( stlab_Clang_release_flags )"},{"sha":"e1dfdfff00016e2386f886303441e6a0754f9bfb","filename":"cmake/stlab/development/GNU.cmake","status":"modified","additions":1,"deletions":1,"changes":2,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/cmake%2Fstlab%2Fdevelopment%2FGNU.cmake","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/cmake%2Fstlab%2Fdevelopment%2FGNU.cmake","contents_url":"https://api.github.com/repos/stlab/stlab/contents/cmake%2Fstlab%2Fdevelopment%2FGNU.cmake?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -1,4 +1,4 @@\n-set( stlab_GNU_base_flags -Wall -Wextra -Wpedantic -Werror -ftemplate-backtrace-limit=0 )\n+set( stlab_GNU_base_flags -Wall -Wextra -Wpedantic -Werror -ftemplate-backtrace-limit=0 -D_GLIBCXX_USE_CXX11_ABI=0)\n set( stlab_GNU_debug_flags -gdwarf-3 )\n set( stlab_GNU_coverage_flags --coverage )\n set( stlab_GNU_release_flags )"},{"sha":"8699ca96e2a306bc2c336e44f52b6bfe71deab56","filename":"cmake/stlab/development/MSVC.cmake","status":"modified","additions":1,"deletions":1,"changes":2,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/cmake%2Fstlab%2Fdevelopment%2FMSVC.cmake","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/cmake%2Fstlab%2Fdevelopment%2FMSVC.cmake","contents_url":"https://api.github.com/repos/stlab/stlab/contents/cmake%2Fstlab%2Fdevelopment%2FMSVC.cmake?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -1,4 +1,4 @@\n-set( stlab_MSVC_base_flags /W3 /WX )\n+set( stlab_MSVC_base_flags /W3 /WX /D_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS)\n set( stlab_MSVC_debug_flags )\n set( stlab_MSVC_coverage_flags )\n set( stlab_MSVC_release_flags )"},{"sha":"b8fc54f6887c7e3cbf70a31c0d5d4639dd0f827c","filename":"conanfile.txt","status":"modified","additions":1,"deletions":1,"changes":2,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/conanfile.txt","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/conanfile.txt","contents_url":"https://api.github.com/repos/stlab/stlab/contents/conanfile.txt?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -1,5 +1,5 @@\n [requires]\n-boost/1.66.0@conan/stable\n+boost/1.68.0@conan/stable\n \n [generators]\n cmake"},{"sha":"3998c05438d20657502fdaa70a4f653d06af3637","filename":"stlab/concurrency/channel.hpp","status":"modified","additions":258,"deletions":203,"changes":461,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Fchannel.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Fchannel.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/stlab%2Fconcurrency%2Fchannel.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -17,6 +17,7 @@\n #include <deque>\n #include <memory>\n #include <mutex>\n+#include <numeric>\n #include <tuple>\n #include <utility>\n \n@@ -25,6 +26,7 @@\n #include <stlab/concurrency/traits.hpp>\n #include <stlab/concurrency/tuple_algorithm.hpp>\n #include <stlab/concurrency/variant.hpp>\n+#include <stlab/functional.hpp>\n #include <stlab/memory.hpp>\n \n /**************************************************************************************************/\n@@ -195,23 +197,21 @@ auto invoke_(F&& f, std::tuple<variant<T, std::exception_ptr>...>& t, std::index\n \n template <typename F, typename... Args>\n auto avoid_invoke(F&& f, std::tuple<variant<Args, std::exception_ptr>...>& t)\n-    -> std::enable_if_t<!std::is_same<void, yield_type<F, Args...>>::value,\n-                        yield_type<F, Args...>> {\n+    -> std::enable_if_t<!std::is_same<void, yield_type<unwrap_reference_t<F>, Args...>>::value,\n+                        yield_type<unwrap_reference_t<F>, Args...>> {\n     return invoke_(std::forward<F>(f), t, std::make_index_sequence<sizeof...(Args)>());\n }\n \n template <typename F, typename... Args>\n auto avoid_invoke(F&& f, std::tuple<variant<Args, std::exception_ptr>...>& t)\n-    -> std::enable_if_t<std::is_same<void, yield_type<F, Args...>>::value, avoid_> {\n+    -> std::enable_if_t<std::is_same<void, yield_type<unwrap_reference_t<F>, Args...>>::value,\n+                        avoid_> {\n     invoke_(std::forward<F>(f), t, std::make_index_sequence<sizeof...(Args)>());\n     return avoid_();\n }\n \n /**************************************************************************************************/\n \n-// The following can be much simplified with if constexpr() in C++17 and w/o a bug in clang and VS\n-// TODO std::variant make T a forwarding ref when the dependency to boost is gone.\n-\n template <std::size_t S>\n struct invoke_variant_dispatcher {\n     template <typename F, typename T, typename... Args, std::size_t... I>\n@@ -230,7 +230,13 @@ template <>\n struct invoke_variant_dispatcher<1> {\n     template <typename F, typename T, typename Arg>\n     static auto invoke_(F&& f, T& t) {\n-        return std::forward<F>(f)(std::move(stlab::get<Arg>(std::get<0>(t))));\n+        if constexpr (std::is_same<Arg, void>::value) {\n+            return;\n+        } else if constexpr (std::is_same<Arg, detail::avoid_>::value) {\n+            return std::forward<F>(f)();\n+        } else {\n+            return std::forward<F>(f)(std::move(stlab::get<Arg>(std::get<0>(t))));\n+        }\n     }\n     template <typename F, typename T, typename... Args>\n     static auto invoke(F&& f, T& t) {\n@@ -279,6 +285,7 @@ struct shared_process_sender {\n     virtual void send(std::exception_ptr) = 0;\n     virtual void add_sender() = 0;\n     virtual void remove_sender() = 0;\n+    virtual std::size_t free_buffer() const = 0;\n };\n \n /**************************************************************************************************/\n@@ -290,12 +297,14 @@ template <typename T>\n constexpr bool has_process_close_v = is_detected_v<process_close_t, T>;\n \n template <typename T>\n-auto process_close(stlab::optional<T>& x) -> std::enable_if_t<has_process_close_v<T>> {\n-    if (x) (*x).close();\n+auto process_close(stlab::optional<T>& x)\n+    -> std::enable_if_t<has_process_close_v<unwrap_reference_t<T>>> {\n+    if (x) unwrap(*x).close();\n }\n \n template <typename T>\n-auto process_close(stlab::optional<T>&) -> std::enable_if_t<!has_process_close_v<T>> {}\n+auto process_close(stlab::optional<T>&)\n+    -> std::enable_if_t<!has_process_close_v<unwrap_reference_t<T>>> {}\n \n /**************************************************************************************************/\n \n@@ -307,33 +316,34 @@ constexpr bool has_process_state_v = is_detected_v<process_state_t, T>;\n \n template <typename T>\n auto get_process_state(const stlab::optional<T>& x)\n-    -> std::enable_if_t<has_process_state_v<T>, process_state_scheduled> {\n-    return (*x).state();\n+    -> std::enable_if_t<has_process_state_v<unwrap_reference_t<T>>, process_state_scheduled> {\n+    return unwrap(*x).state();\n }\n \n template <typename T>\n auto get_process_state(const stlab::optional<T>&)\n-    -> std::enable_if_t<!has_process_state_v<T>, process_state_scheduled> {\n+    -> std::enable_if_t<!has_process_state_v<unwrap_reference_t<T>>, process_state_scheduled> {\n     return await_forever;\n }\n \n /**************************************************************************************************/\n \n template <typename P>\n-using process_set_error_t = decltype(std::declval<P&>().set_error(std::declval<std::exception_ptr>()));\n+using process_set_error_t =\n+    decltype(std::declval<P&>().set_error(std::declval<std::exception_ptr>()));\n \n template <typename P>\n constexpr bool has_set_process_error_v = is_detected_v<process_set_error_t, P>;\n \n template <typename P>\n auto set_process_error(P& process, std::exception_ptr&& error)\n-    -> std::enable_if_t<has_set_process_error_v<P>, void> {\n-    process.set_error(std::move(error));\n+    -> std::enable_if_t<has_set_process_error_v<unwrap_reference_t<P>>, void> {\n+    unwrap(process).set_error(std::move(error));\n }\n \n template <typename P>\n-auto set_process_error(P&, std::exception_ptr&&)\n-    -> std::enable_if_t<!has_set_process_error_v<P>, void> {}\n+auto set_process_error(P&, std::exception_ptr &&)\n+    -> std::enable_if_t<!has_set_process_error_v<unwrap_reference_t<P>>, void> {}\n \n /**************************************************************************************************/\n \n@@ -345,15 +355,24 @@ constexpr bool has_process_yield_v = is_detected_v<process_yield_t, T>;\n \n /**************************************************************************************************/\n \n+template <typename T, typename... Args>\n+using process_await_t = decltype(std::declval<T&>().await(std::declval<Args>()...));\n+\n+template <typename T, typename... Args>\n+constexpr bool has_process_await_v = is_detected_v<process_await_t, T, Args...>;\n+\n+/**************************************************************************************************/\n+\n template <typename P, typename... T, std::size_t... I>\n void await_variant_args_(P& process,\n-                         std::tuple<variant<T, std::exception_ptr>...>& args,\n+                         std::tuple<variant<detail::avoid<T>, std::exception_ptr>...>& args,\n                          std::index_sequence<I...>) {\n-    process.await(std::move(stlab::get<T>(std::get<I>(args)))...);\n+    unwrap(process).await(std::move(stlab::get<T>(std::get<I>(args)))...);\n }\n \n template <typename P, typename... T>\n-void await_variant_args(P& process, std::tuple<variant<T, std::exception_ptr>...>& args) {\n+void await_variant_args(P& process,\n+                        std::tuple<variant<detail::avoid<T>, std::exception_ptr>...>& args) {\n     await_variant_args_<P, T...>(process, args, std::make_index_sequence<sizeof...(T)>());\n }\n \n@@ -368,12 +387,9 @@ stlab::optional<std::exception_ptr> find_argument_error(T& argument) {\n     });\n \n     if (error_index != std::tuple_size<T>::value) {\n-        result =\n-            get_i(argument, error_index,\n-                  [](auto&& elem) {\n-                      return stlab::get<std::exception_ptr>(std::forward<decltype(elem)>(elem));\n-                  },\n-                  std::exception_ptr{});\n+        result = get_i(\n+            argument, error_index, [](auto& elem) { return stlab::get<std::exception_ptr>(elem); },\n+            std::exception_ptr{});\n     }\n \n     return result;\n@@ -384,9 +400,9 @@ stlab::optional<std::exception_ptr> find_argument_error(T& argument) {\n template <typename T>\n struct default_queue_strategy {\n     static const std::size_t arguments_size = 1;\n-    using value_type = std::tuple<variant<T, std::exception_ptr>>;\n+    using value_type = std::tuple<variant<avoid<T>, std::exception_ptr>>;\n \n-    std::deque<variant<T, std::exception_ptr>> _queue;\n+    std::deque<variant<avoid<T>, std::exception_ptr>> _queue;\n \n     bool empty() const { return _queue.empty(); }\n \n@@ -396,6 +412,11 @@ struct default_queue_strategy {\n \n     auto size() const { return std::array<std::size_t, 1>{{_queue.size()}}; }\n \n+    template <std::size_t>\n+    auto queue_size() const {\n+        return _queue.size();\n+    }\n+\n     template <std::size_t, typename U>\n     void append(U&& u) {\n         _queue.emplace_back(std::forward<U>(u));\n@@ -439,6 +460,11 @@ struct zip_with_queue_strategy {\n         return result;\n     }\n \n+    template <std::size_t I>\n+    auto queue_size() const {\n+        return std::get<I>(_queue).size();\n+    }\n+\n     template <std::size_t I, typename U>\n     void append(U&& u) {\n         std::get<I>(_queue).emplace_back(std::forward<U>(u));\n@@ -460,12 +486,14 @@ struct round_robin_queue_strategy {\n     queue_t _queue;\n \n     bool empty() const {\n-        return get_i(_queue, _index, [](const auto& c) { return c.empty(); }, true);\n+        return get_i(\n+            _queue, _index, [](const auto& c) { return c.empty(); }, true);\n     }\n \n     auto front() {\n         assert(!empty() && \"front on an empty container is a very bad idea!\");\n-        return std::make_tuple(get_i(_queue, _index, [](auto& c) { return c.front(); }, item_t{}));\n+        return std::make_tuple(get_i(\n+            _queue, _index, [](auto& c) { return c.front(); }, item_t{}));\n     }\n \n     void pop_front() {\n@@ -488,6 +516,11 @@ struct round_robin_queue_strategy {\n         return result;\n     }\n \n+    template <std::size_t I>\n+    auto queue_size() const {\n+        return std::get<I>(_queue).size();\n+    }\n+\n     template <std::size_t I, typename U>\n     void append(U&& u) {\n         std::get<I>(_queue).emplace_back(std::forward<U>(u));\n@@ -515,7 +548,8 @@ struct unordered_queue_strategy {\n     auto front() {\n         assert(!empty() && \"front on an empty container is a very bad idea!\");\n         _index = tuple_find(_queue, [](const auto& c) { return !c.empty(); });\n-        return std::make_tuple(get_i(_queue, _index, [](auto& c) { return c.front(); }, item_t{}));\n+        return std::make_tuple(get_i(\n+            _queue, _index, [](auto& c) { return std::move(c.front()); }, item_t{}));\n     }\n \n     void pop_front() {\n@@ -537,6 +571,11 @@ struct unordered_queue_strategy {\n         return result;\n     }\n \n+    template <std::size_t I>\n+    auto queue_size() const {\n+        return std::get<I>(_queue).size();\n+    }\n+\n     template <std::size_t I, typename U>\n     void append(U&& u) {\n         std::get<I>(_queue).emplace_back(std::forward<U>(u));\n@@ -587,7 +626,15 @@ struct shared_process_sender_indexed : public shared_process_sender<Arg> {\n \n     void send(std::exception_ptr error) override { enqueue(std::move(error)); }\n \n-    void send(Arg arg) override { enqueue(std::move(arg)); }\n+    void send(avoid<Arg> arg) override { enqueue(std::move(arg)); }\n+\n+    std::size_t free_buffer() const override {\n+        std::unique_lock<std::mutex> lock(_shared_process._process_mutex);\n+        return _shared_process._process_buffer_size == 0 ?\n+                   std::numeric_limits<std::size_t>::max() :\n+                   (_shared_process._process_buffer_size -\n+                    _shared_process._queue.template queue_size<I>());\n+    }\n };\n \n /**************************************************************************************************/\n@@ -626,6 +673,16 @@ struct downstream<\n     void send(std::size_t n, Args... args) {\n         stlab::for_each_n(begin(_data), n, [&](const auto& e) { e(args...); });\n     }\n+\n+    std::size_t minimum_free_buffer() const {\n+        if (size() == 0) return 0;\n+        // std::reduce with C++17\n+        return std::accumulate(_data.cbegin(), _data.cend(),\n+                               std::numeric_limits<std::size_t>::max(),\n+                               [](auto val, const auto& e) {\n+                                   return std::min(val, e.free_buffer() ? *e.free_buffer() : val);\n+                               });\n+    }\n };\n \n template <typename R>\n@@ -647,6 +704,11 @@ struct downstream<\n     void send(std::size_t, Args&&... args) {\n         if (_data) (*_data)(std::forward<Args>(args)...);\n     }\n+\n+    std::size_t minimum_free_buffer() const {\n+        if (_data && (*_data).free_buffer()) return *(*_data).free_buffer();\n+        return 0;\n+    }\n };\n \n /**************************************************************************************************/\n@@ -656,23 +718,25 @@ struct shared_process\n     : shared_process_receiver<R>,\n       shared_process_sender_helper<Q, T, R, std::make_index_sequence<sizeof...(Args)>, Args...>,\n       std::enable_shared_from_this<shared_process<Q, T, R, Args...>> {\n-    static_assert((has_process_yield_v<T> && has_process_state_v<T>) ||\n-                      (!has_process_yield_v<T> && !has_process_state_v<T>),\n+    static_assert((has_process_yield_v<unwrap_reference_t<T>> &&\n+                   has_process_state_v<unwrap_reference_t<T>>) ||\n+                      (!has_process_yield_v<unwrap_reference_t<T>> &&\n+                       !has_process_state_v<unwrap_reference_t<T>>),\n                   \"Processes that use .yield() must have .state() const\");\n \n     /*\n         the downstream continuations are stored in a deque so we don't get reallocations\n         on push back - this allows us to make calls while additional inserts happen.\n     */\n \n-    using result = R;\n-    using queue_strategy = Q;\n+    using result_t = R;\n+    using queue_strategy_t = Q;\n     using process_t = T;\n     using lock_t = std::unique_lock<std::mutex>;\n \n     std::mutex _downstream_mutex;\n-    downstream<R> _downstream;\n-    queue_strategy _queue;\n+    downstream<result_t> _downstream;\n+    queue_strategy_t _queue;\n \n     executor_t _executor;\n     stlab::optional<process_t> _process;\n@@ -695,13 +759,15 @@ struct shared_process\n \n     const std::tuple<std::shared_ptr<shared_process_receiver<Args>>...> _upstream;\n \n+\n+\n     template <typename E, typename F>\n     shared_process(E&& e, F&& f) :\n         shared_process_sender_helper<Q, T, R, std::make_index_sequence<sizeof...(Args)>, Args...>(\n             *this),\n         _executor(std::forward<E>(e)), _process(std::forward<F>(f)) {\n-        _sender_count = 1;\n-        _receiver_count = !std::is_same<result, void>::value;\n+        _sender_count = std::is_same<result_t, void>::value ? 0 : 1;\n+        _receiver_count = !std::is_same<result_t, void>::value;\n     }\n \n     template <typename E, typename F, typename... U>\n@@ -711,16 +777,16 @@ struct shared_process\n         _executor(std::forward<E>(e)), _process(std::forward<F>(f)),\n         _upstream(std::forward<U>(u)...) {\n         _sender_count = sizeof...(Args);\n-        _receiver_count = !std::is_same<result, void>::value;\n+        _receiver_count = !std::is_same<result_t, void>::value;\n     }\n \n     void add_receiver() override {\n-        if (std::is_same<result, void>::value) return;\n+        if (std::is_same<result_t, void>::value) return;\n         ++_receiver_count;\n     }\n \n     void remove_receiver() override {\n-        if (std::is_same<result, void>::value) return;\n+        if (std::is_same<result_t, void>::value) return;\n         /*\n             NOTE (sparent) : Decrementing the receiver count can allow this to start running on a\n             send before we can get to the check - so we need to see if we are already running\n@@ -731,7 +797,9 @@ struct shared_process\n             bool do_run;\n             {\n                 std::unique_lock<std::mutex> lock(_process_mutex);\n-                do_run = (!_queue.empty() || _process_close_queue) && !_process_running;\n+                do_run = ((!_queue.empty() || std::is_same<first_t<Args...>, void>::value) ||\n+                          _process_close_queue) &&\n+                         !_process_running;\n                 _process_running = _process_running || do_run;\n             }\n             if (do_run) run();\n@@ -756,15 +824,14 @@ struct shared_process\n         if (do_run) run();\n         if (do_final) {\n             std::unique_lock<std::mutex> lock(_downstream_mutex);\n-            _downstream.clear(); // This will propogate the close to anything downstream\n+            _downstream.clear(); // This will propagate the close to anything downstream\n             _process = nullopt;\n         }\n     }\n \n     void clear_to_send() override {\n         {\n             std::unique_lock<std::mutex> lock(_process_mutex);\n-            // TODO FP I am not sure if this is the correct way to handle an closed upstream\n             if (_process_final) {\n                 return;\n             }\n@@ -774,9 +841,8 @@ struct shared_process\n         {\n             const auto ps = get_process_state(_process);\n             std::unique_lock<std::mutex> lock(_process_mutex);\n-            assert(_process_suspend_count > 0 && \"Error: Try to unsuspend, but not suspended!\");\n-            --_process_suspend_count; // could be atomic?\n-            assert(_process_running && \"ERROR (sparent) : clear_to_send but not running!\");\n+            if (_process_suspend_count > 0) --_process_suspend_count; // could be atomic?\n+\n             if (!_process_suspend_count) {\n                 if (ps.first == process_state::yield || !_queue.empty() || _process_close_queue) {\n                     do_run = true;\n@@ -803,11 +869,8 @@ struct shared_process\n             message = std::move(_queue.front());\n             _queue.pop_front();\n             auto queue_size = _queue.size();\n-            std::size_t i{0};\n-            std::for_each(queue_size.begin(), queue_size.end(), [&do_cts, &i, this](auto size) {\n-                do_cts[i] = size <= (_process_buffer_size - 1);\n-                ++i;\n-            });\n+            for (auto index = 0u; index < queue_size.size(); ++index)\n+                do_cts[index] = queue_size[index] <= (_process_buffer_size - 1);\n         }\n         return std::make_tuple(std::move(message), do_cts, do_close);\n     }\n@@ -835,19 +898,17 @@ struct shared_process\n                     do_close = true;\n             } else\n                 await_variant_args<process_t, Args...>(*_process, *message);\n-\t\t\t\t}\n-\t\t\t\telse {\n-\t\t\t\t\t\tdo_close = true;\n-\t\t\t\t}\n-\t\t\t\t\n-\t\t\t\tif (do_close)\n-            process_close(_process);\n+        } else {\n+            do_close = true;\n+        }\n+\n+        if (do_close) process_close(_process);\n \n         return bool(message);\n     }\n \n     template <typename U>\n-    auto step() -> std::enable_if_t<has_process_yield_v<U>> {\n+    auto step() -> std::enable_if_t<has_process_yield_v<unwrap_reference_t<U>>> {\n         // in case that the timeout function is just been executed then we have to re-schedule\n         // the current run\n         lock_t lock(_timeout_function_control, std::try_to_lock);\n@@ -862,10 +923,13 @@ struct shared_process\n             is done on yield()\n         */\n         try {\n-            while (get_process_state(_process).first == process_state::await) {\n-                if (!dequeue()) break;\n+            if constexpr (has_process_await_v<unwrap_reference_t<T>, Args...>) {\n+                while (get_process_state(_process).first == process_state::await) {\n+                    if (!dequeue()) break;\n+                }\n+            } else {\n+                if (get_process_state(_process).first == process_state::await) return;\n             }\n-\n             auto now = std::chrono::steady_clock::now();\n             process_state state;\n             std::chrono::steady_clock::time_point when;\n@@ -877,7 +941,7 @@ struct shared_process\n             */\n             if (state == process_state::yield) {\n                 if (when <= now)\n-                    broadcast((*_process).yield());\n+                    broadcast(unwrap(*_process).yield());\n                 else\n                     execute_at(when,\n                                _executor)([_weak_this = make_weak_ptr(this->shared_from_this())] {\n@@ -897,7 +961,7 @@ struct shared_process\n             else if (when == std::chrono::steady_clock::time_point::max()) {\n                 task_done();\n             } else if (when <= now) {\n-                broadcast((*_process).yield());\n+                broadcast(unwrap(*_process).yield());\n             } else {\n                 /* Schedule a timeout. */\n                 _timeout_function_active = true;\n@@ -908,15 +972,10 @@ struct shared_process\n \n                     // try_lock can fail spuriously\n                     while (true) {\n-                        // we were cancelled\n-                        // if (!_this->_timeout_function_active) return;\n-\n                         lock_t lock(_this->_timeout_function_control, std::try_to_lock);\n                         if (!lock) continue;\n \n                         // we were cancelled\n-                        // if (!_this->_timeout_function_active) return;\n-\n                         if (get_process_state(_this->_process).first != process_state::yield) {\n                             _this->try_broadcast();\n                             _this->_timeout_function_active = false;\n@@ -932,7 +991,7 @@ struct shared_process\n \n     void try_broadcast() {\n         try {\n-            if (_process) broadcast((*_process).yield());\n+            if (_process) broadcast(unwrap(*_process).yield());\n         } catch (...) {\n             broadcast(std::move(std::current_exception()));\n         }\n@@ -944,7 +1003,7 @@ struct shared_process\n     */\n \n     template <typename U>\n-    auto step() -> std::enable_if_t<!has_process_yield_v<U>> {\n+    auto step() -> std::enable_if_t<!has_process_yield_v<unwrap_reference_t<U>>> {\n         using queue_t = typename Q::value_type;\n         stlab::optional<queue_t> message;\n         std::array<bool, sizeof...(Args)> do_cts;\n@@ -991,38 +1050,36 @@ struct shared_process\n         */\n \n         std::size_t n;\n+        bool suspend_process;\n         {\n             std::unique_lock<std::mutex> lock(_downstream_mutex);\n             n = _downstream.size();\n+            suspend_process = _downstream.minimum_free_buffer() <= 1;\n         }\n \n-        /*\n-            There is no need for a lock here because the other processes that could change\n-            _process_suspend_count must all be ready (and not running).\n-\n-            But we'll assert that to be sure!\n-        */\n-        // std::unique_lock<std::mutex> lock(_process_mutex);\n-        assert(_process_suspend_count == 0 && \"broadcasting while suspended\");\n-\n-        /*\n-            Suspend for however many downstream processes we have + 1 for this process - that\n-            ensures that we are not kicked to run while still broadcasting.\n-        */\n-\n-        _process_suspend_count = n + 1;\n-\n+        {\n+            std::unique_lock<std::mutex> lock(_process_mutex);\n+            if (suspend_process) {\n+                /*\n+                    Suspend for however many downstream processes we have + 1 for this process -\n+                   that ensures that we are not kicked to run while still broadcasting.\n+                */\n+                _process_suspend_count = n + 1;\n+            } else\n+                _process_suspend_count = 1;\n+        }\n         /*\n             There is no lock on _downstream here. We only ever append to this deque so the first\n             n elements are good.\n         */\n \n         _downstream.send(n, std::forward<A>(args)...);\n \n-        clear_to_send(); // unsuspend this process\n+        clear_to_send(); // unsuspend this process and decrement the _process_suspend_count\n+                         // immediately by 1\n     }\n \n-    void map(sender<result> f) override {\n+    void map(sender<result_t> f) override {\n         /*\n             REVISIT (sparent) : If we are in a final state then we should destruct the sender\n             and not add it here.\n@@ -1042,25 +1099,21 @@ struct shared_process\n \n } // namespace detail\n \n-struct unordered_t\n-{\n-    template<typename...R>\n+struct unordered_t {\n+    template <typename... R>\n     using strategy_type = detail::unordered_queue_strategy<detail::receiver_t<R>...>;\n };\n \n-struct round_robin_t\n-{\n-    template<typename...R>\n+struct round_robin_t {\n+    template <typename... R>\n     using strategy_type = detail::round_robin_queue_strategy<detail::receiver_t<R>...>;\n };\n \n-struct zip_with_t\n-{\n-    template<typename...R>\n+struct zip_with_t {\n+    template <typename... R>\n     using strategy_type = detail::zip_with_queue_strategy<detail::receiver_t<R>...>;\n };\n \n-\n /**************************************************************************************************/\n \n namespace detail {\n@@ -1071,9 +1124,9 @@ struct channel_combiner {\n     template <typename P, typename URP, typename... R, std::size_t... I>\n     static void map_as_sender_(P& p, URP& upstream_receiver_processes, std::index_sequence<I...>) {\n         using shared_process_t = typename P::element_type;\n-        using queue_t = typename shared_process_t::queue_strategy;\n+        using queue_t = typename shared_process_t::queue_strategy_t;\n         using process_t = typename shared_process_t::process_t;\n-        using result_t = typename shared_process_t::result;\n+        using result_t = typename shared_process_t::result_t;\n \n         (void)std::initializer_list<int>{\n             (std::get<I>(upstream_receiver_processes)\n@@ -1091,62 +1144,78 @@ struct channel_combiner {\n     }\n \n     template <typename M, typename F, typename... R>\n-    struct merge_result\n-    {\n-        using type = yield_type<F, receiver_t<first_t<R...>>>;\n+    struct merge_result {\n+        using type = yield_type<unwrap_reference_t<F>, receiver_t<first_t<R...>>>;\n     };\n \n     template <typename F, typename... R>\n-    struct merge_result<zip_with_t, F, R...>\n-    {\n-        using type = yield_type<F, receiver_t<R>...>;\n+    struct merge_result<zip_with_t, F, R...> {\n+        using type = yield_type<unwrap_reference_t<F>, receiver_t<R>...>;\n     };\n \n     template <typename M, typename S, typename F, typename... R>\n     static auto merge_helper(S&& s, F&& f, R&&... upstream_receiver) {\n-\n         using result_t = typename merge_result<M, F, R...>::type;\n \n         auto upstream_receiver_processes = std::make_tuple(upstream_receiver._p...);\n-        auto merge_process = std::make_shared<\n-            shared_process<typename M::template strategy_type<R...>, F, result_t, receiver_t<R>...>>(\n-            std::forward<S>(s), std::forward<F>(f), upstream_receiver._p...);\n+        auto merge_process =\n+            std::make_shared<shared_process<typename M::template strategy_type<R...>, F, result_t,\n+                                            receiver_t<R>...>>(\n+                std::forward<S>(s), std::forward<F>(f), upstream_receiver._p...);\n \n         map_as_sender<decltype(merge_process), decltype(upstream_receiver_processes),\n-            receiver_t<R>...>(merge_process, upstream_receiver_processes);\n+                      receiver_t<R>...>(merge_process, upstream_receiver_processes);\n \n         return receiver<result_t>(std::move(merge_process));\n     }\n };\n \n-struct zip_helper\n-{\n+struct zip_helper {\n     template <typename... T>\n     auto operator()(T&&... t) const {\n         return std::make_tuple(std::forward<T>(t)...);\n     }\n };\n \n+template <typename E, typename T>\n+struct channel_ {\n+    static auto create(E executor) {\n+        auto p = std::make_shared<\n+            detail::shared_process<detail::default_queue_strategy<T>, identity, T, T>>(\n+            std::move(executor), identity());\n+\n+        return std::make_pair(sender<T>(p), receiver<T>(p));\n+    }\n+};\n+\n+template <typename E>\n+struct channel_<E, void> {\n+    static auto create(E executor) {\n+        auto p = std::make_shared<\n+            detail::shared_process<detail::default_queue_strategy<void>, identity, void, void>>(\n+            std::move(executor), identity());\n+\n+        return receiver<void>(p);\n+    }\n+};\n+\n /**************************************************************************************************/\n \n } // namespace detail\n \n /**************************************************************************************************/\n \n-template <typename T, typename S>\n-auto channel(S s) -> std::pair<sender<T>, receiver<T>> {\n-    auto p =\n-        std::make_shared<detail::shared_process<detail::default_queue_strategy<T>, identity, T, T>>(\n-            std::move(s), identity());\n-    return std::make_pair(sender<T>(p), receiver<T>(p));\n+template <typename T, typename E>\n+auto channel(E executor) {\n+    return detail::channel_<E, T>::create(std::move(executor));\n }\n \n /**************************************************************************************************/\n \n template <typename S, typename F, typename... R>\n [[deprecated(\"Use zip_with\")]] auto join(S s, F f, R... upstream_receiver) {\n     return detail::channel_combiner::merge_helper<zip_with_t, S, F, R...>(\n-            std::move(s), std::move(f), std::forward<R>(upstream_receiver)...);\n+        std::move(s), std::move(f), std::forward<R>(upstream_receiver)...);\n }\n \n /**************************************************************************************************/\n@@ -1161,16 +1230,16 @@ template <typename S, typename F, typename... R>\n \n template <typename M, typename S, typename F, typename... R>\n auto merge_channel(S s, F f, R... upstream_receiver) {\n-    return detail::channel_combiner::merge_helper<M, S, F, R...>(\n-        std::move(s), std::move(f), std::move(upstream_receiver)...);\n+    return detail::channel_combiner::merge_helper<M, S, F, R...>(std::move(s), std::move(f),\n+                                                                 std::move(upstream_receiver)...);\n }\n \n /**************************************************************************************************/\n \n template <typename S, typename F, typename... R>\n auto zip_with(S s, F f, R... upstream_receiver) {\n-    return detail::channel_combiner::merge_helper<zip_with_t, S, F, R...>(std::move(s), std::move(f),\n-                                               std::forward<R>(upstream_receiver)...);\n+    return detail::channel_combiner::merge_helper<zip_with_t, S, F, R...>(\n+        std::move(s), std::move(f), std::forward<R>(upstream_receiver)...);\n }\n \n /**************************************************************************************************/\n@@ -1185,16 +1254,10 @@ auto zip(S s, R... r) {\n \n /**************************************************************************************************/\n \n-struct buffer_size {\n-    const std::size_t _value;\n-\n-    explicit buffer_size(std::size_t v) : _value(v) {}\n-};\n-\n-struct executor {\n-    executor_t _e;\n-\n-    explicit executor(executor_t e) : _e(std::move(e)) {}\n+struct buffer_size \n+{\n+    std::size_t _value;\n+    buffer_size(std::size_t b) : _value(b) {}\n };\n \n /**************************************************************************************************/\n@@ -1205,6 +1268,7 @@ struct annotations {\n     stlab::optional<executor_t> _executor;\n     stlab::optional<std::size_t> _buffer_size;\n \n+    annotations(executor_t e, std::size_t bs) : _executor(std::move(e)), _buffer_size(bs) {}\n     explicit annotations(executor_t e) : _executor(std::move(e)) {}\n     explicit annotations(std::size_t bs) : _buffer_size(bs) {}\n };\n@@ -1216,89 +1280,60 @@ struct annotated_process {\n     F _f;\n     annotations _annotations;\n \n-    annotated_process(F f, const executor& e) : _f(std::move(f)), _annotations(e._e) {}\n-    annotated_process(F f, const buffer_size& bs) : _f(std::move(f)), _annotations(bs._value) {}\n+    explicit annotated_process(executor_task_pair<F>&& etp) : _f(std::move(etp._f)), _annotations(std::move(etp._executor)) {}\n+\n+    annotated_process(F f, const executor& e) : _f(std::move(f)), _annotations(e._executor) {}\n+    annotated_process(F f, buffer_size bs) : _f(std::move(f)), _annotations(bs._value) {}\n \n-    annotated_process(F f, executor&& e) : _f(std::move(f)), _annotations(std::move(e._e)) {}\n-    annotated_process(F f, buffer_size&& bs) : _f(std::move(f)), _annotations(bs._value) {}\n+    annotated_process(F f, executor&& e) : _f(std::move(f)), _annotations(std::move(e._executor)) {}\n     annotated_process(F f, annotations&& a) : _f(std::move(f)), _annotations(std::move(a)) {}\n+    annotated_process(executor_task_pair<F>&& etp, buffer_size bs) : _f(std::move(etp._f)), _annotations(std::move(etp._executor), bs) {}\n+    \n };\n \n template <typename B, typename E>\n detail::annotations combine_bs_executor(B&& b, E&& e) {\n     detail::annotations result{b._value};\n-    result._executor = std::forward<E>(e)._e;\n+    result._executor = std::forward<E>(e)._executor;\n     return result;\n }\n \n } // namespace detail\n \n-inline detail::annotations operator&(const buffer_size& bs, const executor& e) {\n+inline detail::annotations operator&(buffer_size bs, const executor& e) {\n     return detail::combine_bs_executor(bs, e);\n }\n \n-inline detail::annotations operator&(const buffer_size& bs, executor&& e) {\n+inline detail::annotations operator&(buffer_size bs, executor&& e) {\n     return detail::combine_bs_executor(bs, std::move(e));\n }\n \n-inline detail::annotations operator&(buffer_size&& bs, executor&& e) {\n-    return detail::combine_bs_executor(std::move(bs), std::move(e));\n-}\n-\n-inline detail::annotations operator&(buffer_size&& bs, const executor& e) {\n-    return detail::combine_bs_executor(std::move(bs), e);\n-}\n-\n-inline detail::annotations operator&(const executor& e, const buffer_size& bs) {\n+inline detail::annotations operator&(const executor& e, buffer_size bs) {\n     return detail::combine_bs_executor(bs, e);\n }\n \n-inline detail::annotations operator&(const executor& e, buffer_size&& bs) {\n-    return detail::combine_bs_executor(std::move(bs), e);\n-}\n-\n-inline detail::annotations operator&(executor&& e, buffer_size&& bs) {\n-    return detail::combine_bs_executor(std::move(bs), std::move(e));\n+inline detail::annotations operator&(executor&& e, buffer_size bs) {\n+    return detail::combine_bs_executor(bs, std::move(e));\n }\n \n template <typename F>\n-detail::annotated_process<F> operator&(const buffer_size& bs, F&& f) {\n+detail::annotated_process<F> operator&(buffer_size bs, F&& f) {\n     return detail::annotated_process<F>(std::forward<F>(f), bs);\n }\n \n template <typename F>\n-detail::annotated_process<F> operator&(buffer_size&& bs, F&& f) {\n-    return detail::annotated_process<F>(std::forward<F>(f), std::move(bs));\n-}\n-\n-template <typename F>\n-detail::annotated_process<F> operator&(F&& f, const buffer_size& bs) {\n+detail::annotated_process<F> operator&(F&& f, buffer_size bs) {\n     return detail::annotated_process<F>(std::forward<F>(f), bs);\n }\n \n template <typename F>\n-detail::annotated_process<F> operator&(F&& f, buffer_size&& bs) {\n-    return detail::annotated_process<F>(std::forward<F>(f), std::move(bs));\n-}\n-\n-template <typename F>\n-detail::annotated_process<F> operator&(const executor& e, F&& f) {\n-    return detail::annotated_process<F>(std::forward<F>(f), e);\n-}\n-\n-template <typename F>\n-detail::annotated_process<F> operator&(executor&& e, F&& f) {\n-    return detail::annotated_process<F>(std::forward<F>(f), std::move(e));\n-}\n-\n-template <typename F>\n-detail::annotated_process<F> operator&(F&& f, const executor& e) {\n-    return detail::annotated_process<F>(std::forward<F>(f), e);\n+detail::annotated_process<F> operator&(executor_task_pair<F>&& etp, buffer_size bs) {\n+    return detail::annotated_process<F>{std::move(etp), bs};\n }\n \n template <typename F>\n-detail::annotated_process<F> operator&(F&& f, executor&& e) {\n-    return detail::annotated_process<F>(std::forward<F>(f), std::move(e));\n+detail::annotated_process<F> operator&(buffer_size bs, executor_task_pair<F>&& etp) {\n+    return detail::annotated_process<F>{std::move(etp), bs};\n }\n \n template <typename F>\n@@ -1314,12 +1349,12 @@ detail::annotated_process<F> operator&(F&& f, detail::annotations&& a) {\n template <typename F>\n detail::annotated_process<F> operator&(detail::annotated_process<F>&& a, executor&& e) {\n     auto result{std::move(a)};\n-    a._annotations._executor = std::move(e._e);\n+    a._annotations._executor = std::move(e._executor);\n     return result;\n }\n \n template <typename F>\n-detail::annotated_process<F> operator&(detail::annotated_process<F>&& a, buffer_size&& bs) {\n+detail::annotated_process<F> operator&(detail::annotated_process<F>&& a, buffer_size bs) {\n     auto result{std::move(a)};\n     a._annotations._buffer_size = bs._value;\n     return result;\n@@ -1341,7 +1376,7 @@ class receiver {\n     friend class receiver;\n \n     template <typename U, typename V>\n-    friend auto channel(V) -> std::pair<sender<U>, receiver<U>>;\n+    friend struct detail::channel_;\n \n     friend struct detail::channel_combiner;\n \n@@ -1356,7 +1391,7 @@ class receiver {\n         if (!_ready && _p) _p->remove_receiver();\n     }\n \n-    receiver(const receiver& x) : _p(x._p) {\n+    receiver(const receiver& x) : _p(x._p), _ready(x._ready) {\n         if (_p) _p->add_receiver();\n     }\n \n@@ -1389,33 +1424,39 @@ class receiver {\n \n         if (_ready) throw channel_error(channel_error_codes::process_already_running);\n \n-        auto p = std::make_shared<detail::shared_process<detail::default_queue_strategy<T>, F,\n-                                                         detail::yield_type<F, T>, T>>(\n+        auto p = std::make_shared<detail::shared_process<\n+            detail::default_queue_strategy<T>, F, detail::yield_type<unwrap_reference_t<F>, T>, T>>(\n             _p->executor(), std::forward<F>(f), _p);\n         _p->map(sender<T>(p));\n-        return receiver<detail::yield_type<F, T>>(std::move(p));\n+        return receiver<detail::yield_type<unwrap_reference_t<F>, T>>(std::move(p));\n     }\n \n     template <typename F>\n-    auto operator|(detail::annotated_process<F>&& ap) {\n+    auto operator|(detail::annotated_process<F> ap) {\n         if (!_p) throw channel_error(channel_error_codes::broken_channel);\n \n         if (_ready) throw channel_error(channel_error_codes::process_already_running);\n \n-        auto executor = ap._annotations._executor.value_or(_p->executor());\n-        auto p = std::make_shared<detail::shared_process<detail::default_queue_strategy<T>, F,\n-                                                         detail::yield_type<F, T>, T>>(\n+        auto executor = std::move(ap._annotations._executor.value_or(_p->executor()));\n+        auto p = std::make_shared<detail::shared_process<\n+            detail::default_queue_strategy<T>, F, detail::yield_type<unwrap_reference_t<F>, T>, T>>(\n             executor, std::move(ap._f), _p);\n \n         _p->map(sender<T>(p));\n \n         if (ap._annotations._buffer_size) p->set_buffer_size(*ap._annotations._buffer_size);\n \n-        return receiver<detail::yield_type<F, T>>(std::move(p));\n+        return receiver<detail::yield_type<unwrap_reference_t<F>, T>>(std::move(p));\n+    }\n+\n+    template <typename F>\n+    auto operator|(executor_task_pair<F> etp) {\n+        return operator|(detail::annotated_process<F>(std::move(etp)));\n     }\n \n-    auto operator|(sender<T> send) const {\n-        return operator|([send](auto&& x) { send(std::forward<decltype(x)>(x)); });\n+    auto operator|(sender<T> send) {\n+        return operator|\n+            ([_send = std::move(send)](auto&& x) { _send(std::forward<decltype(x)>(x)); });\n     }\n };\n \n@@ -1430,7 +1471,7 @@ class sender<T, enable_if_copyable<T>> {\n     friend class receiver;\n \n     template <typename U, typename V>\n-    friend auto channel(V) -> std::pair<sender<U>, receiver<U>>;\n+    friend struct detail::channel_;\n \n     friend struct detail::channel_combiner;\n \n@@ -1480,6 +1521,13 @@ class sender<T, enable_if_copyable<T>> {\n         auto p = _p.lock();\n         if (p) p->send(std::forward<A>(args)...);\n     }\n+\n+    optional<std::size_t> free_buffer() const {\n+        optional<std::size_t> result;\n+        auto p = _p.lock();\n+        if (p) result = p->free_buffer();\n+        return result;\n+    }\n };\n \n template <typename T>\n@@ -1491,7 +1539,7 @@ class sender<T, enable_if_not_copyable<T>> {\n     friend class receiver;\n \n     template <typename U, typename V>\n-    friend auto channel(V) -> std::pair<sender<U>, receiver<U>>;\n+    friend struct detail::channel_;\n \n     friend struct detail::channel_combiner;\n \n@@ -1531,6 +1579,13 @@ class sender<T, enable_if_not_copyable<T>> {\n         auto p = _p.lock();\n         if (p) p->send(std::forward<A>(args)...);\n     }\n+\n+    optional<std::size_t> free_buffer() const {\n+        optional<std::size_t> result;\n+        auto p = _p.lock();\n+        if (p) result = p->free_buffer();\n+        return result;\n+    }\n };\n \n /**************************************************************************************************/"},{"sha":"28f004a2423a2c66fccb706d8c3bf0954c4da9cd","filename":"stlab/concurrency/executor_base.hpp","status":"modified","additions":21,"deletions":0,"changes":21,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Fexecutor_base.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Fexecutor_base.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/stlab%2Fconcurrency%2Fexecutor_base.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -53,6 +53,27 @@ auto execute_delayed(std::chrono::steady_clock::duration duration, E executor) {\n     return execute_at(std::chrono::steady_clock::now() + duration, std::move(executor));\n }\n \n+struct executor {\n+    executor_t _executor;\n+};\n+\n+template <typename F>\n+struct executor_task_pair {\n+    executor_t _executor;\n+    F _f;\n+};\n+\n+template <typename F>\n+executor_task_pair<F> operator&(executor e, F&& f) {\n+    return executor_task_pair<F>{std::move(e._executor), std::forward<F>(f)};\n+}\n+\n+template <typename F>\n+executor_task_pair<F> operator&(F&& f, executor e) {\n+    return executor_task_pair<F>{std::move(e._executor), std::forward<F>(f)};\n+}\n+\n+\n /**************************************************************************************************/\n \n } // namespace v1"},{"sha":"80436fed1e464fb513d6d54ae78d201d000b0cfe","filename":"stlab/concurrency/future.hpp","status":"modified","additions":166,"deletions":54,"changes":220,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Ffuture.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Ffuture.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/stlab%2Fconcurrency%2Ffuture.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -211,14 +211,14 @@ struct reduction_helper;\n /**************************************************************************************************/\n \n template <typename T, typename = void>\n-struct value_setter;\n+struct value_;\n \n } // namespace detail\n \n /**************************************************************************************************/\n \n template <typename Sig, typename E, typename F>\n-auto package(E&&, F &&)\n+auto package(E, F&&)\n     -> std::pair<detail::packaged_task_from_signature_t<Sig>, future<detail::result_of_t_<Sig>>>;\n \n /**************************************************************************************************/\n@@ -471,6 +471,8 @@ struct shared_base<T, enable_if_not_copyable<T>> : std::enable_shared_from_this<\n     template <typename R>\n     auto reduce(future<future<R>>&& r) -> future<R>;\n \n+    auto reduce(future<future<void>>&& r)->future<void>;\n+\n     void set_exception(std::exception_ptr error) {\n         _error = std::move(error);\n         then_t then;\n@@ -642,7 +644,7 @@ struct shared<R(Args...)> : shared_base<R>, shared_task<Args...> {\n                 this->set_value(_f, std::move(args)...);\n             } catch (...) {\n                 this->set_exception(std::current_exception());\n-            }\n+        }\n         _f = function_t();\n     }\n \n@@ -664,11 +666,11 @@ class packaged_task {\n     explicit packaged_task(ptr_t p) : _p(std::move(p)) {}\n \n     template <typename Signature, typename E, typename F>\n-    friend auto package(E&&, F &&) -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n-                                                future<detail::result_of_t_<Signature>>>;\n+    friend auto package(E, F&&) -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n+                                              future<detail::result_of_t_<Signature>>>;\n \n     template <typename Signature, typename E, typename F>\n-    friend auto package_with_broken_promise(E&&, F&&)\n+    friend auto package_with_broken_promise(E, F&&)\n         -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n                      future<detail::result_of_t_<Signature>>>;\n \n@@ -715,18 +717,18 @@ class future<T, enable_if_copyable<T>> {\n     explicit future(ptr_t p) : _p(std::move(p)) {}\n \n     template <typename Signature, typename E, typename F>\n-    friend auto package(E&&, F&&) -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n-                                                future<detail::result_of_t_<Signature>>>;\n+    friend auto package(E, F&&) -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n+                                             future<detail::result_of_t_<Signature>>>;\n \n     template <typename Signature, typename E, typename F>\n-    friend auto package_with_broken_promise(E&&, F &&)\n+    friend auto package_with_broken_promise(E, F &&)\n         -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n                      future<detail::result_of_t_<Signature>>>;\n \n     friend struct detail::shared_base<T>;\n \n     template <typename, typename>\n-    friend struct detail::value_setter;\n+    friend struct detail::value_;\n \n public:\n     using result_type = T;\n@@ -746,41 +748,81 @@ class future<T, enable_if_copyable<T>> {\n         return _p->then(std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(F&& f) const& {\n+        return then(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto then(E&& executor, F&& f) const& {\n         return _p->then(std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(executor_task_pair<F> etp) const& {\n+        return then(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     template <typename F>\n     auto then(F&& f) && {\n         return _p->then_r(unique_usage(_p), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(F&& f) && {\n+        return std::move(*this).then(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto then(E&& executor, F&& f) && {\n         return _p->then_r(unique_usage(_p), std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(executor_task_pair<F> etp) && {\n+        return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     template <typename F>\n     auto recover(F&& f) const& {\n         return _p->recover(std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(F&& f) const& {\n+        return recover(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto recover(E&& executor, F&& f) const& {\n         return _p->recover(std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(executor_task_pair<F> etp) const& {\n+        return recover(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     template <typename F>\n     auto recover(F&& f) && {\n         return _p->recover_r(unique_usage(_p), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(F&& f) && {\n+        return std::move(*this).recover(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto recover(E&& executor, F&& f) && {\n         return _p->recover_r(unique_usage(_p), std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(executor_task_pair<F> etp) && {\n+        return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     void detach() const {\n         then([_hold = _p](auto) {}, [](const auto&) {});\n     }\n@@ -806,16 +848,16 @@ class future<void, void> {\n     explicit future(ptr_t p) : _p(std::move(p)) {}\n \n     template <typename Signature, typename E, typename F>\n-    friend auto package(E&&, F &&) -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n-                                                future<detail::result_of_t_<Signature>>>;\n+    friend auto package(E, F&&) -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n+                                              future<detail::result_of_t_<Signature>>>;\n \n     template <typename Signature, typename E, typename F>\n-    friend auto package_with_broken_promise(E&&, F &&)\n+    friend auto package_with_broken_promise(E, F &&)\n         -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n                      future<detail::result_of_t_<Signature>>>;\n \n     template <typename, typename>\n-    friend struct detail::value_setter;\n+    friend struct detail::value_;\n \n     friend struct detail::shared_base<void>;\n \n@@ -837,41 +879,81 @@ class future<void, void> {\n         return _p->then(std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(F&& f) const& {\n+        return then(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto then(E&& executor, F&& f) const& {\n         return _p->then(std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(executor_task_pair<F> etp) const& {\n+        return then(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     template <typename F>\n     auto then(F&& f) && {\n         return _p->then_r(unique_usage(_p), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(F&& f) && {\n+        return std::move(*this).then(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto then(E&& executor, F&& f) && {\n         return _p->then_r(unique_usage(_p), std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(executor_task_pair<F> etp) && {\n+        return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     template <typename F>\n     auto recover(F&& f) const& {\n         return _p->recover(std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(F&& f) const& {\n+        return recover(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto recover(E&& executor, F&& f) const& {\n         return _p->recover(std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(executor_task_pair<F> etp) const& {\n+        return recover(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     template <typename F>\n     auto recover(F&& f) && {\n         return _p->recover_r(unique_usage(_p), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(F&& f) && {\n+        return std::move(*this).recover(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto recover(E&& executor, F&& f) && {\n         return _p->recover_r(unique_usage(_p), std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(executor_task_pair<F> etp) && {\n+        return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     void detach() const {\n         then([_hold = _p](auto) {}, []() {});\n     }\n@@ -896,18 +978,18 @@ class future<T, enable_if_not_copyable<T>> {\n     future(const future&) = default;\n \n     template <typename Signature, typename E, typename F>\n-    friend auto package(E&&, F &&) -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n-                                           future<detail::result_of_t_<Signature>>>;\n+    friend auto package(E, F &&) -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n+                                              future<detail::result_of_t_<Signature>>>;\n \n     template <typename Signature, typename E, typename F>\n-    friend auto package_with_broken_promise(E&&, F &&)\n+    friend auto package_with_broken_promise(E, F &&)\n         -> std::pair<detail::packaged_task_from_signature_t<Signature>,\n                      future<detail::result_of_t_<Signature>>>;\n \n     friend struct detail::shared_base<T>;\n \n     template <typename, typename>\n-    friend struct detail::value_setter;\n+    friend struct detail::value_;\n \n public:\n     using result_type = T;\n@@ -930,21 +1012,41 @@ class future<T, enable_if_not_copyable<T>> {\n         return _p->then_r(unique_usage(_p), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(F&& f) && {\n+        return std::move(*this).then(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto then(E&& executor, F&& f) && {\n         return _p->then_r(unique_usage(_p), std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator|(executor_task_pair<F> etp) && {\n+        return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     template <typename F>\n     auto recover(F&& f) && {\n         return _p->recover_r(unique_usage(_p), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(F&& f) && {\n+        return std::move(*this).recover(std::forward<F>(f));\n+    }\n+\n     template <typename E, typename F>\n     auto recover(E&& executor, F&& f) && {\n         return _p->recover_r(unique_usage(_p), std::forward<E>(executor), std::forward<F>(f));\n     }\n \n+    template <typename F>\n+    auto operator^(executor_task_pair<F> etp) && {\n+        return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);\n+    }\n+\n     void detach() const {\n         _p->then_r(unique_usage(_p), [_hold = _p](auto) {}, [](auto&&) {});\n     }\n@@ -961,17 +1063,17 @@ class future<T, enable_if_not_copyable<T>> {\n };\n \n template <typename Sig, typename E, typename F>\n-auto package(E&& executor, F&& f)\n+auto package(E executor, F&& f)\n     -> std::pair<detail::packaged_task_from_signature_t<Sig>, future<detail::result_of_t_<Sig>>> {\n-    auto p = std::make_shared<detail::shared<Sig>>(std::forward<E>(executor), std::forward<F>(f));\n+    auto p = std::make_shared<detail::shared<Sig>>(std::move(executor), std::forward<F>(f));\n     return std::make_pair(detail::packaged_task_from_signature_t<Sig>(p),\n                           future<detail::result_of_t_<Sig>>(p));\n }\n \n template <typename Sig, typename E, typename F>\n-auto package_with_broken_promise(E&& executor, F&& f)\n+auto package_with_broken_promise(E executor, F&& f)\n     -> std::pair<detail::packaged_task_from_signature_t<Sig>, future<detail::result_of_t_<Sig>>> {\n-    auto p = std::make_shared<detail::shared<Sig>>(std::forward<E>(executor), std::forward<F>(f));\n+    auto p = std::make_shared<detail::shared<Sig>>(std::move(executor), std::forward<F>(f));\n     auto result = std::make_pair(detail::packaged_task_from_signature_t<Sig>(p),\n                                  future<detail::result_of_t_<Sig>>(p));\n     result.second._p->_error =\n@@ -987,15 +1089,15 @@ namespace detail {\n template <typename F>\n struct assign_ready_future {\n     template <typename T>\n-    static void assign(T& x, F& f) {\n-        x = *(std::move(f).get_try());\n+    static void assign(T& x, F f) {\n+        x = std::move(*(std::move(f).get_try()));\n     }\n };\n \n template <>\n struct assign_ready_future<future<void>> {\n     template <typename T>\n-    static void assign(T& x, future<void>&) {\n+    static void assign(T& x, future<void>) {\n         x = std::move(typename T::value_type()); // to set the optional\n     }\n };\n@@ -1011,8 +1113,8 @@ struct when_all_shared {\n     packaged_task<> _f;\n \n     template <std::size_t index, typename FF>\n-    void done(FF& f) {\n-        assign_ready_future<FF>::assign(std::get<index>(_args), f);\n+    void done(FF&& f) {\n+        assign_ready_future<FF>::assign(std::get<index>(_args), std::forward<FF>(f));\n         if (--_remaining == 0) _f();\n     }\n \n@@ -1125,7 +1227,7 @@ auto apply_when_any_arg(F& f, P& p) {\n }\n \n template <std::size_t i, typename E, typename P, typename T>\n-void attach_when_arg_(E&& executor, const std::shared_ptr<P>& p, T a) {\n+void attach_when_arg_(E&& executor, std::shared_ptr<P>& p, T a) {\n     p->_holds[i] = std::move(a).recover(std::forward<E>(executor), [_w = std::weak_ptr<P>(p)](auto x) {\n         auto p = _w.lock();\n         if (!p) return;\n@@ -1134,18 +1236,18 @@ void attach_when_arg_(E&& executor, const std::shared_ptr<P>& p, T a) {\n         if (error) {\n             p->failure(*error);\n         } else {\n-            p->template done<i>(x);\n+            p->template done<i>(std::move(x));\n         }\n     });\n }\n \n template <typename E, typename P, typename... Ts, std::size_t... I>\n-void attach_when_args_(std::index_sequence<I...>, E&& executor, const std::shared_ptr<P>& p, Ts... a) {\n-    (void)std::initializer_list<int>{(attach_when_arg_<I>(std::forward<E>(executor), p, a), 0)...};\n+void attach_when_args_(std::index_sequence<I...>, E&& executor, std::shared_ptr<P>& p, Ts... a) {\n+    (void)std::initializer_list<int>{(attach_when_arg_<I>(std::forward<E>(executor), p, std::move(a)), 0)...};\n }\n \n template <typename E, typename P, typename... Ts>\n-void attach_when_args(E&& executor, const std::shared_ptr<P>& p, Ts... a) {\n+void attach_when_args(E&& executor, std::shared_ptr<P>& p, Ts... a) {\n     attach_when_args_(std::make_index_sequence<sizeof...(Ts)>(), std::forward<E>(executor), p, std::move(a)...);\n }\n \n@@ -1165,7 +1267,7 @@ auto when_all(E executor, F f, future<Ts>... args) {\n     });\n     shared->_f = std::move(p.first);\n \n-    detail::attach_when_args(std::move(executor), shared, std::move(args)...);\n+    detail::attach_when_args(executor, shared, std::move(args)...);\n \n     return std::move(p.second);\n }\n@@ -1184,7 +1286,7 @@ struct make_when_any {\n         });\n         shared->_f = std::move(p.first);\n \n-        detail::attach_when_args(std::move(executor), shared, std::move(arg), std::move(args)...);\n+        detail::attach_when_args(executor, shared, std::move(arg), std::move(args)...);\n \n         return std::move(p.second);\n     }\n@@ -1204,7 +1306,7 @@ struct make_when_any<void> {\n         });\n         shared->_f = std::move(p.first);\n \n-        detail::attach_when_args(std::move(executor), shared, std::move(args)...);\n+        detail::attach_when_args(executor, shared, std::move(args)...);\n \n         return std::move(p.second);\n     }\n@@ -1392,9 +1494,9 @@ struct common_context : CR {\n /**************************************************************************************************/\n \n template <typename C, typename E, typename T>\n-void attach_tasks(size_t index, E&& executor, const std::shared_ptr<C>& context, T&& a) {\n+void attach_tasks(size_t index, E executor, const std::shared_ptr<C>& context, T&& a) {\n     context->_holds[index] =\n-        std::move(a).recover(std::forward<E>(executor), [_context = std::weak_ptr<C>(context), _i = index](auto x) {\n+        std::move(a).recover(std::move(executor), [_context = std::weak_ptr<C>(context), _i = index](auto x) {\n             auto p = _context.lock();\n             if (!p) return;\n             auto error = x.error();\n@@ -1477,7 +1579,7 @@ auto when_all(E executor, F f, std::pair<I, I> range) {\n     }\n \n     return detail::create_range_of_futures<result_t, param_t, context_t>::do_it(\n-        std::move(executor), std::move(f), range.first, range.second);\n+        executor, std::move(f), range.first, range.second);\n }\n \n /**************************************************************************************************/\n@@ -1544,7 +1646,7 @@ struct reduction_helper<future<void>> {\n /**************************************************************************************************/\n \n template <typename T>\n-struct value_setter<T, enable_if_copyable<T>> {\n+struct value_<T, enable_if_copyable<T>> {\n     template <typename C>\n     static void proceed(C& sb) {\n         typename C::then_t then;\n@@ -1571,12 +1673,12 @@ struct value_setter<T, enable_if_copyable<T>> {\n                 .recover([_p = sb.shared_from_this()](future<R> f) {\n                     if (f.error()) {\n                         _p->_error = std::move(*f.error());\n-                        value_setter::proceed(*_p);\n+                        proceed(*_p);\n                         throw future_error(future_error_codes::reduction_failed);\n                     }\n                     return *f.get_try();\n                 })\n-                .then([_p = sb.shared_from_this()](auto) { value_setter::proceed(*_p); });\n+                .then([_p = sb.shared_from_this()](auto) { proceed(*_p); });\n     }\n \n     template <typename F, typename... Args>\n@@ -1585,7 +1687,7 @@ struct value_setter<T, enable_if_copyable<T>> {\n                          .recover([_p = sb.shared_from_this()](future<void> f) {\n                              if (f.error()) {\n                                  _p->_error = std::move(*f.error());\n-                                 value_setter::proceed(*_p);\n+                                 value_::proceed(*_p);\n                                  throw future_error(future_error_codes::reduction_failed);\n                              }\n                              return;\n@@ -1595,7 +1697,7 @@ struct value_setter<T, enable_if_copyable<T>> {\n };\n \n template <typename T>\n-struct value_setter<T, enable_if_not_copyable<T>> {\n+struct value_<T, enable_if_not_copyable<T>> {\n     template <typename C>\n     static void proceed(C& sb) {\n         typename C::then_t then;\n@@ -1615,18 +1717,23 @@ struct value_setter<T, enable_if_not_copyable<T>> {\n \n     template <typename R, typename F, typename... Args>\n     static void set(shared_base<future<R>>& sb, F& f, Args&&... args) {\n-        // On VS a static_assert works, on MAC not.\n-        assert(!\"Reduction on move-only types is not supported so far\");\n         sb._result = f(std::forward<Args>(args)...);\n         sb._reduction_helper.value =\n-            (*sb._result)\n-                .then([](auto&& f) { return std::forward<decltype(f)>(f); })\n-                .then([_p = sb.shared_from_this()](auto&) { proceed(*_p); });\n+            std::move(*sb._result)\n+                .recover([_p = sb.shared_from_this()](future<R> f) {\n+                    if (f.error()) {\n+                        _p->_error = std::move(*f.error());\n+                        proceed(*_p);\n+                        throw future_error(future_error_codes::reduction_failed);\n+                    }\n+                    return *f.get_try();\n+                })\n+                .then([_p = sb.shared_from_this()](auto) { proceed(*_p); });\n     }\n };\n \n template <>\n-struct value_setter<void> {\n+struct value_<void> {\n     template <typename C>\n     static void proceed(C& sb) {\n         typename C::then_t then;\n@@ -1651,18 +1758,18 @@ struct value_setter<void> {\n template <typename T>\n template <typename F, typename... Args>\n void shared_base<T, enable_if_copyable<T>>::set_value(F& f, Args&&... args) {\n-    value_setter<T>::set(*this, f, std::forward<Args>(args)...);\n+    value_<T>::set(*this, f, std::forward<Args>(args)...);\n }\n \n template <typename T>\n template <typename F, typename... Args>\n void shared_base<T, enable_if_not_copyable<T>>::set_value(F& f, Args&&... args) {\n-    value_setter<T>::set(*this, f, std::forward<Args>(args)...);\n+    value_<T>::set(*this, f, std::forward<Args>(args)...);\n }\n \n template <typename F, typename... Args>\n void shared_base<void>::set_value(F& f, Args&&... args) {\n-    value_setter<void>::set(*this, f, std::forward<Args>(args)...);\n+    value_<void>::set(*this, f, std::forward<Args>(args)...);\n }\n \n /**************************************************************************************************/\n@@ -1701,10 +1808,15 @@ auto shared_base<T, enable_if_copyable<T>>::reduce(future<future<R>>&& r) -> fut\n \n /**************************************************************************************************/\n \n+template <typename T>\n+auto shared_base<T, enable_if_not_copyable<T>>::reduce(future<future<void>>&& r) -> future<void> {\n+    return std::move(r).then([](auto&&){});\n+}\n+\n template <typename T>\n template <typename R>\n auto shared_base<T, enable_if_not_copyable<T>>::reduce(future<future<R>>&& r) -> future<R> {\n-    return std::move(r).then([](auto f) { return *f.get_try(); });\n+    return std::move(r).then([](auto&& f) { return *std::forward<future<R>>(f).get_try(); });\n }\n \n /**************************************************************************************************/\n@@ -1732,7 +1844,7 @@ auto shared_base<void>::reduce(future<future<R>>&& r) -> future<R> {\n \n /**************************************************************************************************/\n \n-#if STLAB_FUTURE_COROUTINES_SUPPORT\n+#ifdef STLAB_FUTURE_COROUTINES_SUPPORT\n \n template <typename T, typename... Args>\n struct std::experimental::coroutine_traits<stlab::future<T>, Args...> {"},{"sha":"4fdd575fa67c50c38546a82037b830ff43f90484","filename":"stlab/concurrency/task.hpp","status":"modified","additions":2,"deletions":2,"changes":4,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Ftask.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Ftask.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/stlab%2Fconcurrency%2Ftask.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -267,11 +267,11 @@ const typename task<R(Args...)>::concept task<R(Args...)>::template model<F, tru\n \n template <class R, class... Args>\n template <class F>\n-const typename task<R(Args...)>::concept task<R(Args...)>::template model<F, false>::_vtable;\n+const typename task<R(Args...)>::concept task<R(Args...)>::model<F, false>::_vtable;\n \n template <class R, class... Args>\n template <class F>\n-const typename task<R(Args...)>::concept task<R(Args...)>::template model<F, true>::_vtable;\n+const typename task<R(Args...)>::concept task<R(Args...)>::model<F, true>::_vtable;\n \n #endif\n "},{"sha":"d941c2fab43c6ab3fd5a4758aeba307b6ea656da","filename":"stlab/concurrency/tuple_algorithm.hpp","status":"modified","additions":3,"deletions":4,"changes":7,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Ftuple_algorithm.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fconcurrency%2Ftuple_algorithm.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/stlab%2Fconcurrency%2Ftuple_algorithm.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -124,9 +124,8 @@ void tuple_for_each(T& t, Op op) {\n  * The default value is returned, if the index is equal or greater to tuple_size\n  */\n template <typename T, typename F, typename D>\n-auto get_i(T& t, std::size_t index, F&& f, D&& default_v) {\n-    return detail::get_i_impl<0, std::tuple_size<T>::value>::go(t, index, std::forward<F>(f),\n-                                                                std::forward<D>(default_v));\n+auto get_i(T& t, std::size_t index, F f, D&& default_v) {\n+    return detail::get_i_impl<0, std::tuple_size<T>::value>::go(t, index, std::move(f), std::forward<D>(default_v));\n }\n \n /*\n@@ -150,7 +149,7 @@ constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)\n \n template <class F, class Tuple, std::size_t... I>\n constexpr decltype(auto) apply_optional_indexed_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {\n-    return std::forward<F>(f)(*std::get<I>(std::forward<Tuple>(t))...);\n+    return std::forward<F>(f)(std::move(*std::get<I>(std::forward<Tuple>(t)))...);\n }\n \n /**************************************************************************************************/"},{"sha":"3de18b3da31a698611c0c62042d7e2002f97582f","filename":"stlab/functional.hpp","status":"modified","additions":22,"deletions":0,"changes":22,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Ffunctional.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Ffunctional.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/stlab%2Ffunctional.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -48,6 +48,28 @@ constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value;\n \n /**************************************************************************************************/\n \n+template <typename T>\n+T& unwrap(T& val) {\n+    return val;\n+}\n+\n+template <typename T>\n+const T& unwrap(const T& val) {\n+  return val;\n+}\n+\n+template <typename T>\n+T& unwrap(std::reference_wrapper<T>& val) {\n+    return val.get();\n+}\n+\n+template <typename T>\n+const T& unwrap(const std::reference_wrapper<T>& val) {\n+  return val.get();\n+}\n+\n+/**************************************************************************************************/\n+\n } // namespace v1\n \n /**************************************************************************************************/"},{"sha":"f1faa2524a2fe114f0654f3c2a5662973b4cfd61","filename":"stlab/version.hpp","status":"modified","additions":2,"deletions":2,"changes":4,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fversion.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/stlab%2Fversion.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/stlab%2Fversion.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -19,13 +19,13 @@\n //  STLAB_VERSION / 100 % 1000 is the minor version\n //  STLAB_VERSION / 100000 is the major version\n \n-#define STLAB_VERSION 100303\n+#define STLAB_VERSION 100400\n \n //\n //  STLAB_LIB_VERSION must be defined to be the same as STLAB_VERSION\n //  but as a *string* in the form \"x_y[_z]\" where x is the major version\n //  number, y is the minor version number, and z is the patch level if not 0.\n \n-#define STLAB_LIB_VERSION \"1_3_3\"\n+#define STLAB_LIB_VERSION \"1_4_0\"\n \n #endif"},{"sha":"f71963d7de8c9d0cf59186858ce4a54d48b326ff","filename":"test/channel_test_helper.hpp","status":"modified","additions":4,"deletions":0,"changes":4,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Fchannel_test_helper.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Fchannel_test_helper.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Fchannel_test_helper.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -72,6 +72,10 @@ struct channel_test_fixture : channel_test_fixture_base {\n     std::array<stlab::receiver<T>, N> _receive;\n \n     channel_test_fixture() {\n+        test_reset();\n+    } \n+    \n+    void test_reset() {\n         for (std::size_t i = 0; i < N; i++)\n             std::tie(_send[i], _receive[i]) = stlab::channel<T>(stlab::default_executor);\n     }"},{"sha":"d49daf47dd7c3f0115feb2d726ac75296262244e","filename":"test/channel_tests.cpp","status":"modified","additions":378,"deletions":73,"changes":451,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Fchannel_tests.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Fchannel_tests.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Fchannel_tests.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -12,6 +12,7 @@\n #define STLAB_DISABLE_FUTURE_COROUTINES\n #endif\n \n+#include <queue>\n #include <stlab/concurrency/channel.hpp>\n #include <stlab/concurrency/default_executor.hpp>\n #include <stlab/concurrency/future.hpp>\n@@ -21,20 +22,21 @@\n #include \"channel_test_helper.hpp\"\n \n using namespace stlab;\n+using namespace std;\n using namespace channel_test_helper;\n \n BOOST_AUTO_TEST_CASE(int_sender) {\n     BOOST_TEST_MESSAGE(\"int sender\");\n \n-    stlab::sender<int> send;\n+    sender<int> send;\n \n     BOOST_REQUIRE_NO_THROW(send(42));\n }\n \n BOOST_AUTO_TEST_CASE(int_receiver) {\n     BOOST_TEST_MESSAGE(\"int receiver\");\n \n-    stlab::receiver<int> receive;\n+    receiver<int> receive;\n \n     BOOST_REQUIRE_EQUAL(false, receive.ready());\n \n@@ -57,10 +59,10 @@ BOOST_AUTO_TEST_CASE(int_channel) {\n BOOST_AUTO_TEST_CASE(int_channel_move_ctor_sender) {\n     BOOST_TEST_MESSAGE(\"int channel move ctor sender\");\n \n-    std::atomic_int result{0};\n+    atomic_int result{0};\n \n     auto check = _receive[0] | [&](int x) { result = x; };\n-    auto sut = std::move(_send[0]);\n+    auto sut = move(_send[0]);\n \n     _receive[0].set_ready();\n     sut(42);\n@@ -73,11 +75,11 @@ BOOST_AUTO_TEST_CASE(int_channel_move_ctor_sender) {\n BOOST_AUTO_TEST_CASE(int_channel_move_assignment_sender) {\n     BOOST_TEST_MESSAGE(\"int channel move assignment sender\");\n \n-    stlab::sender<int> sut;\n-    std::atomic_int result{0};\n+    sender<int> sut;\n+    atomic_int result{0};\n \n     auto check = _receive[0] | [&](int x) { result = x; };\n-    sut = std::move(_send[0]);\n+    sut = move(_send[0]);\n \n     _receive[0].set_ready();\n     sut(42);\n@@ -90,7 +92,7 @@ BOOST_AUTO_TEST_CASE(int_channel_move_assignment_sender) {\n BOOST_AUTO_TEST_CASE(int_channel_copy_ctor_sender) {\n     BOOST_TEST_MESSAGE(\"int channel copy ctor sender\");\n \n-    std::atomic_int result{0};\n+    atomic_int result{0};\n \n     auto check = _receive[0] | [&](int x) { result = x; };\n     auto sut(_send[0]);\n@@ -114,8 +116,8 @@ BOOST_AUTO_TEST_CASE(int_channel_copy_ctor_sender) {\n BOOST_AUTO_TEST_CASE(int_channel_copy_assignment_sender) {\n     BOOST_TEST_MESSAGE(\"int channel copy assignment sender\");\n \n-    stlab::sender<int> sut;\n-    std::atomic_int result{0};\n+    sender<int> sut;\n+    atomic_int result{0};\n \n     auto check = _receive[0] | [&](int x) { result = x; };\n     sut = _send[0];\n@@ -139,9 +141,9 @@ BOOST_AUTO_TEST_CASE(int_channel_copy_assignment_sender) {\n BOOST_AUTO_TEST_CASE(int_channel_move_ctor_receiver) {\n     BOOST_TEST_MESSAGE(\"int channel move ctor receiver\");\n \n-    std::atomic_int result{0};\n+    atomic_int result{0};\n \n-    auto sut = std::move(_receive[0]);\n+    auto sut = move(_receive[0]);\n \n     auto check = sut | [&](int x) { result = x; };\n     sut.set_ready();\n@@ -155,10 +157,10 @@ BOOST_AUTO_TEST_CASE(int_channel_move_ctor_receiver) {\n BOOST_AUTO_TEST_CASE(int_channel_move_assignment_receiver) {\n     BOOST_TEST_MESSAGE(\"int channel move assignment receiver\");\n \n-    stlab::receiver<int> sut;\n-    std::atomic_int result{0};\n+    receiver<int> sut;\n+    atomic_int result{0};\n \n-    sut = std::move(_receive[0]);\n+    sut = move(_receive[0]);\n     auto check = sut | [&](int x) { result = x; };\n \n     sut.set_ready();\n@@ -172,7 +174,7 @@ BOOST_AUTO_TEST_CASE(int_channel_move_assignment_receiver) {\n BOOST_AUTO_TEST_CASE(int_channel_copy_ctor_receiver) {\n     BOOST_TEST_MESSAGE(\"int channel copy ctor receiver\");\n \n-    std::atomic_int result{0};\n+    atomic_int result{0};\n \n     auto sut(_receive[0]);\n \n@@ -190,8 +192,8 @@ BOOST_AUTO_TEST_CASE(int_channel_copy_ctor_receiver) {\n BOOST_AUTO_TEST_CASE(int_channel_copy_assignment_receiver) {\n     BOOST_TEST_MESSAGE(\"int channel copy assignment receiver\");\n \n-    stlab::receiver<int> sut;\n-    std::atomic_int result{0};\n+    receiver<int> sut;\n+    atomic_int result{0};\n \n     sut = _receive[0];\n \n@@ -208,11 +210,11 @@ BOOST_AUTO_TEST_CASE(int_channel_copy_assignment_receiver) {\n }\n \n BOOST_AUTO_TEST_CASE(int_concatenate_two_channels) {\n-    std::atomic_int result{0};\n-    stlab::sender<int> A, X;\n-    stlab::receiver<int> B, Y;\n-    std::tie(A, B) = stlab::channel<int>(stlab::default_executor);\n-    std::tie(X, Y) = stlab::channel<int>(stlab::default_executor);\n+    atomic_int result{0};\n+    sender<int> A, X;\n+    receiver<int> B, Y;\n+    tie(A, B) = channel<int>(default_executor);\n+    tie(X, Y) = channel<int>(default_executor);\n \n     auto b = B | [](int x) { return x * 2; } | X;\n \n@@ -227,24 +229,304 @@ BOOST_AUTO_TEST_CASE(int_concatenate_two_channels) {\n \n     BOOST_REQUIRE_EQUAL(42, result);\n }\n+\n+BOOST_AUTO_TEST_CASE(int_concatenate_channels_with_different_executor) {\n+    {\n+        atomic_int result{ 0 };\n+\n+        auto done = _receive[0] | (executor{ immediate_executor } & [](int x) { return x + 1; }) | [&result](int x) { result = x; };\n+\n+        _receive[0].set_ready();\n+\n+        _send[0](42);\n+\n+        wait_until_done([&] { return result == 43; });\n+\n+        BOOST_REQUIRE_EQUAL(43, result);\n+    }\n+    test_reset();\n+    {\n+        atomic_int result{ 0 };\n+\n+        auto done = _receive[0] | ([](int x) { return x + 1; } & executor{ immediate_executor }) | [&result](int x) { result = x; };\n+\n+        _receive[0].set_ready();\n+\n+        _send[0](42);\n+\n+        wait_until_done([&] { return result == 43; });\n+\n+        BOOST_REQUIRE_EQUAL(43, result);\n+    }\n+}\n+\n BOOST_AUTO_TEST_SUITE_END()\n \n+namespace {\n+\n+class main_queue {\n+    deque<task<void()>> _q;\n+\n+public:\n+    auto executor() {\n+        return [this](auto&& task) { _q.emplace_back(forward<decltype(task)>(task)); };\n+    }\n+\n+    void run() {\n+        while (execute_next_task()) {\n+        }\n+    }\n+\n+    bool execute_next_task() {\n+        if (!_q.empty()) {\n+            _q.front()();\n+            _q.pop_front();\n+            return true;\n+        }\n+        return false;\n+    }\n+};\n+\n+struct echo {\n+    process_state_scheduled _state = await_forever;\n+    int _result = -1;\n+\n+    void await(int x) {\n+        _result = x;\n+        _state = yield_immediate;\n+    }\n+\n+    int yield() {\n+        _state = await_forever;\n+        return _result;\n+    }\n+\n+    void close() {}\n+\n+    const auto& state() const { return _state; }\n+};\n+\n+template <typename... T>\n+struct generator {\n+    const process_state_scheduled _state = yield_immediate;\n+\n+    int _value = 0;\n+    tuple<T&...> _queues;\n+\n+    explicit generator(T&... q) : _queues(q...) {}\n+\n+    template <size_t... I>\n+    void push_values(int value, index_sequence<I...>) {\n+        (void)initializer_list<int>{(get<I>(_queues).push(value), 0)...};\n+    }\n+\n+    int yield() {\n+        push_values(_value, make_index_sequence<sizeof...(T)>());\n+        return _value++;\n+    }\n+\n+    void close() {}\n+\n+    const auto& state() const { return _state; }\n+};\n+\n+void RequireInClosedRange(size_t minValue, size_t test, size_t maxValue) {\n+    BOOST_REQUIRE_LE(minValue, test);\n+    BOOST_REQUIRE_LE(test, maxValue);\n+}\n+\n+} // namespace\n+\n+BOOST_AUTO_TEST_CASE(int_channel_with_2_sized_buffer) {\n+    main_queue q;\n+\n+    auto receive = channel<void>(q.executor());\n+    queue<int> valuesInFlight;\n+\n+    generator myGenerator(valuesInFlight);\n+    echo myEcho;\n+\n+    auto r2 = move(receive) | ref(myGenerator) |\n+              (buffer_size{2} & ref(myEcho)) | [&valuesInFlight](auto x) {\n+                  BOOST_REQUIRE_EQUAL(x, valuesInFlight.front());\n+                  valuesInFlight.pop();\n+                  cout << x << endl;\n+              };\n+\n+    r2.set_ready();\n+\n+    // The first buffer has size 2 and the next process has per default size 1,\n+    // so there can be max 2 + 1 values in flight\n+    q.execute_next_task(); // generate value(0)\n+    BOOST_REQUIRE_GE(3u, valuesInFlight.size());\n+    q.execute_next_task(); // await and yield value(0) by echo\n+    BOOST_REQUIRE_GE(3u, valuesInFlight.size());\n+\n+    q.execute_next_task(); // generate value(1)\n+    BOOST_REQUIRE_GE(3u, valuesInFlight.size());\n+    q.execute_next_task(); // generate value(2)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+\n+    q.execute_next_task(); // print value (0)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // generate value(3)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // await and yield value(1) by echo\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // print value (1)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // await and yield value(2) by echo\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // generate value(4)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // print value (2)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // await and yield value(3) by echo\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // generate value(5)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+    q.execute_next_task(); // print value (3)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 3u);\n+}\n+\n+BOOST_AUTO_TEST_CASE(int_channel_with_3_sized_buffer) {\n+    main_queue q;\n+\n+    auto receive = channel<void>(q.executor());\n+    queue<int> valuesInFlight;\n+\n+    generator myGenerator(valuesInFlight);\n+    echo myEcho;\n+\n+    auto r2 = move(receive) | ref(myGenerator) |\n+              (buffer_size{3} & ref(myEcho)) | [&valuesInFlight](auto x) {\n+                  BOOST_REQUIRE_EQUAL(x, valuesInFlight.front());\n+                  valuesInFlight.pop();\n+              };\n+\n+    r2.set_ready();\n+\n+    // The first buffer has size 3 and the next process has per default size 1,\n+    // so there can be max 3 + 1 values in flight\n+\n+    q.execute_next_task(); // generate value(0)\n+    BOOST_REQUIRE_GE(3u, valuesInFlight.size());\n+    q.execute_next_task(); // await and yield value(0) by echo\n+    BOOST_REQUIRE_GE(3u, valuesInFlight.size());\n+    q.execute_next_task(); // generate value(1)\n+    BOOST_REQUIRE_GE(3u, valuesInFlight.size());\n+    q.execute_next_task(); // generate value(2)\n+\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // print value (0)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // generate value(3)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // generate value(4)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // await and yield value(1) by echo\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // print value (1)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // await and yield value(2) by echo\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // generate value(5)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // print value (2)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // await and yield value(3) by echo\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // generate value(6)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+    q.execute_next_task(); // print value (3)\n+    RequireInClosedRange(2u, valuesInFlight.size(), 4u);\n+}\n+\n+BOOST_AUTO_TEST_CASE(int_channel_with_split_different_sized_buffer) {\n+    // Here the bigger buffer size must not steer the upstream, but the\n+    // smaller size\n+    vector<pair<size_t, size_t>> bufferSizes = {\n+        {1, 2}, {1, 2}, {1, 3}, {3, 1}, {2, 1}};\n+\n+    for (const auto& bs : bufferSizes) {\n+        main_queue q;\n+\n+        queue<int> valuesInFlight1;\n+        queue<int> valuesInFlight2;\n+\n+        generator myGenerator1(valuesInFlight1);\n+        generator myGenerator2(valuesInFlight2);\n+\n+        auto receive = channel<void>(q.executor());\n+\n+        auto g = move(receive) | generator(valuesInFlight1, valuesInFlight2);\n+\n+        auto r1 = g | (buffer_size{ bs.first } &echo()) | [&valuesInFlight1](auto x) {\n+            BOOST_REQUIRE_EQUAL(x, valuesInFlight1.front());\n+            valuesInFlight1.pop();\n+        };\n+        auto r2 = g | (buffer_size{ bs.second } &echo()) | [&valuesInFlight2](auto x) {\n+            BOOST_REQUIRE_EQUAL(x, valuesInFlight2.front());\n+            valuesInFlight2.pop();\n+        };\n+\n+        g.set_ready();\n+        r1.set_ready();\n+        r2.set_ready();\n+\n+        // In all cases one buffer has just the size of 1 so the overall number of values\n+        // in flight can only be 1 + 1\n+\n+        q.execute_next_task(); // generate value(0)\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // await and yield value(0) by echo1\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // await and yield value(0) by echo2\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // print1 value (0)\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // generate value(1)\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // print2 value (0)\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // await and yield value(1) by echo1\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // await and yield value(1) by echo2\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // print1 value (1)\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+        q.execute_next_task(); // generate value(1)\n+        q.execute_next_task(); // print2 value (1)\n+        RequireInClosedRange(0, valuesInFlight1.size(), 2u);\n+        RequireInClosedRange(0, valuesInFlight2.size(), 2u);\n+    }\n+}\n+\n BOOST_AUTO_TEST_CASE(int_channel_one_value_different_buffer_sizes) {\n     BOOST_TEST_MESSAGE(\"int channel one value different buffer sizes\");\n \n-    for (auto bs : {0, 1, 2, 10}) {\n-        stlab::sender<int> send;\n-        stlab::receiver<int> receive;\n-        std::tie(send, receive) = stlab::channel<int>(stlab::default_executor);\n-        std::atomic_int result{0};\n+    for (auto bs : {0ull, 1ull, 2ull, 10ull}) {\n+        sender<int> send;\n+        receiver<int> receive;\n+        tie(send, receive) = channel<int>(default_executor);\n+        atomic_int result{0};\n \n-        auto check = receive | (stlab::buffer_size(bs) & [&](int x) { result += x; });\n+        auto check = receive | (buffer_size{ bs } &[&](int x) { result += x; });\n \n         receive.set_ready();\n         send(1);\n \n         while (result < 1) {\n-            std::this_thread::sleep_for(std::chrono::microseconds(1));\n+            this_thread::sleep_for(chrono::microseconds(1));\n         }\n \n         BOOST_REQUIRE_EQUAL(1, result);\n@@ -254,20 +536,20 @@ BOOST_AUTO_TEST_CASE(int_channel_one_value_different_buffer_sizes) {\n BOOST_AUTO_TEST_CASE(int_channel_two_values_different_buffer_sizes) {\n     BOOST_TEST_MESSAGE(\"int channel two values different buffer sizes\");\n \n-    for (auto bs : {0, 1, 2, 10}) {\n-        stlab::sender<int> send;\n-        stlab::receiver<int> receive;\n-        std::tie(send, receive) = stlab::channel<int>(stlab::default_executor);\n-        std::atomic_int result{0};\n+    for (auto bs : {0ull, 1ull, 2ull, 10ull}) {\n+        sender<int> send;\n+        receiver<int> receive;\n+        tie(send, receive) = channel<int>(default_executor);\n+        atomic_int result{0};\n \n-        auto check = receive | (stlab::buffer_size(bs) & [&](int x) { result += x; });\n+        auto check = receive | (buffer_size{ bs } &[&](int x) { result += x; });\n \n         receive.set_ready();\n         send(1);\n         send(1);\n \n         while (result < 2) {\n-            std::this_thread::sleep_for(std::chrono::microseconds(1));\n+            this_thread::sleep_for(chrono::microseconds(1));\n         }\n \n         BOOST_REQUIRE_EQUAL(2, result);\n@@ -277,57 +559,80 @@ BOOST_AUTO_TEST_CASE(int_channel_two_values_different_buffer_sizes) {\n BOOST_AUTO_TEST_CASE(int_channel_many_values_different_buffer_sizes) {\n     BOOST_TEST_MESSAGE(\"int channel many values different buffer sizes\");\n \n-    for (auto bs : {0, 1, 2, 10}) {\n-        stlab::sender<int> send;\n-        stlab::receiver<int> receive;\n-        std::tie(send, receive) = stlab::channel<int>(stlab::default_executor);\n-        std::atomic_int result{0};\n+    {\n+        for (auto bs : { 0ull, 1ull, 2ull, 10ull }) {\n+            sender<int> send;\n+            receiver<int> receive;\n+            tie(send, receive) = channel<int>(default_executor);\n+            atomic_int result{ 0 };\n \n-        auto check = receive | (stlab::buffer_size(bs) & [&](int x) { result += x; });\n+            auto check = receive | (buffer_size{ bs } &[&](int x) { result += x; });\n \n-        receive.set_ready();\n-        for (auto i = 0; i < 10; ++i)\n-            send(1);\n+            receive.set_ready();\n+            for (auto i = 0; i < 10; ++i)\n+                send(1);\n+\n+            while (result < 10) {\n+                this_thread::sleep_for(chrono::microseconds(1));\n+            }\n+\n+            BOOST_REQUIRE_EQUAL(10, result);\n+        }\n+    }\n+    {\n+        for (auto bs : { 0ull, 1ull, 2ull, 10ull }) {\n+            sender<int> send;\n+            receiver<int> receive;\n+            tie(send, receive) = channel<int>(default_executor);\n+            atomic_int result{ 0 };\n+\n+            auto check = receive | ([&](int x) { result += x; } &buffer_size{ bs });\n+\n+            receive.set_ready();\n+            for (auto i = 0; i < 10; ++i)\n+                send(1);\n+\n+            while (result < 10) {\n+                this_thread::sleep_for(chrono::microseconds(1));\n+            }\n \n-        while (result < 10) {\n-            std::this_thread::sleep_for(std::chrono::microseconds(1));\n+            BOOST_REQUIRE_EQUAL(10, result);\n         }\n \n-        BOOST_REQUIRE_EQUAL(10, result);\n     }\n }\n \n BOOST_AUTO_TEST_CASE(report_channel_broken_when_no_process_is_attached) {\n     BOOST_TEST_MESSAGE(\"Expect broken channel exception when no process is attached\");\n \n-    stlab::receiver<int> receive;\n+    receiver<int> receive;\n \n     BOOST_REQUIRE_EXCEPTION(\n-        (receive | [](int) { return 1; }), stlab::channel_error,\n-        ([](const auto& e) { return std::string(\"broken channel\") == e.what(); }));\n+        (receive | [](int) { return 1; }), channel_error,\n+        ([](const auto& e) { return string(\"broken channel\") == e.what(); }));\n \n     BOOST_REQUIRE_EXCEPTION(\n-        (receive | (stlab::buffer_size{2} & [](int) { return 1; })), stlab::channel_error,\n-        ([](const auto& e) { return std::string(\"broken channel\") == e.what(); }));\n+        (receive | (buffer_size{2} & [](int) { return 1; })), channel_error,\n+        ([](const auto& e) { return string(\"broken channel\") == e.what(); }));\n }\n \n BOOST_AUTO_TEST_CASE(report_channel_broken_when_process_is_already_running) {\n     BOOST_TEST_MESSAGE(\n         \"Expect \\\"process already running\\\" exception when process is already running\");\n \n-    stlab::sender<int> send;\n-    stlab::receiver<int> receive;\n-    std::tie(send, receive) = stlab::channel<int>(stlab::default_executor);\n+    sender<int> send;\n+    receiver<int> receive;\n+    tie(send, receive) = channel<int>(default_executor);\n \n     receive.set_ready();\n \n     BOOST_REQUIRE_EXCEPTION(\n-        (receive | [](int) { return 1; }), stlab::channel_error,\n-        ([](const auto& e) { return std::string(\"process already running\") == e.what(); }));\n+        (receive | [](int) { return 1; }), channel_error,\n+        ([](const auto& e) { return string(\"process already running\") == e.what(); }));\n \n     BOOST_REQUIRE_EXCEPTION(\n-        (receive | (stlab::buffer_size{2} & [](int) { return 1; })), stlab::channel_error,\n-        ([](const auto& e) { return std::string(\"process already running\") == e.what(); }));\n+        (receive | (buffer_size{2} & [](int) { return 1; })), channel_error,\n+        ([](const auto& e) { return string(\"process already running\") == e.what(); }));\n }\n \n BOOST_AUTO_TEST_CASE(sender_receiver_equality_tests) {\n@@ -357,7 +662,7 @@ BOOST_AUTO_TEST_CASE(sender_receiver_equality_tests) {\n     {\n         sender<int> send;\n         receiver<int> rec;\n-        std::tie(send, rec) = channel<int>(immediate_executor);\n+        tie(send, rec) = channel<int>(immediate_executor);\n \n         auto a = send;\n         auto x = rec;\n@@ -368,16 +673,16 @@ BOOST_AUTO_TEST_CASE(sender_receiver_equality_tests) {\n     {\n         sender<int> a, b;\n         receiver<int> x, y;\n-        std::tie(a, x) = channel<int>(immediate_executor);\n-        std::tie(b, y) = channel<int>(immediate_executor);\n+        tie(a, x) = channel<int>(immediate_executor);\n+        tie(b, y) = channel<int>(immediate_executor);\n \n         BOOST_REQUIRE(a != b);\n         BOOST_REQUIRE(x != y);\n     }\n     {\n         sender<move_only> send;\n         receiver<move_only> rec;\n-        std::tie(send, rec) = channel<move_only>(immediate_executor);\n+        tie(send, rec) = channel<move_only>(immediate_executor);\n \n         sender<move_only> a;\n         BOOST_REQUIRE(a != send);\n@@ -388,8 +693,8 @@ BOOST_AUTO_TEST_CASE(sender_receiver_swap_tests) {\n     {\n         sender<int> a, b;\n         receiver<int> x, y;\n-        std::tie(a, x) = channel<int>(immediate_executor);\n-        std::tie(b, y) = channel<int>(immediate_executor);\n+        tie(a, x) = channel<int>(immediate_executor);\n+        tie(b, y) = channel<int>(immediate_executor);\n         int result1(0), result2(0);\n \n         auto v = x | [&result1](int i) { result1 = i; };\n@@ -409,8 +714,8 @@ BOOST_AUTO_TEST_CASE(sender_receiver_swap_tests) {\n     {\n         sender<int> a, b;\n         receiver<int> x, y;\n-        std::tie(a, x) = channel<int>(immediate_executor);\n-        std::tie(b, y) = channel<int>(immediate_executor);\n+        tie(a, x) = channel<int>(immediate_executor);\n+        tie(b, y) = channel<int>(immediate_executor);\n         int result1(0), result2(0);\n \n         swap(x, y);\n@@ -430,8 +735,8 @@ BOOST_AUTO_TEST_CASE(sender_receiver_swap_tests) {\n     {\n         sender<move_only> a, b;\n         receiver<move_only> x, y;\n-        std::tie(a, x) = channel<move_only>(immediate_executor);\n-        std::tie(b, y) = channel<move_only>(immediate_executor);\n+        tie(a, x) = channel<move_only>(immediate_executor);\n+        tie(b, y) = channel<move_only>(immediate_executor);\n         int result1(0), result2(0);\n \n         auto v = x | [&result1](move_only i) { result1 = i.member(); };\n@@ -451,8 +756,8 @@ BOOST_AUTO_TEST_CASE(sender_receiver_swap_tests) {\n     {\n         sender<move_only> a, b;\n         receiver<move_only> x, y;\n-        std::tie(a, x) = channel<move_only>(immediate_executor);\n-        std::tie(b, y) = channel<move_only>(immediate_executor);\n+        tie(a, x) = channel<move_only>(immediate_executor);\n+        tie(b, y) = channel<move_only>(immediate_executor);\n         int result1(0), result2(0);\n \n         swap(x, y);"},{"sha":"5c614cf0df9ec7934db821fa3342e7e7f3eb82df","filename":"test/future_recover_tests.cpp","status":"modified","additions":871,"deletions":327,"changes":1198,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_recover_tests.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_recover_tests.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_recover_tests.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -16,219 +16,443 @@\n \n #include \"future_test_helper.hpp\"\n \n+using namespace std;\n using namespace stlab;\n using namespace future_test_helper;\n \n BOOST_FIXTURE_TEST_SUITE(future_recover_void, test_fixture<void>)\n BOOST_AUTO_TEST_CASE(future_recover_failure_before_recover_initialized_on_rvalue) {\n     BOOST_TEST_MESSAGE(\"running future recover, failure before recover initialized on r-value\");\n+    \n+    /* \n+    combining the tests as in future_then_tests is not possible because of a bug in gcc\n+    \n+    using task_t = function<void(future<void>)>;\n+    using op_t = future<void>(future<void>::*)(task_t&&)&&;\n+\n+    op_t ops[] = {static_cast<op_t>(&future<void>::recover<task_t>),\n+                  static_cast<op_t>(&future<void>::operator^<task_t>)};\n+\n+    for (const auto& op : ops)\n+    */\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        sut = async(make_executor<0>(),\n+                    [& _error = error]() ->void {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    })\n+            .recover([](future<void> failedFuture) {\n+                if (failedFuture.error()) check_failure<test_exception>(failedFuture, \"failure\");\n+            });\n+        wait_until_future_completed(sut);\n \n-    auto error = false;\n-\n-    sut = async(custom_scheduler<0>(),\n-                [& _error = error] {\n-                    _error = true;\n-                    throw test_exception(\"failure\");\n-                })\n-              .recover([](auto failedFuture) {\n-                  if (failedFuture.error()) check_failure<test_exception>(failedFuture, \"failure\");\n-              });\n-    wait_until_future_completed(sut);\n-\n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_GE(2, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_GE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        sut = (async(make_executor<0>(),\n+                     [& _error = error]() ->void {\n+                         _error = true;\n+                         throw test_exception(\"failure\");\n+                     })\n+               ^ [](future<void> failedFuture) {\n+                    if (failedFuture.error()) check_failure<test_exception>(failedFuture, \"failure\");\n+                });\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_GE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_recover_failure_before_recover_initialized_on_lvalue) {\n     BOOST_TEST_MESSAGE(\"running future recover, failure before recover initialized on l-value\");\n \n-    auto error = false;\n-    auto interim = async(custom_scheduler<0>(), [& _error = error] {\n-        _error = true;\n-        throw test_exception(\"failure\");\n-    });\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error] {\n+            _error = true;\n+            throw test_exception(\"failure\");\n+        });\n \n-    wait_until_future_fails<test_exception>(interim);\n+        wait_until_future_fails<test_exception>(interim);\n \n-    sut = interim.recover(\n-        [](auto failedFuture) { check_failure<test_exception>(failedFuture, \"failure\"); });\n+        sut = interim.recover(\n+            [](auto failedFuture) { check_failure<test_exception>(failedFuture, \"failure\"); });\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(2, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error] {\n+            _error = true;\n+            throw test_exception(\"failure\");\n+        });\n+\n+        wait_until_future_fails<test_exception>(interim);\n+\n+        sut = interim ^\n+            [](auto failedFuture) { check_failure<test_exception>(failedFuture, \"failure\"); };\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_failure_before_recover_initialized_with_custom_scheduler_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future recover, failure before recover initialized, with custom scheduler on r-value\");\n \n-    auto error = false;\n+    {\n+        auto error = false;\n \n-    sut = async(custom_scheduler<0>(),\n-                [& _error = error] {\n-                    _error = true;\n-                    throw test_exception(\"failure\");\n-                })\n-              .recover(custom_scheduler<1>(), [](auto failedFuture) {\n-                  check_failure<test_exception>(failedFuture, \"failure\");\n-              });\n+        sut = async(make_executor<0>(),\n+                    [& _error = error] {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    })\n+            .recover(make_executor<1>(), [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+            });\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n \n-    wait_until_future_completed(sut);\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n \n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    {\n+        auto error = false;\n+\n+        sut = async(make_executor<0>(),\n+                    [& _error = error] {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    }) ^\n+              (executor{make_executor<1>()} &\n+               [](auto failedFuture) { check_failure<test_exception>(failedFuture, \"failure\"); });\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_failure_before_recover_initialized_with_custom_scheduler_on_lvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future recover, failure before recover initialized, with custom scheduler on l-value\");\n \n-    auto error = false;\n-    auto interim = async(custom_scheduler<0>(), [& _error = error] {\n-        _error = true;\n-        throw test_exception(\"failure\");\n-    });\n-\n-    wait_until_future_fails<test_exception>(interim);\n+    {\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error] {\n+            _error = true;\n+            throw test_exception(\"failure\");\n+        });\n \n-    sut = interim.recover(custom_scheduler<1>(), [](auto failedFuture) {\n-        check_failure<test_exception>(failedFuture, \"failure\");\n-    });\n+        wait_until_future_fails<test_exception>(interim);\n \n-    wait_until_future_completed(sut);\n+        sut = interim.recover(make_executor<1>(), [](auto failedFuture) {\n+            check_failure<test_exception>(failedFuture, \"failure\");\n+        });\n \n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n-}\n+        wait_until_future_completed(sut);\n \n-BOOST_AUTO_TEST_CASE(future_recover_failure_after_recover_initialized_on_rvalue) {\n-    BOOST_TEST_MESSAGE(\"running future recover, failure after recover initialized on r-value\");\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n \n-    auto error = false;\n-    std::mutex block;\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n \n     {\n-        lock_t hold(block);\n-        sut = async(custom_scheduler<0>(),\n-                    [& _error = error, &_block = block] {\n-                        lock_t lock(_block);\n-                        _error = true;\n-                        throw test_exception(\"failure\");\n-                    })\n-                  .recover([](auto failedFuture) {\n-                      check_failure<test_exception>(failedFuture, \"failure\");\n-                  });\n-    }\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error] {\n+            _error = true;\n+            throw test_exception(\"failure\");\n+        });\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_fails<test_exception>(interim);\n \n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_GE(2, custom_scheduler<0>::usage_counter());\n-}\n+        sut = interim ^ ( executor{make_executor<1>()} & [](auto failedFuture) {\n+                  check_failure<test_exception>(failedFuture, \"failure\");\n+              });\n \n-BOOST_AUTO_TEST_CASE(future_recover_failure_after_recover_initialized_on_lvalue) {\n-    BOOST_TEST_MESSAGE(\"running future recover, failure after recover initialized on l-value\");\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n+}\n \n-    auto error = false;\n-    std::mutex block;\n+BOOST_AUTO_TEST_CASE(future_recover_failure_after_recover_initialized_on_rvalue) {\n+    BOOST_TEST_MESSAGE(\"running future recover, failure after recover initialized on r-value\");\n \n     {\n-        lock_t hold(block);\n-        auto interim = async(custom_scheduler<0>(), [& _error = error, &_block = block] {\n-            lock_t lock(_block);\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block] {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        })\n+                .recover([](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_GE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error] {\n             _error = true;\n             throw test_exception(\"failure\");\n         });\n \n-        sut = interim.recover(\n-            [](auto failedFuture) { check_failure<test_exception>(failedFuture, \"failure\"); });\n+        wait_until_future_fails<test_exception>(interim);\n+\n+        sut = interim ^\n+            [](auto failedFuture) { check_failure<test_exception>(failedFuture, \"failure\"); };\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(2, custom_scheduler<0>::usage_counter());\n     }\n+}\n \n-    wait_until_future_completed(sut);\n+BOOST_AUTO_TEST_CASE(future_recover_failure_after_recover_initialized_on_lvalue) {\n+    BOOST_TEST_MESSAGE(\"running future recover, failure after recover initialized on l-value\");\n \n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_GE(2, custom_scheduler<0>::usage_counter());\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            auto interim = async(make_executor<0>(), [& _error = error, &_block = block] {\n+                lock_t lock(_block);\n+                _error = true;\n+                throw test_exception(\"failure\");\n+            });\n+\n+            sut = interim.recover(\n+                [](auto failedFuture) { check_failure<test_exception>(failedFuture, \"failure\"); });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_GE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            auto interim = async(make_executor<0>(), [& _error = error, &_block = block] {\n+                lock_t lock(_block);\n+                _error = true;\n+                throw test_exception(\"failure\");\n+            });\n+\n+            sut = interim ^\n+                [](auto failedFuture) { check_failure<test_exception>(failedFuture, \"failure\"); };\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_GE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_failure_after_recover_initialized_with_custom_scheduler_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future recover, failure after recover initialized with custom scheduler on r-value\");\n \n-    auto error = false;\n-    std::mutex block;\n+    {\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block] {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        })\n+                .recover(make_executor<1>(), [](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n+\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n \n     {\n-        lock_t hold(block);\n-        sut = async(custom_scheduler<0>(),\n-                    [& _error = error, &_block = block] {\n-                        lock_t lock(_block);\n-                        _error = true;\n-                        throw test_exception(\"failure\");\n-                    })\n-                  .recover(custom_scheduler<1>(), [](auto failedFuture) {\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block] {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        }) ^\n+                  (executor{make_executor<1>()} & [](auto failedFuture) {\n                       check_failure<test_exception>(failedFuture, \"failure\");\n                   });\n-    }\n+        }\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_failure_after_recover_initialized_with_custom_scheduler_on_lvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future recover, failure after recover initialized with custom scheduler on l-value\");\n \n-    auto error = false;\n-    std::mutex block;\n-\n     {\n-        lock_t hold(block);\n-        auto interim = async(custom_scheduler<0>(), [& _error = error, &_block = block] {\n-            lock_t lock(_block);\n-            _error = true;\n-            throw test_exception(\"failure\");\n-        });\n-\n-        sut = interim.recover(custom_scheduler<1>(), [](auto failedFuture) {\n-            check_failure<test_exception>(failedFuture, \"failure\");\n-        });\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            auto interim = async(make_executor<0>(), [& _error = error, &_block = block] {\n+                lock_t lock(_block);\n+                _error = true;\n+                throw test_exception(\"failure\");\n+            });\n+\n+            sut = interim.recover(make_executor<1>(), [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+            });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n     }\n \n-    wait_until_future_completed(sut);\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n \n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    {\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            auto interim = async(make_executor<0>(), [& _error = error, &_block = block] {\n+                lock_t lock(_block);\n+                _error = true;\n+                throw test_exception(\"failure\");\n+            });\n+\n+            sut = interim ^ ( executor{make_executor<1>()} & [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+            });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_recover_failure_during_when_all_on_lvalue) {\n     BOOST_TEST_MESSAGE(\"running future recover while failed when_all on l-value\");\n \n-    int result{0};\n-    auto f1 = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n-    auto f2 = async(custom_scheduler<1>(), [] { return 42; });\n-\n-    sut = when_all(custom_scheduler<0>(), [](int x, int y) { return x + y; }, f1, f2)\n-              .recover([](auto error) {\n-                  if (error.error())\n-                      return 815;\n-                  else\n-                      return 0;\n-              })\n-              .then([&](int x) { result = x; });\n-\n-    wait_until_future_completed(sut);\n-    BOOST_REQUIRE_EQUAL(815, result);\n+    {\n+        custom_scheduler<0>::reset();\n+        int result{0};\n+        auto f1 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+        auto f2 = async(make_executor<1>(), [] { return 42; });\n+\n+        sut = when_all(make_executor<0>(), [](int x, int y) { return x + y; }, f1, f2)\n+            .recover([](auto error) {\n+                if (error.error())\n+                    return 815;\n+                else\n+                    return 0;\n+            })\n+            .then([&](int x) { result = x; });\n+\n+        wait_until_future_completed(sut);\n+        BOOST_REQUIRE_EQUAL(815, result);\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        int result{0};\n+        auto f1 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+        auto f2 = async(make_executor<1>(), [] { return 42; });\n+\n+        sut = (when_all(make_executor<0>(), [](int x, int y) { return x + y; }, f1, f2) ^\n+                  [](auto error) {\n+                      if (error.error())\n+                          return 815;\n+                      else\n+                          return 0;\n+                  }) |\n+              [&](int x) { result = x; };\n+\n+        wait_until_future_completed(sut);\n+        BOOST_REQUIRE_EQUAL(815, result);\n+    }\n }\n BOOST_AUTO_TEST_SUITE_END()\n \n@@ -237,317 +461,637 @@ BOOST_AUTO_TEST_CASE(\n     future_recover_int_simple_recover_failure_before_recover_initialized_on_rvalue) {\n     BOOST_TEST_MESSAGE(\"running future int recover, failure before recover initialized on r-value\");\n \n-    auto error = false;\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n \n-    sut = async(custom_scheduler<0>(),\n-                [& _error = error]() -> int {\n-                    _error = true;\n-                    throw test_exception(\"failure\");\n-                })\n-              .recover([](auto failedFuture) {\n-                  check_failure<test_exception>(failedFuture, \"failure\");\n-                  return 42;\n-              });\n+        sut = async(make_executor<0>(),\n+                    [& _error = error]() -> int {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    })\n+                  .recover([](auto failedFuture) {\n+                      check_failure<test_exception>(failedFuture, \"failure\");\n+                      return 42;\n+                  });\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+\n+        sut = async(make_executor<0>(),\n+                    [& _error = error]() -> int {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    })\n+            ^ [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return 42;\n+            };\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n-    BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_int_simple_recover_failure_before_recover_initialized_on_lvalue) {\n     BOOST_TEST_MESSAGE(\"running future int recover, failure before recover initialized on l-value\");\n \n-    auto error = false;\n-    auto interim = async(custom_scheduler<0>(), [& _error = error]() -> int {\n-        _error = true;\n-        throw test_exception(\"failure\");\n-    });\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error]() -> int {\n+            _error = true;\n+            throw test_exception(\"failure\");\n+        });\n+\n+        sut = interim.recover([](auto failedFuture) {\n+            check_failure<test_exception>(failedFuture, \"failure\");\n+            return 42;\n+        });\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error]() -> int {\n+            _error = true;\n+            throw test_exception(\"failure\");\n+        });\n \n-    sut = interim.recover([](auto failedFuture) {\n-        check_failure<test_exception>(failedFuture, \"failure\");\n-        return 42;\n-    });\n+        sut = interim ^ [](auto failedFuture) {\n+            check_failure<test_exception>(failedFuture, \"failure\");\n+            return 42;\n+        };\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n-    BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_int_simple_recover_failure_after_recover_initialized_on_rvalue) {\n     BOOST_TEST_MESSAGE(\"running future int recover, failure after recover initialized on r-value\");\n \n-    auto error = false;\n-    std::mutex block;\n-\n     {\n-        lock_t hold(block);\n-\n-        sut = async(custom_scheduler<0>(),\n-                    [& _error = error, &_block = block]() -> int {\n-                        lock_t lock(_block);\n-                        _error = true;\n-                        throw test_exception(\"failure\");\n-                    })\n-                  .recover([](auto failedFuture) {\n-                      check_failure<test_exception>(failedFuture, \"failure\");\n-                      return 42;\n-                  });\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block]() -> int {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        })\n+                .recover([](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                    return 42;\n+                });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block]() -> int {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        })\n+                ^ [](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                    return 42;\n+                };\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n     }\n-\n-    wait_until_future_completed(sut);\n-\n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n-    BOOST_REQUIRE(error);\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_int_simple_recover_failure_after_recover_initialized_on_lvalue) {\n     BOOST_TEST_MESSAGE(\"running future int recover, failure after recover initialized on l-value\");\n \n-    auto error = false;\n-    std::mutex block;\n-\n     {\n-        lock_t hold(block);\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n \n-        auto interim = async(custom_scheduler<0>(), [& _error = error, &_block = block]() -> int {\n-            lock_t lock(_block);\n-            _error = true;\n-            throw test_exception(\"failure\");\n-        });\n+        {\n+            lock_t hold(block);\n \n-        sut = interim.recover([](auto failedFuture) {\n-            check_failure<test_exception>(failedFuture, \"failure\");\n-            return 42;\n-        });\n+            auto interim = async(make_executor<0>(), [& _error = error, &_block = block]() -> int {\n+                lock_t lock(_block);\n+                _error = true;\n+                throw test_exception(\"failure\");\n+            });\n+\n+            sut = interim.recover([](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return 42;\n+            });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n     }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+\n+            auto interim = async(make_executor<0>(), [& _error = error, &_block = block]() -> int {\n+                lock_t lock(_block);\n+                _error = true;\n+                throw test_exception(\"failure\");\n+            });\n \n-    wait_until_future_completed(sut);\n+            sut = interim ^ [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return 42;\n+            };\n+        }\n \n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n-    BOOST_REQUIRE(error);\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_int_simple_recover_failure_before_recover_initialized_with_custom_scheduler_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future int recover, failure before recover initialized with custom scheduler on r-value\");\n \n-    auto error = false;\n+    {\n+        auto error = false;\n \n-    sut = async(custom_scheduler<0>(),\n-                [& _error = error]() -> int {\n-                    _error = true;\n-                    throw test_exception(\"failure\");\n-                })\n-              .recover(custom_scheduler<1>(), [](auto failedFuture) {\n-                  check_failure<test_exception>(failedFuture, \"failure\");\n-                  return 42;\n-              });\n+        sut = async(make_executor<0>(),\n+                    [& _error = error]() -> int {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    })\n+            .recover(make_executor<1>(), [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return 42;\n+            });\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n+\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n+\n+    {\n+        auto error = false;\n+\n+        sut = async(make_executor<0>(),\n+                    [& _error = error]() -> int {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    }) ^\n+            ( executor{make_executor<1>()} & [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return 42;\n+            });\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_int_simple_recover_failure_before_recover_initialized_with_custom_scheduler_on_lvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future int recover, failure before recover initialized with custom scheduler on l-value\");\n \n-    auto error = false;\n-    auto interim = async(custom_scheduler<0>(), [& _error = error]() -> int {\n-        _error = true;\n-        throw test_exception(\"failure\");\n-    });\n+    {\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error]() -> int {\n+            _error = true;\n+            throw test_exception(\"failure\");\n+        });\n+\n+        wait_until_future_fails<test_exception>(interim);\n+\n+        sut = interim.recover(make_executor<1>(), [](auto failedFuture) {\n+            check_failure<test_exception>(failedFuture, \"failure\");\n+            return 42;\n+        });\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n+\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n+\n+    {\n+        auto error = false;\n+        auto interim = async(make_executor<0>(), [& _error = error]() -> int {\n+            _error = true;\n+            throw test_exception(\"failure\");\n+        });\n \n-    wait_until_future_fails<test_exception>(interim);\n+        wait_until_future_fails<test_exception>(interim);\n \n-    sut = interim.recover(custom_scheduler<1>(), [](auto failedFuture) {\n-        check_failure<test_exception>(failedFuture, \"failure\");\n-        return 42;\n-    });\n+        sut = interim ^ ( executor{make_executor<1>()} & [](auto failedFuture) {\n+            check_failure<test_exception>(failedFuture, \"failure\");\n+            return 42;\n+        });\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_int_simple_recover_failure_after_recover_initialized_with_custom_scheduler_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future int recover, failure after recover initialized with custom scheduler on r-value\");\n \n-    auto error = false;\n-    std::mutex block;\n-\n     {\n-        lock_t hold(block);\n-\n-        sut = async(custom_scheduler<0>(),\n-                    [& _error = error, &_block = block]() -> int {\n-                        lock_t lock(_block);\n-                        _error = true;\n-                        throw test_exception(\"failure\");\n-                    })\n-                  .recover(custom_scheduler<1>(), [](auto failedFuture) {\n-                      check_failure<test_exception>(failedFuture, \"failure\");\n-                      return 42;\n-                  });\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block]() -> int {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        })\n+                .recover(make_executor<1>(), [](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                    return 42;\n+                });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n     }\n \n-    wait_until_future_completed(sut);\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n \n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    {\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block]() -> int {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        }) ^ ( executor{make_executor<1>()} & [](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                    return 42;\n+                });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_int_simple_recover_failure_after_recover_initialized_with_custom_scheduler_on_lvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future int recover, failure after recover initialized with custom scheduler on l-value\");\n \n-    auto error = false;\n-    std::mutex block;\n-\n     {\n-        lock_t hold(block);\n-        auto interim = async(custom_scheduler<0>(), [& _error = error, &_block = block]() -> int {\n-            lock_t lock(_block);\n-            _error = true;\n-            throw test_exception(\"failure\");\n-        });\n-\n-        sut = interim.recover(custom_scheduler<1>(), [](auto failedFuture) {\n-            check_failure<test_exception>(failedFuture, \"failure\");\n-            return 42;\n-        });\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            auto interim = async(make_executor<0>(), [& _error = error, &_block = block]() -> int {\n+                lock_t lock(_block);\n+                _error = true;\n+                throw test_exception(\"failure\");\n+            });\n+\n+            sut = interim.recover(make_executor<1>(), [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return 42;\n+            });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n     }\n \n-    wait_until_future_completed(sut);\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n \n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    {\n+        auto error = false;\n+        mutex block;\n+\n+        {\n+            lock_t hold(block);\n+            auto interim = async(make_executor<0>(), [& _error = error, &_block = block]() -> int {\n+                lock_t lock(_block);\n+                _error = true;\n+                throw test_exception(\"failure\");\n+            });\n+\n+            sut = interim ^ (executor{make_executor<1>()} & [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return 42;\n+            });\n+        }\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_SUITE_END()\n \n-BOOST_FIXTURE_TEST_SUITE(future_recover_move_only_type, test_fixture<stlab::move_only>)\n+BOOST_FIXTURE_TEST_SUITE(future_recover_move_only_type, test_fixture<move_only>)\n BOOST_AUTO_TEST_CASE(\n     future_recover_move_only_type_recover_failure_before_recover_initialized_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future move only type recover, failure before recover initialized on r-value\");\n \n-    auto error = false;\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n \n-    sut = async(custom_scheduler<0>(),\n-                [& _error = error]() -> move_only {\n-                    _error = true;\n-                    throw test_exception(\"failure\");\n-                })\n-              .recover([](auto failedFuture) {\n-                  check_failure<test_exception>(failedFuture, \"failure\");\n-                  return move_only(42);\n-              });\n+        sut = async(make_executor<0>(),\n+                    [& _error = error]() -> move_only {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    })\n+            .recover([](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return move_only(42);\n+            });\n+\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE(error);\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+\n+        sut = async(make_executor<0>(),\n+                     [& _error = error]() -> move_only {\n+                         _error = true;\n+                         throw test_exception(\"failure\");\n+                     })\n+               ^ [](auto failedFuture) {\n+            check_failure<test_exception>(failedFuture, \"failure\");\n+            return move_only(42);\n+        };\n \n-    auto result = wait_until_future_r_completed(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE(error);\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_recover_move_only_types_recover_failure_after_recover_initialized) {\n     BOOST_TEST_MESSAGE(\"running future move only type recover, failure after recover initialized\");\n \n-    auto error = false;\n-    std::mutex block;\n     {\n-        lock_t hold(block);\n-\n-        sut = async(custom_scheduler<0>(),\n-                    [& _error = error, &_block = block]() -> move_only {\n-                        lock_t lock(_block);\n-                        _error = true;\n-                        throw test_exception(\"failure\");\n-                    })\n-                  .recover([](auto failedFuture) {\n-                      check_failure<test_exception>(failedFuture, \"failure\");\n-                      return move_only(42);\n-                  });\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n+        {\n+            lock_t hold(block);\n+\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block]() -> move_only {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        })\n+                .recover([](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                    return move_only(42);\n+                });\n+        }\n+\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE(error);\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        auto error = false;\n+        mutex block;\n+        {\n+            lock_t hold(block);\n+\n+            sut = async(make_executor<0>(),\n+                         [& _error = error, &_block = block]() -> move_only {\n+                             lock_t lock(_block);\n+                             _error = true;\n+                             throw test_exception(\"failure\");\n+                         })\n+                   ^ [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return move_only(42);\n+            };\n+        }\n+\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE(error);\n     }\n-\n-    auto result = wait_until_future_r_completed(sut);\n-\n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE(error);\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_move_only_type_recover_failure_before_recover_initialized_with_custom_scheduler_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future move only type recover, failure before recover initialized with custom scheduler on r-value\");\n \n-    auto error = false;\n+    {\n+        auto error = false;\n \n-    sut = async(custom_scheduler<0>(),\n-                [& _error = error]() -> move_only {\n-                    _error = true;\n-                    throw test_exception(\"failure\");\n-                })\n-              .recover(custom_scheduler<1>(), [](auto failedFuture) {\n-                  check_failure<test_exception>(failedFuture, \"failure\");\n-                  return move_only(42);\n-              });\n+        sut = async(make_executor<0>(),\n+                    [& _error = error]() -> move_only {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    })\n+            .recover(make_executor<1>(), [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return move_only(42);\n+            });\n+\n+        auto result = wait_until_future_r_completed(sut);\n \n-    auto result = wait_until_future_r_completed(sut);\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n+\n+    {\n+        auto error = false;\n+\n+        sut = async(make_executor<0>(),\n+                    [& _error = error]() -> move_only {\n+                        _error = true;\n+                        throw test_exception(\"failure\");\n+                    }) ^\n+            ( executor{make_executor<1>()} & [](auto failedFuture) {\n+                check_failure<test_exception>(failedFuture, \"failure\");\n+                return move_only(42);\n+            });\n+\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_recover_move_only_types_recover_failure_after_recover_initialized_with_custom_scheduler_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future move only type recover, failure after recover initialized with custom scheduler on r-value\");\n \n-    auto error = false;\n-    std::mutex block;\n     {\n-        lock_t hold(block);\n-        sut = async(custom_scheduler<0>(),\n-                    [& _error = error, &_block = block]() -> move_only {\n-                        lock_t lock(_block);\n-                        _error = true;\n-                        throw test_exception(\"failure\");\n-                    })\n-                  .recover(custom_scheduler<1>(), [](auto failedFuture) {\n-                      check_failure<test_exception>(failedFuture, \"failure\");\n-                      return move_only(42);\n-                  });\n+        auto error = false;\n+        mutex block;\n+        {\n+            lock_t hold(block);\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block]() -> move_only {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        })\n+                .recover(make_executor<1>(), [](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                    return move_only(42);\n+                });\n+        }\n+\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n     }\n \n-    auto result = wait_until_future_r_completed(sut);\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n+\n+    {\n+        auto error = false;\n+        mutex block;\n+        {\n+            lock_t hold(block);\n+            sut = async(make_executor<0>(),\n+                        [& _error = error, &_block = block]() -> move_only {\n+                            lock_t lock(_block);\n+                            _error = true;\n+                            throw test_exception(\"failure\");\n+                        }) ^\n+                ( executor{make_executor<1>()} & [](auto failedFuture) {\n+                    check_failure<test_exception>(failedFuture, \"failure\");\n+                    return move_only(42);\n+                });\n+        }\n+\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE(error);\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n+    }\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE(error);\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_GE(1, custom_scheduler<1>::usage_counter());\n }\n \n BOOST_AUTO_TEST_SUITE_END()"},{"sha":"2ae9f2bd20e13937b7937850b26a87a8af607c2d","filename":"test/future_test_helper.cpp","status":"modified","additions":1,"deletions":0,"changes":1,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_test_helper.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_test_helper.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_test_helper.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -17,3 +17,4 @@ test_exception::test_exception(const char* error) : _error(error) {}\n const char* test_exception::what() const noexcept { return _error.c_str(); }\n \n } // namespace future_test_helper\n+"},{"sha":"84d4413fec7df74d7a1c3c770b8d9a7c83fedaad","filename":"test/future_test_helper.hpp","status":"modified","additions":15,"deletions":2,"changes":17,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_test_helper.hpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_test_helper.hpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_test_helper.hpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -21,6 +21,7 @@\n using lock_t = std::unique_lock<std::mutex>;\n \n namespace future_test_helper {\n+\n template <std::size_t no>\n struct custom_scheduler {\n     using result_type = void;\n@@ -40,7 +41,9 @@ struct custom_scheduler {\n \n     static int usage_counter() { return counter().load(); }\n \n-    static void reset() { counter() = 0; }\n+    static void reset() {\n+        counter() = 0;\n+    }\n \n     static std::atomic_int& counter() {\n         static std::atomic_int counter;\n@@ -51,8 +54,18 @@ struct custom_scheduler {\n     size_t _id = no; // only used for debugging purpose\n };\n \n+\n+\n+template <std::size_t I>\n+stlab::executor_t make_executor() {\n+    return [_executor = custom_scheduler<I>{}](stlab::task<void()> f) mutable {\n+        _executor(std::move(f));\n+    };\n+}\n+\n+\n class test_exception : public std::exception {\n-    const std::string _error;\n+    std::string _error;\n \n public:\n     test_exception() {}"},{"sha":"5d1b512d3ada66d4c2a4970e1ce326ebe086bc59","filename":"test/future_tests.cpp","status":"modified","additions":3,"deletions":3,"changes":6,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_tests.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_tests.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_tests.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(future_constructed_minimal_fn, T, copyable_test_ty\n \n     test_setup setup;\n     {\n-        auto sut = async(custom_scheduler<0>(), []() -> T { return T(0); });\n+        auto sut = async(make_executor<0>(), []() -> T { return T(0); });\n         BOOST_REQUIRE(sut.valid() == true);\n         BOOST_REQUIRE(!sut.error());\n \n@@ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(future_constructed_minimal_fn_with_parameters,\n \n     test_setup setup;\n     {\n-        auto sut = async(custom_scheduler<0>(), [](auto x) -> T { return x + T(0); }, T(42));\n+        auto sut = async(make_executor<0>(), [](auto x) -> T { return x + T(0); }, T(42));\n         BOOST_REQUIRE(sut.valid() == true);\n         BOOST_REQUIRE(!sut.error());\n \n@@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(future_constructed_minimal_fn_moveonly) {\n     test_setup setup;\n     {\n         auto sut =\n-            async(custom_scheduler<0>(), []() -> v1::move_only { return v1::move_only{42}; });\n+            async(make_executor<0>(), []() -> v1::move_only { return v1::move_only{42}; });\n         BOOST_REQUIRE(sut.valid() == true);\n         BOOST_REQUIRE(!sut.error());\n "},{"sha":"79291416b20cbe6998d53e5448e326738b0a0b22","filename":"test/future_then_tests.cpp","status":"modified","additions":981,"deletions":330,"changes":1311,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_then_tests.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_then_tests.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_then_tests.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -6,6 +6,7 @@\n \n /**************************************************************************************************/\n \n+#include <boost/mpl/list.hpp>\n #include <boost/test/unit_test.hpp>\n \n #include <stlab/concurrency/default_executor.hpp>\n@@ -15,6 +16,7 @@\n \n #include \"future_test_helper.hpp\"\n \n+using namespace std;\n using namespace stlab;\n using namespace future_test_helper;\n \n@@ -25,7 +27,7 @@ BOOST_AUTO_TEST_CASE(future_void_single_task) {\n \n     int p = 0;\n \n-    sut = async(custom_scheduler<0>(), [& _p = p] { _p = 42; });\n+    sut = async(make_executor<0>(), [& _p = p] { _p = 42; });\n \n     check_valid_future(sut);\n     wait_until_future_completed(sut);\n@@ -37,9 +39,9 @@ BOOST_AUTO_TEST_CASE(future_void_single_task) {\n BOOST_AUTO_TEST_CASE(future_void_single_task_detached) {\n     BOOST_TEST_MESSAGE(\"running future void single task detached\");\n \n-    std::atomic_int p{0};\n+    atomic_int p{0};\n     {\n-        auto detached = async(custom_scheduler<0>(), [& _p = p] { _p = 42; });\n+        auto detached = async(make_executor<0>(), [& _p = p] { _p = 42; });\n         detached.detach();\n     }\n     while (p.load() != 42) {\n@@ -49,76 +51,164 @@ BOOST_AUTO_TEST_CASE(future_void_single_task_detached) {\n BOOST_AUTO_TEST_CASE(future_void_two_tasks_with_same_scheduler_then_on_rvalue) {\n     BOOST_TEST_MESSAGE(\"running future void with two task on same scheduler, then on r-value\");\n \n-    std::atomic_int p{0};\n+    /* because of a gcc version < 6 bug, it is not possible to use the following\n+    using task_t = function<void()>;\n+    using op_t = future<void>(future<void>::*)(task_t&&)&&;\n \n-    sut = async(custom_scheduler<0>(), [& _p = p] { _p = 42; }).then([& _p = p] { _p += 42; });\n+    op_t ops[] = {static_cast<op_t>(&future<void>::then<task_t>),\n+                  static_cast<op_t>(&future<void>::operator|<task_t>)};\n \n-    check_valid_future(sut);\n-    wait_until_future_completed(sut);\n+    for (const auto& op : ops)\n+    */\n+    {\n+        atomic_int p{0};\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, p);\n-    BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then([& _p = p] { _p += 42; });\n+\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        atomic_int p{0};\n+\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) | [& _p = p] { _p += 42; };\n+\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_void_two_tasks_with_same_scheduler_then_on_lvalue) {\n     BOOST_TEST_MESSAGE(\"running future void with two task on same scheduler, then on l-value\");\n \n-    std::atomic_int p{0};\n-    auto interim = async(custom_scheduler<0>(), [& _p = p] { _p = 42; });\n+    {\n+        atomic_int p{0};\n+        auto interim = async(make_executor<0>(), [& _p = p] { _p = 42; });\n \n-    sut = interim.then([& _p = p] { _p += 42; });\n+        sut = interim.then([& _p = p] { _p += 42; });\n \n-    check_valid_future(sut);\n-    wait_until_future_completed(sut);\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, p);\n-    BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        atomic_int p{0};\n+        auto interim = async(make_executor<0>(), [& _p = p] { _p = 42; });\n+\n+        sut = interim | [& _p = p] { _p += 42; };\n+\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_int_void_two_tasks_with_same_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future int void tasks with same scheduler\");\n \n-    std::atomic_int p{0};\n+    {\n+        atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; }).then([& _p = p](auto x) { _p = x + 42; });\n-    check_valid_future(sut);\n+        sut =\n+            async(make_executor<0>(), [] { return 42; }).then([& _p = p](auto x) { _p = x + 42; });\n+        check_valid_future(sut);\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, p);\n-    BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        atomic_int p{0};\n+\n+        sut = async(make_executor<0>(), [] { return 42; }) | [& _p = p](auto x) { _p = x + 42; };\n+        check_valid_future(sut);\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_int_void_two_tasks_with_different_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future int void tasks with different schedulers\");\n \n-    std::atomic_int p{0};\n+    {\n+        atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; })\n-              .then(custom_scheduler<1>(), [& _p = p](auto x) { _p = x + 42; });\n-    check_valid_future(sut);\n+        sut = async(make_executor<0>(), [] {\n+                  return 42;\n+              }).then(make_executor<1>(), [& _p = p](auto x) { _p = x + 42; });\n+        check_valid_future(sut);\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, p);\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+    }\n+    {\n+        atomic_int p{0};\n+\n+        sut = async(make_executor<0>(), [] { return 42; }) |\n+              (executor{make_executor<1>()} & [& _p = p](auto x) { _p = x + 42; });\n+\n+        check_valid_future(sut);\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_void_two_tasks_with_different_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future void two tasks with different schedulers\");\n \n-    std::atomic_int p{0};\n+    {\n+        atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), [& _p = p] { _p = 42; })\n-              .then(custom_scheduler<1>(), [& _p = p] { _p += 42; });\n-    check_valid_future(sut);\n+        sut = async(make_executor<0>(), [& _p = p] {\n+                  _p = 42;\n+              }).then(make_executor<1>(), [& _p = p] { _p += 42; });\n+        check_valid_future(sut);\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, p);\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+    }\n+\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n+\n+    {\n+        atomic_int p{0};\n+\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) |\n+              (executor{make_executor<1>()} & [& _p = p] { _p += 42; });\n+\n+        check_valid_future(sut);\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n /*\n@@ -131,72 +221,172 @@ BOOST_AUTO_TEST_CASE(future_void_two_tasks_with_different_scheduler) {\n BOOST_AUTO_TEST_CASE(future_void_Y_formation_tasks_with_same_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future void with Y formation with same scheduler\");\n \n-    std::atomic_int p{0};\n-    int r1 = 0;\n-    int r2 = 0;\n+    {\n+        atomic_int p{0};\n+        int r1 = 0;\n+        int r2 = 0;\n \n-    sut = async(custom_scheduler<0>(), [& _p = p] { _p = 42; });\n-    auto f1 = sut.then(custom_scheduler<0>(), [& _p = p, &_r = r1] { _r = 42 + _p; });\n-    auto f2 = sut.then(custom_scheduler<0>(), [& _p = p, &_r = r2] { _r = 4711 + _p; });\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; });\n+        auto f1 = sut.then([& _p = p, &_r = r1] { _r = 42 + _p; });\n+        auto f2 = sut.then([& _p = p, &_r = r2] { _r = 4711 + _p; });\n \n-    check_valid_future(sut, f1, f2);\n-    wait_until_future_completed(f1, f2);\n+        check_valid_future(sut, f1, f2);\n+        wait_until_future_completed(f1, f2);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, r1);\n-    BOOST_REQUIRE_EQUAL(42 + 4711, r2);\n-    BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, r1);\n+        BOOST_REQUIRE_EQUAL(42 + 4711, r2);\n+        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        atomic_int p{0};\n+        int r1 = 0;\n+        int r2 = 0;\n+\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; });\n+        auto f1 = sut | [& _p = p, &_r = r1] { _r = 42 + _p; };\n+        auto f2 = sut | [& _p = p, &_r = r2] { _r = 4711 + _p; };\n+\n+        check_valid_future(sut, f1, f2);\n+        wait_until_future_completed(f1, f2);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, r1);\n+        BOOST_REQUIRE_EQUAL(42 + 4711, r2);\n+        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(reduction_future_void) {\n     BOOST_TEST_MESSAGE(\"running future reduction void to void\");\n \n-    std::atomic_bool first{false};\n-    std::atomic_bool second{false};\n+    {\n+        bool first{false};\n+        bool second{false};\n \n-    sut = async(default_executor, [& _flag = first] { _flag = true; }).then([& _flag = second] {\n-        return async(default_executor, [&_flag] { _flag = true; });\n-    });\n+        sut = async(make_executor<0>(), [&] { first = true; }).then([&] {\n+            return async(make_executor<0>(), [&] { second = true; });\n+        });\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE(first);\n-    BOOST_REQUIRE(second);\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n+    {\n+        bool first{false};\n+        bool second{false};\n+\n+        sut = async(make_executor<0>(), [&] { first = true; }) |\n+              [&] { return async(make_executor<0>(), [&] { second = true; }); };\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(reduction_future_int_to_void) {\n     BOOST_TEST_MESSAGE(\"running future reduction int to void\");\n-    std::atomic_bool first{false};\n-    std::atomic_bool second{false};\n-    std::atomic_int result{0};\n \n-    sut = async(default_executor,\n-                [& _flag = first] {\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+        atomic_int result{0};\n+\n+        sut = async(default_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return 42;\n+              }).then([& _flag = second, &_result = result](auto x) {\n+            return async(\n+                default_executor,\n+                [&_flag, &_result](auto x) {\n                     _flag = true;\n-                    return 42;\n-                })\n-              .then([& _flag = second, &_result = result](auto x) {\n-                  return async(default_executor,\n-                               [&_flag, &_result](auto x) {\n-                                   _flag = true;\n-                                   _result = x + 42;\n-                               },\n-                               x);\n-              });\n+                    _result = x + 42;\n+                },\n+                x);\n+        });\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE(first);\n-    BOOST_REQUIRE(second);\n-    BOOST_REQUIRE_EQUAL(84, result);\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+        BOOST_REQUIRE_EQUAL(84, result);\n+    }\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+        atomic_int result{0};\n+\n+        sut = async(default_executor,\n+                    [& _flag = first] {\n+                        _flag = true;\n+                        return 42;\n+                    }) |\n+              [& _flag = second, &_result = result](auto x) {\n+                  return async(\n+                      default_executor,\n+                      [&_flag, &_result](auto x) {\n+                          _flag = true;\n+                          _result = x + 42;\n+                      },\n+                      x);\n+              };\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+        BOOST_REQUIRE_EQUAL(84, result);\n+    }\n+}\n+\n+BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_void) {\n+    BOOST_TEST_MESSAGE(\"running future reduction move-only to void\");\n+    {\n+        atomic_bool first{false};\n+        move_only result;\n+\n+        sut = async(default_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return move_only(42);\n+              }).then([& _result = result](auto&& x) {\n+            return async(\n+                default_executor, [&_result](auto&& x) { _result = std::move(x); },\n+                forward<move_only>(x));\n+        });\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE_EQUAL(42, result.member());\n+    }\n+    {\n+        bool first{false};\n+        move_only result;\n+\n+        sut = async(immediate_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return move_only(42);\n+              }).then([& _result = result](auto&& x) {\n+            return async(\n+                immediate_executor, [&_result](auto&& x) { _result = std::move(x); },\n+                forward<move_only>(x));\n+        });\n+\n+        BOOST_REQUIRE(sut.get_try());\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE_EQUAL(42, result.member());\n+    }\n }\n \n BOOST_AUTO_TEST_SUITE_END()\n \n-BOOST_FIXTURE_TEST_SUITE(future_then_non_copyable, test_fixture<stlab::move_only>)\n+BOOST_FIXTURE_TEST_SUITE(future_then_non_copyable, test_fixture<move_only>)\n BOOST_AUTO_TEST_CASE(future_non_copyable_single_task) {\n     BOOST_TEST_MESSAGE(\"running future non copyable single task\");\n \n-    sut = async(custom_scheduler<0>(), [] { return move_only(42); });\n+    sut = async(make_executor<0>(), [] { return move_only(42); });\n \n     check_valid_future(sut);\n     auto result = wait_until_future_r_completed(sut);\n@@ -207,26 +397,24 @@ BOOST_AUTO_TEST_CASE(future_non_copyable_single_task) {\n \n BOOST_AUTO_TEST_CASE(future_then_non_copyable_detach) {\n     BOOST_TEST_MESSAGE(\"running future non copyable, detached\");\n-    std::atomic_bool check{false};\n+    atomic_bool check{false};\n     {\n-        async(custom_scheduler<0>(),\n-              [& _check = check] {\n-                  _check = true;\n-                  return move_only(42);\n-              })\n-            .detach();\n+        async(make_executor<0>(), [& _check = check] {\n+            _check = true;\n+            return move_only(42);\n+        }).detach();\n     }\n     while (!check) {\n-        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n+        this_thread::sleep_for(chrono::milliseconds(1));\n     }\n }\n \n BOOST_AUTO_TEST_CASE(future_non_copyable_capture) {\n     BOOST_TEST_MESSAGE(\"running future non copyable capture\");\n \n-    stlab::v1::move_only m{42};\n+    move_only m{42};\n \n-    sut = async(custom_scheduler<0>(), [& _m = m] { return move_only(_m.member()); });\n+    sut = async(make_executor<0>(), [& _m = m] { return move_only(_m.member()); });\n \n     check_valid_future(sut);\n     auto result = wait_until_future_r_completed(sut);\n@@ -240,119 +428,217 @@ BOOST_AUTO_TEST_CASE(\n     BOOST_TEST_MESSAGE(\n         \"running future copyable with non copyable as contination with same scheduler, then on r-value\");\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; }).then([](auto x) { return move_only(x); });\n+    {\n+        sut =\n+            async(make_executor<0>(), [] { return 42; }).then([](auto x) { return move_only(x); });\n \n-    check_valid_future(sut);\n-    auto result = wait_until_future_r_completed(sut);\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        sut = async(make_executor<0>(), [] { return 42; }) | [](auto x) { return move_only(x); };\n+\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_copyable_with_non_copyable_as_continuation_with_different_scheduler_then_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future copyable with non copyable as contination with different scheduler, then on r-value\");\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; }).then(custom_scheduler<1>(), [](auto x) {\n-        return move_only(x);\n-    });\n+    {\n+        sut = async(make_executor<0>(), [] { return 42; }).then(make_executor<1>(), [](auto x) {\n+            return move_only(x);\n+        });\n \n-    check_valid_future(sut);\n-    auto result = wait_until_future_r_completed(sut);\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n+\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n+\n+    {\n+        sut = async(make_executor<0>(), [] { return 42; }) |\n+              (executor{make_executor<1>()} & [](auto x) { return move_only(x); });\n+\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_copyable_with_non_copyable_as_continuation_with_same_scheduler_then_on_lvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future copyable with non copyable as contination with same scheduler, then on l-value\");\n \n-    auto interim = async(custom_scheduler<0>(), [] { return 42; });\n+    {\n+        auto interim = async(make_executor<0>(), [] { return 42; });\n+\n+        sut = interim.then([](auto x) { return move_only(x); });\n \n-    sut = interim.then([](auto x) { return move_only(x); });\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    check_valid_future(sut);\n-    auto result = wait_until_future_r_completed(sut);\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        auto interim = async(make_executor<0>(), [] { return 42; });\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        sut = interim | [](auto x) { return move_only(x); };\n+\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(\n     future_copyable_with_non_copyable_as_continuation_with_different_scheduler_then_on_lvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future copyable with non copyable as contination with different scheduler, then on l-value\");\n+    {\n+        auto interim = async(make_executor<0>(), [] { return 42; });\n \n-    auto interim = async(custom_scheduler<0>(), [] { return 42; });\n+        sut = interim.then(make_executor<1>(), [](auto x) { return move_only(x); });\n \n-    sut = interim.then(custom_scheduler<1>(), [](auto x) { return move_only(x); });\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    check_valid_future(sut);\n-    auto result = wait_until_future_r_completed(sut);\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n+    {\n+        auto interim = async(make_executor<0>(), [] { return 42; });\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+        sut = interim | (executor{make_executor<1>()} & [](auto x) { return move_only(x); });\n+\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_non_copyable_as_continuation_with_same_scheduler_then_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future non copyable as contination with same scheduler, then on r-value\");\n \n-    sut = async(custom_scheduler<0>(), [] { return move_only(42); }).then([](auto x) {\n-        return move_only(x.member() * 2);\n-    });\n+    {\n+        sut = async(make_executor<0>(), [] { return move_only(42); }).then([](auto&& x) {\n+            return move_only(x.member() * 2);\n+        });\n \n-    check_valid_future(sut);\n-    auto result = wait_until_future_r_completed(sut);\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 * 2, result->member());\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 * 2, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        sut = async(make_executor<0>(), [] { return move_only(42); }) |\n+              [](auto&& x) { return move_only(x.member() * 2); };\n+\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 * 2, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_non_copyable_as_continuation_with_different_scheduler_then_on_rvalue) {\n     BOOST_TEST_MESSAGE(\n         \"running future non copyable as contination with different scheduler, then on r-value\");\n+    {\n+        sut = async(make_executor<0>(), [] {\n+                  return move_only(42);\n+              }).then(make_executor<1>(), [](auto x) { return move_only(x.member() * 2); });\n \n-    sut = async(custom_scheduler<0>(), [] { return move_only(42); })\n-              .then(custom_scheduler<1>(), [](auto x) { return move_only(x.member() * 2); });\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    check_valid_future(sut);\n-    auto result = wait_until_future_r_completed(sut);\n+        BOOST_REQUIRE_EQUAL(42 * 2, result->member());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n+\n+    custom_scheduler<0>::reset();\n+    custom_scheduler<1>::reset();\n \n-    BOOST_REQUIRE_EQUAL(42 * 2, result->member());\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n-    BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    {\n+        sut = async(make_executor<0>(), [] { return move_only(42); }) |\n+              (executor{make_executor<1>()} & [](auto x) { return move_only(x.member() * 2); });\n+\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 * 2, result->member());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_SUITE_END()\n \n-BOOST_FIXTURE_TEST_SUITE(future_then_move_only, test_fixture<stlab::v1::move_only>)\n+BOOST_FIXTURE_TEST_SUITE(future_then_move_only, test_fixture<move_only>)\n \n BOOST_AUTO_TEST_CASE(future_async_move_only_move_captured_to_result) {\n     BOOST_TEST_MESSAGE(\"running future move only move to result\");\n \n-    sut = async(custom_scheduler<0>(), [] { return move_only{42}; }).then([](auto x) {\n-        return std::move(x);\n-    });\n+    {\n+        sut = async(make_executor<0>(), [] { return move_only{42}; }).then([](auto x) {\n+            return move(x);\n+        });\n \n-    check_valid_future(sut);\n-    auto result = wait_until_future_r_completed(sut);\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        sut = async(make_executor<0>(), [] { return move_only{42}; }) |\n+              [](auto x) { return move(x); };\n+\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_async_moving_move_only_capture_to_result) {\n     BOOST_TEST_MESSAGE(\"moving move_only capture to result\");\n \n-    stlab::v1::move_only m{42};\n+    move_only m{42};\n \n-    sut = async(custom_scheduler<0>(), [& _m = m] { return std::move(_m); });\n+    sut = async(make_executor<0>(), [& _m = m] { return move(_m); });\n \n     check_valid_future(sut);\n     auto result = wait_until_future_r_completed(sut);\n@@ -364,9 +650,9 @@ BOOST_AUTO_TEST_CASE(future_async_moving_move_only_capture_to_result) {\n BOOST_AUTO_TEST_CASE(future_async_mutable_move_move_only_capture_to_result) {\n     BOOST_TEST_MESSAGE(\"moving move_only capture to result in mutable task\");\n \n-    stlab::v1::move_only m{42};\n+    move_only m{42};\n \n-    sut = async(custom_scheduler<0>(), [& _m = m]() mutable { return std::move(_m); });\n+    sut = async(make_executor<0>(), [& _m = m]() mutable { return move(_m); });\n \n     check_valid_future(sut);\n     auto result = wait_until_future_r_completed(sut);\n@@ -378,10 +664,11 @@ BOOST_AUTO_TEST_CASE(future_async_mutable_move_move_only_capture_to_result) {\n BOOST_AUTO_TEST_CASE(future_continuation_moving_move_only_capture_to_result) {\n     BOOST_TEST_MESSAGE(\"moving move_only capture to result\");\n \n-    stlab::v1::move_only m{42};\n+    move_only m{42};\n \n-    sut = async(custom_scheduler<0>(), [] { return stlab::v1::move_only{10}; })\n-              .then([& _m = m](auto) mutable { return std::move(_m); });\n+    sut = async(make_executor<0>(), [] { return move_only{10}; }).then([& _m = m](auto) mutable {\n+        return move(_m);\n+    });\n \n     check_valid_future(sut);\n     auto result = wait_until_future_r_completed(sut);\n@@ -393,16 +680,81 @@ BOOST_AUTO_TEST_CASE(future_continuation_moving_move_only_capture_to_result) {\n BOOST_AUTO_TEST_CASE(future_continuation_async_mutable_move_move_only_capture_to_result) {\n     BOOST_TEST_MESSAGE(\"moving move_only capture to result in mutable task\");\n \n-    stlab::v1::move_only m{42};\n+    {\n+        move_only m{42};\n \n-    sut = async(custom_scheduler<0>(), []() mutable { return stlab::v1::move_only{10}; })\n-              .then([& _m = m](auto) mutable { return std::move(_m); });\n+        sut = async(make_executor<0>(), []() mutable {\n+                  return move_only{10};\n+              }).then([& _m = m](auto) mutable { return move(_m); });\n \n-    check_valid_future(sut);\n-    auto result = wait_until_future_r_completed(sut);\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42, result->member());\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        move_only m{42};\n+\n+        sut = async(make_executor<0>(), []() mutable { return move_only{10}; }) |\n+              [& _m = m](auto) mutable { return move(_m); };\n+\n+        check_valid_future(sut);\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42, result->member());\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n+}\n+\n+BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_move_only) {\n+    BOOST_TEST_MESSAGE(\"running future reduction move-only to move-only\");\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return move_only(42);\n+              }).then([& _flag = second](auto&& x) {\n+            return async(\n+                default_executor,\n+                [&_flag](auto&& x) {\n+                    _flag = true;\n+                    return forward<move_only>(x);\n+                },\n+                forward<move_only>(x));\n+        });\n+\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+        BOOST_REQUIRE_EQUAL(42, (*result).member());\n+    }\n+    {\n+        bool first{false};\n+        bool second{false};\n+\n+        sut = async(immediate_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return move_only(42);\n+              }).then([& _flag = second](auto&& x) {\n+            return async(\n+                immediate_executor,\n+                [&_flag](auto&& x) {\n+                    _flag = true;\n+                    return forward<move_only>(x);\n+                },\n+                forward<move_only>(x));\n+        });\n+\n+        auto result = wait_until_future_r_completed(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+        BOOST_REQUIRE_EQUAL(42, (*result).member());\n+    }\n }\n \n BOOST_AUTO_TEST_SUITE_END()\n@@ -412,7 +764,7 @@ BOOST_FIXTURE_TEST_SUITE(future_then_int, test_fixture<int>)\n BOOST_AUTO_TEST_CASE(future_int_single_task) {\n     BOOST_TEST_MESSAGE(\"running future int single tasks\");\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; });\n+    sut = async(make_executor<0>(), [] { return 42; });\n \n     check_valid_future(sut);\n     wait_until_future_completed(sut);\n@@ -424,12 +776,12 @@ BOOST_AUTO_TEST_CASE(future_int_single_task) {\n BOOST_AUTO_TEST_CASE(future_int_single_task_get_try_on_rvalue) {\n     BOOST_TEST_MESSAGE(\"running future int single tasks, get_try on r-value\");\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; });\n+    sut = async(make_executor<0>(), [] { return 42; });\n \n-    auto test_result_1 = std::move(sut).get_try(); // test for r-value implementation\n+    auto test_result_1 = move(sut).get_try(); // test for r-value implementation\n     (void)test_result_1;\n     wait_until_future_completed(sut);\n-    auto test_result_2 = std::move(sut).get_try();\n+    auto test_result_2 = move(sut).get_try();\n \n     BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n     BOOST_REQUIRE_EQUAL(42, *test_result_2);\n@@ -438,49 +790,73 @@ BOOST_AUTO_TEST_CASE(future_int_single_task_get_try_on_rvalue) {\n \n BOOST_AUTO_TEST_CASE(future_int_single_task_detached) {\n     BOOST_TEST_MESSAGE(\"running future int single tasks, detached\");\n-    std::atomic_bool check{false};\n+    atomic_bool check{false};\n     {\n-        auto detached = async(custom_scheduler<0>(), [& _check = check] {\n+        auto detached = async(make_executor<0>(), [& _check = check] {\n             _check = true;\n             return 42;\n         });\n         detached.detach();\n     }\n     while (!check) {\n-        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n+        this_thread::sleep_for(chrono::milliseconds(1));\n     }\n }\n \n BOOST_AUTO_TEST_CASE(future_int_two_tasks_with_same_scheduler_then_on_rvalue) {\n     BOOST_TEST_MESSAGE(\"running future int two tasks with same scheduler, then on r-value\");\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; }).then([](auto x) { return x + 42; });\n+    {\n+        sut = async(make_executor<0>(), [] { return 42; }).then([](auto x) { return x + 42; });\n \n-    check_valid_future(sut);\n-    wait_until_future_completed(sut);\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());\n-    BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        sut = async(make_executor<0>(), [] { return 42; }) | [](auto x) { return x + 42; };\n+\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_int_two_tasks_with_same_scheduler_then_on_lvalue) {\n     BOOST_TEST_MESSAGE(\"running future int two tasks with same scheduler, then on l-value\");\n \n-    auto interim = async(custom_scheduler<0>(), [] { return 42; });\n+    {\n+        auto interim = async(make_executor<0>(), [] { return 42; });\n \n-    sut = interim.then([](auto x) { return x + 42; });\n+        sut = interim.then([](auto x) { return x + 42; });\n \n-    check_valid_future(sut);\n-    wait_until_future_completed(sut);\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());\n-    BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        auto interim = async(make_executor<0>(), [] { return 42; });\n+\n+        sut = interim | [](auto x) { return x + 42; };\n+\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_int_two_tasks_with_different_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future int two tasks with different scheduler\");\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; }).then(custom_scheduler<1>(), [](auto x) {\n+    sut = async(make_executor<0>(), [] { return 42; }).then(make_executor<1>(), [](auto x) {\n         return x + 42;\n     });\n \n@@ -495,30 +871,45 @@ BOOST_AUTO_TEST_CASE(future_int_two_tasks_with_different_scheduler) {\n BOOST_AUTO_TEST_CASE(future_void_int_two_tasks_with_same_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future void int tasks with same scheduler\");\n \n-    std::atomic_int p{0};\n+    {\n+        atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), [& _p = p] { _p = 42; }).then([& _p = p] {\n-        _p += 42;\n-        return _p.load();\n-    });\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then([& _p = p] {\n+            _p += 42;\n+            return _p.load();\n+        });\n \n-    check_valid_future(sut);\n-    wait_until_future_completed(sut);\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, p);\n-    BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        atomic_int p{0};\n+\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) | [& _p = p] {\n+            _p += 42;\n+            return _p.load();\n+        };\n+\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_void_int_two_tasks_with_different_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future void int tasks with different schedulers\");\n \n-    std::atomic_int p{0};\n+    atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), [& _p = p] { _p = 42; })\n-              .then(custom_scheduler<1>(), [& _p = p] {\n-                  _p += 42;\n-                  return _p.load();\n-              });\n+    sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then(make_executor<1>(), [& _p = p] {\n+        _p += 42;\n+        return _p.load();\n+    });\n \n     check_valid_future(sut);\n     wait_until_future_completed(sut);\n@@ -534,15 +925,29 @@ BOOST_AUTO_TEST_CASE(future_void_int_two_tasks_with_different_scheduler) {\n BOOST_AUTO_TEST_CASE(future_int_three_tasks_with_same_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future int with three tasks with same scheduler\");\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; })\n-              .then(custom_scheduler<0>(), [](auto x) { return x + 42; })\n-              .then(custom_scheduler<0>(), [](auto x) { return x + 42; });\n+    {\n+        sut = async(make_executor<0>(), [] {\n+                  return 42;\n+              }).then([](auto x) {\n+                    return x + 42;\n+                }).then([](auto x) { return x + 42; });\n \n-    check_valid_future(sut);\n-    wait_until_future_completed(sut);\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42 + 42, *sut.get_try());\n-    BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42 + 42, *sut.get_try());\n+        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        sut = async(make_executor<0>(), [] { return 42; }) | [](auto x) { return x + 42; } |\n+              [](auto x) { return x + 42; };\n+\n+        check_valid_future(sut);\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42 + 42, *sut.get_try());\n+        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n /*\n@@ -555,61 +960,122 @@ BOOST_AUTO_TEST_CASE(future_int_three_tasks_with_same_scheduler) {\n BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_with_same_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future int Y formation tasks with same scheduler\");\n \n-    sut = async(custom_scheduler<0>(), [] { return 42; });\n-    auto f1 = sut.then(custom_scheduler<0>(), [](auto x) -> int { return x + 42; });\n-    auto f2 = sut.then(custom_scheduler<0>(), [](auto x) -> int { return x + 4177; });\n+    {\n+        sut = async(make_executor<0>(), [] { return 42; });\n+        auto f1 = sut.then([](auto x) { return x + 42; });\n+        auto f2 = sut.then([](auto x) { return x + 4177; });\n \n-    check_valid_future(sut, f1, f2);\n-    wait_until_future_completed(f1, f2);\n+        check_valid_future(sut, f1, f2);\n+        wait_until_future_completed(f1, f2);\n \n-    BOOST_REQUIRE_EQUAL(42 + 42, *f1.get_try());\n-    BOOST_REQUIRE_EQUAL(42 + 4177, *f2.get_try());\n-    BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+        BOOST_REQUIRE_EQUAL(42 + 42, *f1.get_try());\n+        BOOST_REQUIRE_EQUAL(42 + 4177, *f2.get_try());\n+        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        sut = async(make_executor<0>(), [] { return 42; });\n+        auto f1 = sut | [](auto x) { return x + 42; };\n+        auto f2 = sut | [](auto x) { return x + 4177; };\n+\n+        check_valid_future(sut, f1, f2);\n+        wait_until_future_completed(f1, f2);\n+\n+        BOOST_REQUIRE_EQUAL(42 + 42, *f1.get_try());\n+        BOOST_REQUIRE_EQUAL(42 + 4177, *f2.get_try());\n+        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(reduction_future_void_to_int) {\n     BOOST_TEST_MESSAGE(\"running future reduction void to int\");\n-    std::atomic_bool first{false};\n-    std::atomic_bool second{false};\n \n-    sut = async(default_executor, [& _flag = first] { _flag = true; }).then([& _flag = second] {\n-        return async(default_executor, [&_flag] {\n-            _flag = true;\n-            return 42;\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] { _flag = true; }).then([& _flag = second] {\n+            return async(default_executor, [&_flag] {\n+                _flag = true;\n+                return 42;\n+            });\n         });\n-    });\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE(first);\n-    BOOST_REQUIRE(second);\n-    BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+    }\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] { _flag = true; }) | [& _flag = second] {\n+            return async(default_executor, [&_flag] {\n+                _flag = true;\n+                return 42;\n+            });\n+        };\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+        BOOST_REQUIRE_EQUAL(42, *sut.get_try());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(reduction_future_int_to_int) {\n     BOOST_TEST_MESSAGE(\"running future reduction int to int\");\n-    std::atomic_bool first{false};\n-    std::atomic_bool second{false};\n \n-    sut = async(default_executor,\n-                [& _flag = first] {\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return 42;\n+              }).then([& _flag = second](auto x) {\n+            return async(\n+                default_executor,\n+                [&_flag](auto x) {\n                     _flag = true;\n-                    return 42;\n-                })\n-              .then([& _flag = second](auto x) {\n-                  return async(default_executor,\n-                               [&_flag](auto x) {\n-                                   _flag = true;\n-                                   return x + 42;\n-                               },\n-                               x);\n-              });\n+                    return x + 42;\n+                },\n+                x);\n+        });\n \n-    wait_until_future_completed(sut);\n+        wait_until_future_completed(sut);\n \n-    BOOST_REQUIRE(first);\n-    BOOST_REQUIRE(second);\n-    BOOST_REQUIRE_EQUAL(84, *sut.get_try());\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+        BOOST_REQUIRE_EQUAL(84, *sut.get_try());\n+    }\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor,\n+                    [& _flag = first] {\n+                        _flag = true;\n+                        return 42;\n+                    }) |\n+              [& _flag = second](auto x) {\n+                  return async(\n+                      default_executor,\n+                      [&_flag](auto x) {\n+                          _flag = true;\n+                          return x + 42;\n+                      },\n+                      x);\n+              };\n+\n+        wait_until_future_completed(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+        BOOST_REQUIRE_EQUAL(84, *sut.get_try());\n+    }\n }\n BOOST_AUTO_TEST_SUITE_END()\n \n@@ -618,10 +1084,11 @@ BOOST_AUTO_TEST_SUITE_END()\n // ----------------------------------------------------------------------------\n \n BOOST_FIXTURE_TEST_SUITE(future_void_then_error, test_fixture<void>)\n+\n BOOST_AUTO_TEST_CASE(future_void_single_task_error) {\n     BOOST_TEST_MESSAGE(\"running future void with single tasks that fails\");\n \n-    sut = async(custom_scheduler<0>(), [] { throw test_exception(\"failure\"); });\n+    sut = async(make_executor<0>(), [] { throw test_exception(\"failure\"); });\n \n     wait_until_future_fails<test_exception>(sut);\n     check_failure<test_exception>(sut, \"failure\");\n@@ -631,50 +1098,147 @@ BOOST_AUTO_TEST_CASE(future_void_single_task_error) {\n BOOST_AUTO_TEST_CASE(future_void_two_tasks_error_in_1st_task_with_same_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future void with two tasks which first fails\");\n \n-    std::atomic_int p{0};\n+    {\n+        atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), [] { throw test_exception(\"failure\"); }).then([& _p = p] {\n-        _p = 42;\n-    });\n+        sut = async(make_executor<0>(), [] { throw test_exception(\"failure\"); }).then([& _p = p] {\n+            _p = 42;\n+        });\n \n-    wait_until_future_fails<test_exception>(sut);\n-    check_failure<test_exception>(sut, \"failure\");\n-    BOOST_REQUIRE_EQUAL(0, p);\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        wait_until_future_fails<test_exception>(sut);\n+        check_failure<test_exception>(sut, \"failure\");\n+        BOOST_REQUIRE_EQUAL(0, p);\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        atomic_int p{0};\n+\n+        sut = async(make_executor<0>(), [] { throw test_exception(\"failure\"); }) |\n+              [& _p = p] { _p = 42; };\n+\n+        wait_until_future_fails<test_exception>(sut);\n+        check_failure<test_exception>(sut, \"failure\");\n+        BOOST_REQUIRE_EQUAL(0, p);\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_void_two_tasks_error_in_2nd_task_with_same_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future void with two tasks which second fails\");\n \n-    std::atomic_int p{0};\n+    {\n+        atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), [& _p = p] { _p = 42; }).then([& _p = p] {\n-        throw test_exception(\"failure\");\n-    });\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then([& _p = p] {\n+            (void)_p;\n+            throw test_exception(\"failure\");\n+        });\n \n-    wait_until_future_fails<test_exception>(sut);\n+        wait_until_future_fails<test_exception>(sut);\n \n-    check_failure<test_exception>(sut, \"failure\");\n-    BOOST_REQUIRE_EQUAL(42, p);\n-    BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+        check_failure<test_exception>(sut, \"failure\");\n+        BOOST_REQUIRE_EQUAL(42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        atomic_int p{0};\n+\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) | [& _p = p] {\n+            (void)_p;\n+            throw test_exception(\"failure\");\n+        };\n+\n+        wait_until_future_fails<test_exception>(sut);\n+\n+        check_failure<test_exception>(sut, \"failure\");\n+        BOOST_REQUIRE_EQUAL(42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(reduction_future_void_to_void_error) {\n     BOOST_TEST_MESSAGE(\"running future reduction void to void where the inner future fails\");\n-    std::atomic_bool first{false};\n-    std::atomic_bool second{false};\n \n-    sut = async(default_executor, [& _flag = first] { _flag = true; }).then([& _flag = second] {\n-        return async(default_executor, [&_flag] {\n-            _flag = true;\n-            throw test_exception(\"failure\");\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] { _flag = true; }).then([& _flag = second] {\n+            return async(default_executor, [&_flag] {\n+                _flag = true;\n+                throw test_exception(\"failure\");\n+            });\n         });\n-    });\n \n-    wait_until_future_fails<test_exception>(sut);\n+        wait_until_future_fails<test_exception>(sut);\n \n-    BOOST_REQUIRE(first);\n-    BOOST_REQUIRE(second);\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] { _flag = true; }) | [& _flag = second] {\n+            return async(default_executor, [&_flag] {\n+                _flag = true;\n+                throw test_exception(\"failure\");\n+            });\n+        };\n+\n+        wait_until_future_fails<test_exception>(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n+}\n+\n+BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_void_when_inner_future_fails) {\n+    BOOST_TEST_MESSAGE(\"running future reduction move-only to void when inner future fails\");\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return move_only(42);\n+              }).then([& _check = second](auto&& x) {\n+            return async(\n+                default_executor,\n+                [&_check](auto&&) {\n+                    _check = true;\n+                    throw test_exception(\"failure\");\n+                },\n+                forward<move_only>(x));\n+        });\n+\n+        wait_until_future_fails<test_exception>(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n+    {\n+        bool first{false};\n+        bool second{false};\n+\n+        sut = async(immediate_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return move_only(42);\n+              }).then([& _check = second](auto&& x) {\n+            return async(\n+                immediate_executor,\n+                [&_check](auto&&) {\n+                    _check = true;\n+                    throw test_exception(\"failure\");\n+                },\n+                forward<move_only>(x));\n+        });\n+\n+        wait_until_future_fails<test_exception>(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n }\n \n BOOST_AUTO_TEST_SUITE_END()\n@@ -684,67 +1248,94 @@ BOOST_FIXTURE_TEST_SUITE(future_then_int_error, test_fixture<int>)\n BOOST_AUTO_TEST_CASE(future_int_single_task_error) {\n     BOOST_TEST_MESSAGE(\"running future int with single tasks that fails\");\n \n-    sut = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    sut = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n     wait_until_future_fails<test_exception>(sut);\n \n     check_failure<test_exception>(sut, \"failure\");\n     BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n }\n-#if 0 // currently disabled, because I have doubts that get_try on an r-value makes any sense at al\n-    BOOST_AUTO_TEST_CASE(future_int_single_task_with_error_get_try_on_rvalue) {\n-        BOOST_TEST_MESSAGE(\"running future int single tasks, with error on get_try on r-value\");\n \n-        sut = async(custom_scheduler<0>(), []()->int { throw test_exception(\"failure\"); });\n-        auto test_result_1 = std::move(sut).get_try(); // test for r-value implementation\n+BOOST_AUTO_TEST_CASE(future_int_two_tasks_error_in_1st_task_with_same_scheduler) {\n+    BOOST_TEST_MESSAGE(\"running future int with two tasks which first fails\");\n+\n+    {\n+        custom_scheduler<0>::reset();\n+        int p = 0;\n+\n+        sut = async(make_executor<0>(), [] {\n+                  throw test_exception(\"failure\");\n+              }).then([& _p = p]() -> int {\n+            _p = 42;\n+            return _p;\n+        });\n+\n         wait_until_future_fails<test_exception>(sut);\n-        check_failure<test_exception>(std::move(sut), \"failure\");\n+\n+        check_failure<test_exception>(sut, \"failure\");\n+        BOOST_REQUIRE_EQUAL(0, p);\n         BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n     }\n-#endif\n-BOOST_AUTO_TEST_CASE(future_int_two_tasks_error_in_1st_task_with_same_scheduler) {\n-    BOOST_TEST_MESSAGE(\"running future int with two tasks which first fails\");\n-    int p = 0;\n+    {\n+        custom_scheduler<0>::reset();\n+        int p = 0;\n \n-    sut = async(custom_scheduler<0>(), [] { throw test_exception(\"failure\"); })\n-              .then([& _p = p]() -> int {\n-                  _p = 42;\n-                  return _p;\n-              });\n+        sut = async(make_executor<0>(), [] { throw test_exception(\"failure\"); }) |\n+              [& _p = p]() -> int {\n+            _p = 42;\n+            return _p;\n+        };\n \n-    wait_until_future_fails<test_exception>(sut);\n+        wait_until_future_fails<test_exception>(sut);\n \n-    check_failure<test_exception>(sut, \"failure\");\n-    BOOST_REQUIRE_EQUAL(0, p);\n-    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+        check_failure<test_exception>(sut, \"failure\");\n+        BOOST_REQUIRE_EQUAL(0, p);\n+        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_int_two_tasks_error_in_2nd_task_with_same_scheduler) {\n     BOOST_TEST_MESSAGE(\"running future void with two tasks which second fails\");\n \n-    std::atomic_int p{0};\n+    {\n+        custom_scheduler<0>::reset();\n+        atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), [& _p = p] { _p = 42; }).then([]() -> int {\n-        throw test_exception(\"failure\");\n-    });\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then([]() -> int {\n+            throw test_exception(\"failure\");\n+        });\n \n-    wait_until_future_fails<test_exception>(sut);\n+        wait_until_future_fails<test_exception>(sut);\n \n-    check_failure<test_exception>(sut, \"failure\");\n-    BOOST_REQUIRE_EQUAL(42, p);\n-    BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+        check_failure<test_exception>(sut, \"failure\");\n+        BOOST_REQUIRE_EQUAL(42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n+    {\n+        custom_scheduler<0>::reset();\n+        atomic_int p{0};\n+\n+        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) |\n+              []() -> int { throw test_exception(\"failure\"); };\n+\n+        wait_until_future_fails<test_exception>(sut);\n+\n+        check_failure<test_exception>(sut, \"failure\");\n+        BOOST_REQUIRE_EQUAL(42, p);\n+        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());\n+    }\n }\n \n BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_with_failing_1st_task) {\n     BOOST_TEST_MESSAGE(\"running future int Y formation tasks where the 1st tasks fails\");\n \n-    std::atomic_int p{0};\n+    atomic_int p{0};\n \n-    sut = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n-    auto f1 = sut.then(custom_scheduler<0>(), [& _p = p](auto x) -> int {\n+    sut = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    auto f1 = sut.then(make_executor<0>(), [& _p = p](auto x) -> int {\n         _p += 1;\n         return x + 42;\n     });\n-    auto f2 = sut.then(custom_scheduler<0>(), [& _p = p](auto x) -> int {\n+    auto f2 = sut.then(make_executor<0>(), [& _p = p](auto x) -> int {\n         _p += 1;\n         return x + 4177;\n     });\n@@ -760,9 +1351,9 @@ BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_with_failing_1st_task) {\n BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_where_one_of_the_2nd_task_failing) {\n     BOOST_TEST_MESSAGE(\"running future int Y formation tasks where one of the 2nd tasks fails\");\n \n-    sut = async(custom_scheduler<0>(), []() -> int { return 42; });\n-    auto f1 = sut.then(custom_scheduler<0>(), [](auto) -> int { throw test_exception(\"failure\"); });\n-    auto f2 = sut.then(custom_scheduler<0>(), [](auto x) -> int { return x + 4711; });\n+    sut = async(make_executor<0>(), []() -> int { return 42; });\n+    auto f1 = sut.then(make_executor<0>(), [](auto) -> int { throw test_exception(\"failure\"); });\n+    auto f2 = sut.then(make_executor<0>(), [](auto x) -> int { return x + 4711; });\n \n     wait_until_future_completed(f2);\n     wait_until_future_fails<test_exception>(f1);\n@@ -775,9 +1366,9 @@ BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_where_one_of_the_2nd_task_fail\n BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_where_both_of_the_2nd_task_failing) {\n     BOOST_TEST_MESSAGE(\"running future int Y formation tasks where both of the 2nd tasks fails\");\n \n-    sut = async(custom_scheduler<0>(), []() -> int { return 42; });\n-    auto f1 = sut.then(custom_scheduler<0>(), [](auto) -> int { throw test_exception(\"failure\"); });\n-    auto f2 = sut.then(custom_scheduler<0>(), [](auto) -> int { throw test_exception(\"failure\"); });\n+    sut = async(make_executor<0>(), []() -> int { return 42; });\n+    auto f1 = sut.then(make_executor<0>(), [](auto) -> int { throw test_exception(\"failure\"); });\n+    auto f2 = sut.then(make_executor<0>(), [](auto) -> int { throw test_exception(\"failure\"); });\n \n     wait_until_future_fails<test_exception>(f1, f2);\n \n@@ -788,11 +1379,15 @@ BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_where_both_of_the_2nd_task_fai\n \n BOOST_AUTO_TEST_CASE(reduction_future_void_to_int_error) {\n     BOOST_TEST_MESSAGE(\"running future reduction void to int where the outer future fails\");\n-    std::atomic_bool first{false};\n-    std::atomic_bool second{false};\n+    atomic_bool first{false};\n+    atomic_bool second{false};\n \n-    sut = async(default_executor, [& _flag = first] { _flag = true; })\n-              .then([& _flag = second]() -> future<int> { throw test_exception(\"failure\"); });\n+    sut = async(default_executor, [& _flag = first] {\n+              _flag = true;\n+          }).then([& _flag = second]() -> future<int> {\n+        (void)_flag;\n+        throw test_exception(\"failure\");\n+    });\n \n     wait_until_future_fails<test_exception>(sut);\n \n@@ -802,49 +1397,105 @@ BOOST_AUTO_TEST_CASE(reduction_future_void_to_int_error) {\n \n BOOST_AUTO_TEST_CASE(reduction_future_int_to_int_error) {\n     BOOST_TEST_MESSAGE(\"running future reduction int to int where the inner future fails\");\n-    std::atomic_bool first{false};\n-    std::atomic_bool second{false};\n \n-    sut = async(default_executor,\n-                [& _flag = first] {\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return 42;\n+              }).then([& _flag = second](auto x) {\n+            return async(\n+                default_executor,\n+                [&_flag](auto) -> int {\n                     _flag = true;\n-                    return 42;\n-                })\n-              .then([& _flag = second](auto x) {\n-                  return async(default_executor,\n-                               [&_flag](auto) -> int {\n-                                   _flag = true;\n-                                   throw test_exception(\"failure\");\n-                               },\n-                               x);\n-              });\n+                    throw test_exception(\"failure\");\n+                },\n+                x);\n+        });\n \n-    wait_until_future_fails<test_exception>(sut);\n+        wait_until_future_fails<test_exception>(sut);\n \n-    BOOST_REQUIRE(first);\n-    BOOST_REQUIRE(second);\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor,\n+                    [& _flag = first] {\n+                        _flag = true;\n+                        return 42;\n+                    }) |\n+              [& _flag = second](auto x) {\n+                  return async(\n+                      default_executor,\n+                      [&_flag](auto) -> int {\n+                          _flag = true;\n+                          throw test_exception(\"failure\");\n+                      },\n+                      x);\n+              };\n+\n+        wait_until_future_fails<test_exception>(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n }\n \n BOOST_AUTO_TEST_SUITE_END()\n \n-#if 0\n-BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_move_only) {\n-    BOOST_TEST_MESSAGE(\"running future reduction move-only to move-only\");\n-    std::atomic_bool first{ false };\n-    std::atomic_bool second{ false };\n+BOOST_FIXTURE_TEST_SUITE(future_then_move_only_error, test_fixture<move_only>)\n \n-    future<move_only> a = async(default_executor, [&_flag = first] {\n-        _flag = true; std::cout << 1 << std::endl; return move_only(42); }).then([&_flag = second] (auto&& x) {\n-        return async(default_executor, [&_flag] (auto&& x) {\n-            _flag = true; std::cout << 2 << std::endl; return std::forward<move_only>(x); }, std::forward<move_only>(x));\n-    });\n+BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_move_only_when_inner_future_fails) {\n+    BOOST_TEST_MESSAGE(\"running future reduction move-only to move-only when inner future fails\");\n+    {\n+        atomic_bool first{false};\n+        atomic_bool second{false};\n+\n+        sut = async(default_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return move_only(42);\n+              }).then([& _flag = second](auto&& x) {\n+            return async(\n+                default_executor,\n+                [&_flag](auto&&) -> move_only {\n+                    _flag = true;\n+                    throw test_exception(\"failure\");\n+                },\n+                forward<move_only>(x));\n+        });\n \n-    while (!a.get_try()) {\n-        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n+        wait_until_future_fails<test_exception>(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n     }\n+    {\n+        bool first{false};\n+        bool second{false};\n \n-    BOOST_REQUIRE(first);\n-    BOOST_REQUIRE(second);\n-    BOOST_REQUIRE_EQUAL(42, a.get_try().value().member());\n+        sut = async(immediate_executor, [& _flag = first] {\n+                  _flag = true;\n+                  return move_only(42);\n+              }).then([& _flag = second](auto&& x) {\n+            return async(\n+                immediate_executor,\n+                [&_flag](auto&&) -> move_only {\n+                    _flag = true;\n+                    throw test_exception(\"failure\");\n+                },\n+                forward<move_only>(x));\n+        });\n+\n+        wait_until_future_fails<test_exception>(sut);\n+\n+        BOOST_REQUIRE(first);\n+        BOOST_REQUIRE(second);\n+    }\n }\n-#endif\n+\n+BOOST_AUTO_TEST_SUITE_END()"},{"sha":"43330b0f212b8049708d3d621c63edb8a4e8b6a2","filename":"test/future_when_all_arguments_tests.cpp","status":"modified","additions":92,"deletions":37,"changes":129,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_when_all_arguments_tests.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_when_all_arguments_tests.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_when_all_arguments_tests.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -15,6 +15,7 @@\n #include <string>\n \n #include \"future_test_helper.hpp\"\n+#include <stlab/test/model.hpp>\n \n using namespace stlab;\n using namespace future_test_helper;\n@@ -23,8 +24,8 @@ BOOST_FIXTURE_TEST_SUITE(future_when_all_args_int, test_fixture<int>)\n BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_one_element) {\n     BOOST_TEST_MESSAGE(\"running future when_all int with one element\");\n \n-    auto f1 = async(custom_scheduler<0>(), [] { return 42; });\n-    sut = when_all(custom_scheduler<1>(), [](auto x) { return x + x; }, f1);\n+    auto f1 = async(make_executor<0>(), [] { return 42; });\n+    sut = when_all(make_executor<1>(), [](auto x) { return x + x; }, f1);\n \n     check_valid_future(sut);\n     wait_until_future_completed(sut);\n@@ -37,13 +38,13 @@ BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_one_element) {\n BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_many_elements) {\n     BOOST_TEST_MESSAGE(\"running future when_all args int with many elements\");\n \n-    auto f1 = async(custom_scheduler<0>(), [] { return 1; });\n-    auto f2 = async(custom_scheduler<0>(), [] { return 2; });\n-    auto f3 = async(custom_scheduler<0>(), [] { return 3; });\n-    auto f4 = async(custom_scheduler<0>(), [] { return 5; });\n+    auto f1 = async(make_executor<0>(), [] { return 1; });\n+    auto f2 = async(make_executor<0>(), [] { return 2; });\n+    auto f3 = async(make_executor<0>(), [] { return 3; });\n+    auto f4 = async(make_executor<0>(), [] { return 5; });\n \n     sut = when_all(\n-        custom_scheduler<1>(),\n+        make_executor<1>(),\n         [](int x1, int x2, int x3, int x4) { return 7 * x1 + 11 * x2 + 13 * x3 + 17 * x4; }, f1, f2,\n         f3, f4);\n \n@@ -58,7 +59,22 @@ BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_many_elements) {\n BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_ready_element) {\n     BOOST_TEST_MESSAGE(\"running future when_all int with ready element\");\n \n-    sut = when_all(custom_scheduler<1>(), [](auto x) { return x + x; }, make_ready_future<int>(42, immediate_executor));\n+    sut = when_all(make_executor<1>(), [](auto x) { return x + x; },\n+        make_ready_future<int>(42, immediate_executor));\n+\n+    check_valid_future(sut);\n+    wait_until_future_completed(sut);\n+\n+    BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());\n+    BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+}\n+\n+BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_executor) {\n+    BOOST_TEST_MESSAGE(\"running future when_all int with ready element\");\n+\n+    sut = stlab::when_all(make_executor<1>(), [](auto x, auto y) { return x + y; },\n+                             stlab::make_ready_future<int>(42, stlab::immediate_executor),\n+                             stlab::make_ready_future<int>(42, stlab::immediate_executor));\n \n     check_valid_future(sut);\n     wait_until_future_completed(sut);\n@@ -70,7 +86,7 @@ BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_ready_element) {\n BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_two_ready_element) {\n     BOOST_TEST_MESSAGE(\"running future when_all int with two ready element\");\n \n-    sut = when_all(custom_scheduler<1>(), [](auto x, auto y) { return x + y; },\n+    sut = when_all(make_executor<1>(), [](auto x, auto y) { return x + y; },\n                    make_ready_future<int>(42, immediate_executor),\n                    make_ready_future<int>(42, immediate_executor));\n \n@@ -84,7 +100,7 @@ BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_two_ready_element) {\n BOOST_AUTO_TEST_CASE(future_when_all_args) {\n \n     auto main_thread_id = std::this_thread::get_id();\n-    auto sut = when_all(custom_scheduler<1>(), [] { \n+    auto sut = when_all(make_executor<1>(), [] { \n         return std::this_thread::get_id(); \n     }, make_ready_future(stlab::immediate_executor));\n \n@@ -96,16 +112,55 @@ BOOST_AUTO_TEST_CASE(future_when_all_args) {\n \n BOOST_AUTO_TEST_SUITE_END()\n \n+BOOST_FIXTURE_TEST_SUITE(future_when_all_args_move_only, test_fixture<move_only>)\n+BOOST_AUTO_TEST_CASE(future_when_all_args_move_only_with_one_element) {\n+  BOOST_TEST_MESSAGE(\"running future when_all move_only with one element\");\n+\n+  auto f1 = async(make_executor<0>(), [] { return move_only(42); });\n+  sut = when_all(make_executor<1>(), [](auto x) { return move_only(x.member() + x.member()); }, std::move(f1));\n+\n+  check_valid_future(sut);\n+  wait_until_future_completed(sut);\n+\n+  BOOST_REQUIRE_EQUAL(42 + 42, (*sut.get_try()).member());\n+  BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+  BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+}\n+\n+BOOST_AUTO_TEST_CASE(future_when_all_args_move_only_with_many_elements) {\n+  BOOST_TEST_MESSAGE(\"running future when_all args move_only with many elements\");\n+\n+  auto f1 = async(make_executor<0>(), [] { return move_only(1); });\n+  auto f2 = async(make_executor<0>(), [] { return move_only(2); });\n+  auto f3 = async(make_executor<0>(), [] { return move_only(3); });\n+  auto f4 = async(make_executor<0>(), [] { return move_only(5); });\n+\n+  sut = when_all(\n+    make_executor<1>(),\n+    [](auto x1, auto x2, auto x3, auto x4) { return move_only(7 * x1.member() + 11 * x2.member() + 13 * x3.member() + 17 * x4.member()); }, \n+    std::move(f1), std::move(f2), std::move(f3), std::move(f4));\n+\n+  check_valid_future(sut);\n+  wait_until_future_completed(sut);\n+\n+  BOOST_REQUIRE_EQUAL(1 * 7 + 2 * 11 + 3 * 13 + 5 * 17, (*sut.get_try()).member());\n+  BOOST_REQUIRE_LE(4, custom_scheduler<0>::usage_counter());\n+  BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+}\n+\n+BOOST_AUTO_TEST_SUITE_END()\n+\n+\n BOOST_FIXTURE_TEST_SUITE(future_when_all_args_string, test_fixture<std::string>)\n BOOST_AUTO_TEST_CASE(future_when_all_args_with_different_types) {\n     BOOST_TEST_MESSAGE(\"running future when_all args with different types\");\n \n-    auto f1 = async(custom_scheduler<0>(), [] { return 1; });\n-    auto f2 = async(custom_scheduler<0>(), [] { return 3.1415; });\n-    auto f3 = async(custom_scheduler<0>(), [] { return std::string(\"Don't panic!\"); });\n-    auto f4 = async(custom_scheduler<0>(), [] { return std::vector<size_t>(2, 3); });\n+    auto f1 = async(make_executor<0>(), [] { return 1; });\n+    auto f2 = async(make_executor<0>(), [] { return 3.1415; });\n+    auto f3 = async(make_executor<0>(), [] { return std::string(\"Don't panic!\"); });\n+    auto f4 = async(make_executor<0>(), [] { return std::vector<size_t>(2, 3); });\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [](int x1, double x2, const std::string& x3, const std::vector<size_t>& x4) {\n                        std::stringstream st;\n                        st << x1 << \" \" << x2 << \" \" << x3 << \" \" << x4[0] << \" \" << x4[1];\n@@ -130,8 +185,8 @@ BOOST_FIXTURE_TEST_SUITE(future_when_all_args_int_failure, test_fixture<int>)\n BOOST_AUTO_TEST_CASE(future_when_all_args_int_failure_with_one_element) {\n     BOOST_TEST_MESSAGE(\"running future when_all int with range of one element\");\n \n-    auto f1 = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n-    sut = when_all(custom_scheduler<1>(), [](auto x) { return x + x; }, f1);\n+    auto f1 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    sut = when_all(make_executor<1>(), [](auto x) { return x + x; }, f1);\n \n     wait_until_future_fails<test_exception>(sut);\n \n@@ -143,13 +198,13 @@ BOOST_AUTO_TEST_CASE(future_when_all_args_int_failure_with_one_element) {\n BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_many_elements_one_failing) {\n     BOOST_TEST_MESSAGE(\"running future when_all args int with many elements one failing\");\n \n-    auto f1 = async(custom_scheduler<0>(), [] { return 1; });\n-    auto f2 = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n-    auto f3 = async(custom_scheduler<0>(), [] { return 3; });\n-    auto f4 = async(custom_scheduler<0>(), [] { return 5; });\n+    auto f1 = async(make_executor<0>(), [] { return 1; });\n+    auto f2 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    auto f3 = async(make_executor<0>(), [] { return 3; });\n+    auto f4 = async(make_executor<0>(), [] { return 5; });\n \n     sut = when_all(\n-        custom_scheduler<1>(),\n+        make_executor<1>(),\n         [](int x1, int x2, int x3, int x4) { return 7 * x1 + 11 * x2 + 13 * x3 + 17 * x4; }, f1, f2,\n         f3, f4);\n \n@@ -163,13 +218,13 @@ BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_many_elements_one_failing) {\n BOOST_AUTO_TEST_CASE(future_when_all_args_int_with_many_elements_all_failing) {\n     BOOST_TEST_MESSAGE(\"running future when_all args int with many elements all failing\");\n \n-    auto f1 = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n-    auto f2 = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n-    auto f3 = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n-    auto f4 = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    auto f1 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    auto f2 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    auto f3 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    auto f4 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n \n     sut = when_all(\n-        custom_scheduler<1>(),\n+        make_executor<1>(),\n         [](int x1, int x2, int x3, int x4) { return 7 * x1 + 11 * x2 + 13 * x3 + 17 * x4; }, f1, f2,\n         f3, f4);\n \n@@ -186,13 +241,13 @@ BOOST_FIXTURE_TEST_SUITE(future_when_all_args_string_failure, test_fixture<std::\n BOOST_AUTO_TEST_CASE(future_when_all_args_with_different_types_one_failing) {\n     BOOST_TEST_MESSAGE(\"running future when_all args with different types one failing\");\n \n-    auto f1 = async(custom_scheduler<0>(), [] { return 1; });\n-    auto f2 = async(custom_scheduler<0>(), [] { return 3.1415; });\n+    auto f1 = async(make_executor<0>(), [] { return 1; });\n+    auto f2 = async(make_executor<0>(), [] { return 3.1415; });\n     auto f3 =\n-        async(custom_scheduler<0>(), []() -> std::string { throw test_exception(\"failure\"); });\n-    auto f4 = async(custom_scheduler<0>(), [] { return std::vector<size_t>(2, 3); });\n+        async(make_executor<0>(), []() -> std::string { throw test_exception(\"failure\"); });\n+    auto f4 = async(make_executor<0>(), [] { return std::vector<size_t>(2, 3); });\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [](int x1, double x2, const std::string& x3, const std::vector<size_t>& x4) {\n                        std::stringstream st;\n                        st << x1 << \" \" << x2 << \" \" << x3 << \" \" << x4[0] << \" \" << x4[1];\n@@ -210,14 +265,14 @@ BOOST_AUTO_TEST_CASE(future_when_all_args_with_different_types_one_failing) {\n BOOST_AUTO_TEST_CASE(future_when_all_args_with_different_types_all_failing) {\n     BOOST_TEST_MESSAGE(\"running future when_all args with different types all failing\");\n \n-    auto f1 = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n-    auto f2 = async(custom_scheduler<0>(), []() -> double { throw test_exception(\"failure\"); });\n+    auto f1 = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    auto f2 = async(make_executor<0>(), []() -> double { throw test_exception(\"failure\"); });\n     auto f3 =\n-        async(custom_scheduler<0>(), []() -> std::string { throw test_exception(\"failure\"); });\n-    auto f4 = async(custom_scheduler<0>(),\n+        async(make_executor<0>(), []() -> std::string { throw test_exception(\"failure\"); });\n+    auto f4 = async(make_executor<0>(),\n                     []() -> std::vector<size_t> { throw test_exception(\"failure\"); });\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [](int x1, double x2, const std::string& x3, const std::vector<size_t>& x4) {\n                        std::stringstream st;\n                        st << x1 << \" \" << x2 << \" \" << x3 << \" \" << x4[0] << \" \" << x4[1];"},{"sha":"92cfc45c71e765ce76fb3f7497d1ca851f119c00","filename":"test/future_when_all_range_tests.cpp","status":"modified","additions":64,"deletions":64,"changes":128,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_when_all_range_tests.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_when_all_range_tests.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_when_all_range_tests.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_void_empty_range) {\n     bool check = {false};\n     std::vector<stlab::future<void>> emptyFutures;\n \n-    sut = when_all(custom_scheduler<0>(), [& _check = check]() { _check = true; },\n+    sut = when_all(make_executor<0>(), [& _check = check]() { _check = true; },\n                    std::make_pair(emptyFutures.begin(), emptyFutures.end()));\n \n     check_valid_future(sut);\n@@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_empty_range) {\n     size_t p = 0;\n     std::vector<stlab::future<int>> emptyFutures;\n \n-    sut = when_all(custom_scheduler<0>(), [& _p = p](std::vector<int> v) { _p = v.size(); },\n+    sut = when_all(make_executor<0>(), [& _p = p](std::vector<int> v) { _p = v.size(); },\n                    std::make_pair(emptyFutures.begin(), emptyFutures.end()));\n \n     check_valid_future(sut);\n@@ -54,9 +54,9 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_range_with_one_element) {\n     size_t p = 0;\n     size_t r = 0;\n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 42; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 42; }));\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p, &_r = r](std::vector<int> v) {\n                        _p = v.size();\n                        _r = v[0];\n@@ -77,12 +77,12 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_range_with_many_elements) {\n     size_t p = 0;\n     size_t r = 0;\n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 1; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 2; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 3; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 5; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 1; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 2; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 3; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 5; }));\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p, &_r = r](std::vector<int> v) {\n                        _p = v.size();\n                        for (auto i : v) {\n@@ -111,14 +111,14 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_range_with_diamond_formation_elements)\n     BOOST_TEST_MESSAGE(\"running future when_all void with range with diamond formation\");\n     int v[4] = {0, 0, 0, 0};\n     int r = 0;\n-    auto start = async(custom_scheduler<0>(), [] { return 4711; });\n+    auto start = async(make_executor<0>(), [] { return 4711; });\n     std::vector<stlab::future<void>> futures(4);\n-    futures[0] = start.then(custom_scheduler<0>(), [& _p = v[0]](auto x) { _p = x + 1; });\n-    futures[1] = start.then(custom_scheduler<0>(), [& _p = v[1]](auto x) { _p = x + 2; });\n-    futures[2] = start.then(custom_scheduler<0>(), [& _p = v[2]](auto x) { _p = x + 3; });\n-    futures[3] = start.then(custom_scheduler<0>(), [& _p = v[3]](auto x) { _p = x + 5; });\n+    futures[0] = start.then(make_executor<0>(), [& _p = v[0]](auto x) { _p = x + 1; });\n+    futures[1] = start.then(make_executor<0>(), [& _p = v[1]](auto x) { _p = x + 2; });\n+    futures[2] = start.then(make_executor<0>(), [& _p = v[2]](auto x) { _p = x + 3; });\n+    futures[3] = start.then(make_executor<0>(), [& _p = v[3]](auto x) { _p = x + 5; });\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _r = r, &v]() {\n                        for (auto i : v) {\n                            _r += i;\n@@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE(future_when_all_int_empty_range) {\n \n     std::vector<stlab::future<int>> emptyFutures;\n \n-    sut = when_all(custom_scheduler<0>(),\n+    sut = when_all(make_executor<0>(),\n                    [](std::vector<int> v) { return static_cast<int>(v.size()); },\n                    std::make_pair(emptyFutures.begin(), emptyFutures.end()));\n     check_valid_future(sut);\n@@ -156,9 +156,9 @@ BOOST_AUTO_TEST_CASE(future_when_all_int_range_with_one_element) {\n     BOOST_TEST_MESSAGE(\"running future when_all int with range of one element\");\n     size_t p = 0;\n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 42; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 42; }));\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p](std::vector<int> v) {\n                        _p = v.size();\n                        return v[0];\n@@ -178,12 +178,12 @@ BOOST_AUTO_TEST_CASE(future_when_all_int_range_with_many_elements) {\n     BOOST_TEST_MESSAGE(\"running future when_all int with range with many elements\");\n     size_t p = 0;\n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 1; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 2; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 3; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 5; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 1; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 2; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 3; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 5; }));\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p](std::vector<int> v) {\n                        _p = v.size();\n                        auto r = 0;\n@@ -213,14 +213,14 @@ start           sut\n BOOST_AUTO_TEST_CASE(future_when_all_int_range_with_diamond_formation_elements) {\n     BOOST_TEST_MESSAGE(\"running future when_all int with range with diamond formation\");\n     size_t p = 0;\n-    auto start = async(custom_scheduler<0>(), [] { return 4711; });\n+    auto start = async(make_executor<0>(), [] { return 4711; });\n     std::vector<stlab::future<int>> futures(4);\n-    futures[0] = start.then(custom_scheduler<0>(), [](auto x) { return x + 1; });\n-    futures[1] = start.then(custom_scheduler<0>(), [](auto x) { return x + 2; });\n-    futures[2] = start.then(custom_scheduler<0>(), [](auto x) { return x + 3; });\n-    futures[3] = start.then(custom_scheduler<0>(), [](auto x) { return x + 5; });\n+    futures[0] = start.then(make_executor<0>(), [](auto x) { return x + 1; });\n+    futures[1] = start.then(make_executor<0>(), [](auto x) { return x + 2; });\n+    futures[2] = start.then(make_executor<0>(), [](auto x) { return x + 3; });\n+    futures[3] = start.then(make_executor<0>(), [](auto x) { return x + 5; });\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p](std::vector<int> v) {\n                        _p = v.size();\n                        auto r = 0;\n@@ -250,12 +250,12 @@ BOOST_AUTO_TEST_CASE(future_when_all_move_range_with_many_elements) {\n     BOOST_TEST_MESSAGE(\"running future when_all move_only with range with many elements\");\n     size_t p = 0;\n     std::vector<stlab::future<stlab::move_only>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), [] { return stlab::move_only{1}; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return stlab::move_only{2}; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return stlab::move_only{3}; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return stlab::move_only{5}; }));\n+    futures.push_back(async(make_executor<0>(), [] { return stlab::move_only{1}; }));\n+    futures.push_back(async(make_executor<0>(), [] { return stlab::move_only{2}; }));\n+    futures.push_back(async(make_executor<0>(), [] { return stlab::move_only{3}; }));\n+    futures.push_back(async(make_executor<0>(), [] { return stlab::move_only{5}; }));\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p](std::vector<stlab::move_only> v) {\n                        _p = v.size();\n                        auto r = 0;\n@@ -287,9 +287,9 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_range_with_one_element) {\n     size_t r = 0;\n     std::vector<stlab::future<int>> futures;\n     futures.push_back(\n-        async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); }));\n+        async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); }));\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p, &_r = r](const std::vector<int>& v) {\n                        _p = v.size();\n                        _r = v[0];\n@@ -312,12 +312,12 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_range_with_many_elements_one_failing)\n     size_t r = 0;\n     std::vector<stlab::future<int>> futures;\n     futures.push_back(\n-        async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 2; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 3; }));\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 5; }));\n+        async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); }));\n+    futures.push_back(async(make_executor<0>(), [] { return 2; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 3; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 5; }));\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p, &_r = r](const std::vector<int>& v) {\n                        _p = v.size();\n                        for (auto i : v) {\n@@ -342,15 +342,15 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_range_with_many_elements_all_failing)\n     size_t r = 0;\n     std::vector<stlab::future<int>> futures;\n     futures.push_back(\n-        async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); }));\n+        async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); }));\n     futures.push_back(\n-        async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); }));\n+        async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); }));\n     futures.push_back(\n-        async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); }));\n+        async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); }));\n     futures.push_back(\n-        async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); }));\n+        async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); }));\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _p = p, &_r = r](const std::vector<int>& v) {\n                        _p = v.size();\n                        for (auto i : v) {\n@@ -380,14 +380,14 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_range_with_diamond_formation_elements_\n         \"running future when_all void with range with diamond formation and start failing\");\n     int v[4] = {0, 0, 0, 0};\n     int r = 0;\n-    auto start = async(custom_scheduler<0>(), []() -> int { throw test_exception(\"failure\"); });\n+    auto start = async(make_executor<0>(), []() -> int { throw test_exception(\"failure\"); });\n     std::vector<stlab::future<void>> futures(4);\n-    futures[0] = start.then(custom_scheduler<0>(), [& _p = v[0]](auto x) { _p = x + 1; });\n-    futures[1] = start.then(custom_scheduler<0>(), [& _p = v[1]](auto x) { _p = x + 2; });\n-    futures[2] = start.then(custom_scheduler<0>(), [& _p = v[2]](auto x) { _p = x + 3; });\n-    futures[3] = start.then(custom_scheduler<0>(), [& _p = v[3]](auto x) { _p = x + 5; });\n+    futures[0] = start.then(make_executor<0>(), [& _p = v[0]](auto x) { _p = x + 1; });\n+    futures[1] = start.then(make_executor<0>(), [& _p = v[1]](auto x) { _p = x + 2; });\n+    futures[2] = start.then(make_executor<0>(), [& _p = v[2]](auto x) { _p = x + 3; });\n+    futures[3] = start.then(make_executor<0>(), [& _p = v[3]](auto x) { _p = x + 5; });\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _r = r, &v]() {\n                        for (auto i : v) {\n                            _r += i;\n@@ -412,14 +412,14 @@ BOOST_AUTO_TEST_CASE(\n         \"running future when_all void with range with diamond formation and one of the parallel tasks is failing\");\n     int v[4] = {0, 0, 0, 0};\n     int r = 0;\n-    auto start = async(custom_scheduler<0>(), []() -> int { return 42; });\n+    auto start = async(make_executor<0>(), []() -> int { return 42; });\n     std::vector<stlab::future<void>> futures(4);\n-    futures[0] = start.then(custom_scheduler<0>(), [& _p = v[0]](auto x) { _p = x + 1; });\n-    futures[1] = start.then(custom_scheduler<0>(), [](auto) { throw test_exception(\"failure\"); });\n-    futures[2] = start.then(custom_scheduler<0>(), [& _p = v[2]](auto x) { _p = x + 3; });\n-    futures[3] = start.then(custom_scheduler<0>(), [& _p = v[3]](auto x) { _p = x + 5; });\n+    futures[0] = start.then(make_executor<0>(), [& _p = v[0]](auto x) { _p = x + 1; });\n+    futures[1] = start.then(make_executor<0>(), [](auto) { throw test_exception(\"failure\"); });\n+    futures[2] = start.then(make_executor<0>(), [& _p = v[2]](auto x) { _p = x + 3; });\n+    futures[3] = start.then(make_executor<0>(), [& _p = v[3]](auto x) { _p = x + 5; });\n \n-    sut = when_all(custom_scheduler<1>(),\n+    sut = when_all(make_executor<1>(),\n                    [& _r = r, &v]() {\n                        for (auto i : v) {\n                            _r += i;\n@@ -440,14 +440,14 @@ BOOST_AUTO_TEST_CASE(future_when_all_void_range_with_diamond_formation_elements_\n         \"running future when_all void with range with diamond formation and the joining tasks is failing\");\n     int v[4] = {0, 0, 0, 0};\n     int r = 0;\n-    auto start = async(custom_scheduler<0>(), []() -> int { return 42; });\n+    auto start = async(make_executor<0>(), []() -> int { return 42; });\n     std::vector<stlab::future<void>> futures(4);\n-    futures[0] = start.then(custom_scheduler<0>(), [& _p = v[0]](auto x) { _p = x + 1; });\n-    futures[1] = start.then(custom_scheduler<0>(), [& _p = v[1]](auto x) { _p = x + 2; });\n-    futures[2] = start.then(custom_scheduler<0>(), [& _p = v[2]](auto x) { _p = x + 3; });\n-    futures[3] = start.then(custom_scheduler<0>(), [& _p = v[3]](auto x) { _p = x + 5; });\n+    futures[0] = start.then(make_executor<0>(), [& _p = v[0]](auto x) { _p = x + 1; });\n+    futures[1] = start.then(make_executor<0>(), [& _p = v[1]](auto x) { _p = x + 2; });\n+    futures[2] = start.then(make_executor<0>(), [& _p = v[2]](auto x) { _p = x + 3; });\n+    futures[3] = start.then(make_executor<0>(), [& _p = v[3]](auto x) { _p = x + 5; });\n \n-    sut = when_all(custom_scheduler<1>(), []() { throw test_exception(\"failure\"); },\n+    sut = when_all(make_executor<1>(), []() { throw test_exception(\"failure\"); },\n                    std::make_pair(futures.begin(), futures.end()));\n \n     wait_until_future_fails<test_exception>(sut);"},{"sha":"a3057d6ee4483f7a23178a768be2e9e20b833eb2","filename":"test/future_when_any_arguments_tests.cpp","status":"modified","additions":72,"deletions":46,"changes":118,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_when_any_arguments_tests.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_when_any_arguments_tests.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_when_any_arguments_tests.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -11,7 +11,7 @@\n #include <stlab/concurrency/default_executor.hpp>\n #include <stlab/concurrency/future.hpp>\n #include <stlab/concurrency/utility.hpp>\n-\n+#include <stlab/test/model.hpp>\n #include <array>\n \n #include \"future_test_helper.hpp\"\n@@ -26,9 +26,9 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_range_with_one_argument) {\n     BOOST_TEST_MESSAGE(\"running future when_any int void with range of one argument\");\n     size_t index = 4711;\n     size_t result = 0;\n-    auto a1 = async(custom_scheduler<0>(), [] { return 42; });\n+    auto a1 = async(make_executor<0>(), [] { return 42; });\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _i = index, &_r = result](int x, size_t index) {\n                        _i = index;\n                        _r = x;\n@@ -53,22 +53,22 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_range_with_many_arguments_first_su\n     size_t used_future_index = 0;\n     size_t result = 0;\n \n-    auto a1 = async(custom_scheduler<0>(), make_non_blocking_functor(\n+    auto a1 = async(make_executor<0>(), make_non_blocking_functor(\n                                                [& _context = block_context]() {\n                                                    _context._may_proceed = true;\n                                                    return 1;\n                                                },\n                                                _task_counter));\n-    auto a2 = async(custom_scheduler<0>(),\n+    auto a2 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 2; }, _task_counter, block_context));\n-    auto a3 = async(custom_scheduler<0>(),\n+    auto a3 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 3; }, _task_counter, block_context));\n-    auto a4 = async(custom_scheduler<0>(),\n+    auto a4 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 5; }, _task_counter, block_context));\n     {\n         lock_t block(*block_context._mutex);\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _r = result, &_used_future_index = used_future_index,\n                         &_counter = any_task_execution_counter](int x, size_t index) {\n                            _used_future_index = index;\n@@ -101,23 +101,23 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_argument_with_many_arguments_middl\n     size_t used_future_index = 0;\n     size_t result = 0;\n \n-    auto a1 = async(custom_scheduler<0>(),\n+    auto a1 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 1; }, _task_counter, block_context));\n-    auto a2 = async(custom_scheduler<0>(),\n+    auto a2 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 2; }, _task_counter, block_context));\n-    auto a3 = async(custom_scheduler<0>(), make_non_blocking_functor(\n+    auto a3 = async(make_executor<0>(), make_non_blocking_functor(\n                                                [& _context = block_context] {\n                                                    _context._may_proceed = true;\n                                                    return 3;\n                                                },\n                                                _task_counter));\n-    auto a4 = async(custom_scheduler<0>(),\n+    auto a4 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 5; }, _task_counter, block_context));\n \n     {\n         lock_t lock(*block_context._mutex);\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _r = result, &_used_future_index = used_future_index,\n                         &_counter = any_task_execution_counter](int x, size_t index) {\n                            _used_future_index = index;\n@@ -150,13 +150,13 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_argument_with_many_arguments_last_\n     size_t used_future_index = 0;\n     size_t result = 0;\n \n-    auto a1 = async(custom_scheduler<0>(),\n+    auto a1 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 1; }, _task_counter, block_context));\n-    auto a2 = async(custom_scheduler<0>(),\n+    auto a2 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 2; }, _task_counter, block_context));\n-    auto a3 = async(custom_scheduler<0>(),\n+    auto a3 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 3; }, _task_counter, block_context));\n-    auto a4 = async(custom_scheduler<0>(), make_non_blocking_functor(\n+    auto a4 = async(make_executor<0>(), make_non_blocking_functor(\n                                                [& _context = block_context] {\n                                                    _context._may_proceed = true;\n                                                    return 5;\n@@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_argument_with_many_arguments_last_\n     {\n         lock_t lock(*block_context._mutex);\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _r = result, &_used_future_index = used_future_index,\n                         &_counter = any_task_execution_counter](int x, size_t index) {\n                            _used_future_index = index;\n@@ -198,13 +198,13 @@ BOOST_AUTO_TEST_CASE(\n     size_t index = 4711;\n     int result = 0;\n \n-    auto a1 = async(custom_scheduler<0>(), make_failing_functor([] { return 1; }, _task_counter));\n-    auto a2 = async(custom_scheduler<0>(), make_failing_functor([] { return 1; }, _task_counter));\n+    auto a1 = async(make_executor<0>(), make_failing_functor([] { return 1; }, _task_counter));\n+    auto a2 = async(make_executor<0>(), make_failing_functor([] { return 1; }, _task_counter));\n     auto a3 =\n-        async(custom_scheduler<0>(), make_non_blocking_functor([] { return 3; }, _task_counter));\n-    auto a4 = async(custom_scheduler<0>(), make_failing_functor([] { return 1; }, _task_counter));\n+        async(make_executor<0>(), make_non_blocking_functor([] { return 3; }, _task_counter));\n+    auto a4 = async(make_executor<0>(), make_failing_functor([] { return 1; }, _task_counter));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _i = index, &_result = result,\n                     &_counter = any_task_execution_counter](int x, size_t index) {\n                        ++_counter;\n@@ -230,12 +230,12 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_arguments_with_many_arguments_all_\n     size_t index = 4711;\n     int r = 0;\n \n-    auto a1 = async(custom_scheduler<0>(), make_failing_functor([] { return 0; }, _task_counter));\n-    auto a2 = async(custom_scheduler<0>(), make_failing_functor([] { return 0; }, _task_counter));\n-    auto a3 = async(custom_scheduler<0>(), make_failing_functor([] { return 0; }, _task_counter));\n-    auto a4 = async(custom_scheduler<0>(), make_failing_functor([] { return 0; }, _task_counter));\n+    auto a1 = async(make_executor<0>(), make_failing_functor([] { return 0; }, _task_counter));\n+    auto a2 = async(make_executor<0>(), make_failing_functor([] { return 0; }, _task_counter));\n+    auto a3 = async(make_executor<0>(), make_failing_functor([] { return 0; }, _task_counter));\n+    auto a4 = async(make_executor<0>(), make_failing_functor([] { return 0; }, _task_counter));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _i = index, &_r = r](int x, size_t index) {\n                        _i = index;\n                        _r = x;\n@@ -258,9 +258,9 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_int_argument_with_one_argument) {\n     BOOST_TEST_MESSAGE(\"running future when_any int int arguments of one argument\");\n     size_t index = 42;\n \n-    auto a1 = async(custom_scheduler<0>(), [] { return 4711; });\n+    auto a1 = async(make_executor<0>(), [] { return 4711; });\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _i = index](int x, size_t index) {\n                        _i = index;\n                        return x;\n@@ -285,13 +285,13 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_int_arguments_with_many_arguments_last_\n     std::atomic_int any_task_execution_counter{0};\n     size_t used_future_index = 0;\n \n-    auto a1 = async(custom_scheduler<0>(),\n+    auto a1 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 42; }, _task_counter, block_context));\n-    auto a2 = async(custom_scheduler<0>(),\n+    auto a2 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 815; }, _task_counter, block_context));\n-    auto a3 = async(custom_scheduler<0>(),\n+    auto a3 = async(make_executor<0>(),\n                     make_blocking_functor([] { return 4711; }, _task_counter, block_context));\n-    auto a4 = async(custom_scheduler<0>(), make_non_blocking_functor(\n+    auto a4 = async(make_executor<0>(), make_non_blocking_functor(\n                                                [& _context = block_context] {\n                                                    _context._may_proceed = true;\n                                                    return 5;\n@@ -300,7 +300,7 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_int_arguments_with_many_arguments_last_\n \n     {\n         lock_t lock(*block_context._mutex);\n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _used_future_index = used_future_index,\n                         &_counter = any_task_execution_counter](int x, size_t index) {\n                            _used_future_index = index;\n@@ -329,28 +329,28 @@ BOOST_AUTO_TEST_CASE(\n     size_t index = 0;\n     std::atomic_int failures{0};\n \n-    auto a1 = async(custom_scheduler<0>(), make_failing_functor(\n+    auto a1 = async(make_executor<0>(), make_failing_functor(\n                                                [& _f = failures]() -> int {\n                                                    ++_f;\n                                                    return 0;\n                                                },\n                                                _task_counter));\n-    auto a2 = async(custom_scheduler<0>(), make_failing_functor(\n+    auto a2 = async(make_executor<0>(), make_failing_functor(\n                                                [& _f = failures]() -> int {\n                                                    ++_f;\n                                                    return 0;\n                                                },\n                                                _task_counter));\n-    auto a3 = async(custom_scheduler<0>(),\n+    auto a3 = async(make_executor<0>(),\n                     make_non_blocking_functor([]() -> int { return 3; }, _task_counter));\n-    auto a4 = async(custom_scheduler<0>(), make_failing_functor(\n+    auto a4 = async(make_executor<0>(), make_failing_functor(\n                                                [& _f = failures]() -> int {\n                                                    ++_f;\n                                                    return 0;\n                                                },\n                                                _task_counter));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _index = index](int x, size_t index) {\n                        _index = index;\n                        return x;\n@@ -383,25 +383,25 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_arguments_with_diamond_formation_argume\n     size_t index = 0;\n     {\n         lock_t lock(*block_context._mutex);\n-        auto start = async(custom_scheduler<0>(), [] { return 4711; });\n+        auto start = async(make_executor<0>(), [] { return 4711; });\n \n         auto a1 =\n-            start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return x + 1; },\n+            start.then(make_executor<0>(), make_blocking_functor([](int x) { return x + 1; },\n                                                                     _task_counter, block_context));\n-        auto a2 = start.then(custom_scheduler<0>(), make_non_blocking_functor(\n+        auto a2 = start.then(make_executor<0>(), make_non_blocking_functor(\n                                                         [& _context = block_context](auto x) {\n                                                             _context._may_proceed = true;\n                                                             return x + 2;\n                                                         },\n                                                         _task_counter));\n         auto a3 =\n-            start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return x + 3; },\n+            start.then(make_executor<0>(), make_blocking_functor([](int x) { return x + 3; },\n                                                                     _task_counter, block_context));\n         auto a4 =\n-            start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return x + 5; },\n+            start.then(make_executor<0>(), make_blocking_functor([](int x) { return x + 5; },\n                                                                     _task_counter, block_context));\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _i = index](int x, size_t index) {\n                            _i = index;\n                            return x;\n@@ -423,3 +423,29 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_arguments_with_diamond_formation_argume\n }\n \n BOOST_AUTO_TEST_SUITE_END()\n+\n+BOOST_FIXTURE_TEST_SUITE(future_when_any_argument_move_only, test_fixture<move_only>)\n+BOOST_AUTO_TEST_CASE(future_when_any_move_only_argument_with_one_argument) {\n+  BOOST_TEST_MESSAGE(\"running future when_any move_only arguments of one argument\");\n+  size_t index = 42;\n+\n+  auto a1 = async(make_executor<0>(), [] { return move_only(4711); });\n+\n+  sut = when_any(make_executor<1>(),\n+    [&_i = index](move_only(x), size_t index) {\n+    _i = index;\n+    return x;\n+  },\n+    std::move(a1));\n+\n+  check_valid_future(sut);\n+\n+  wait_until_future_completed(sut);\n+\n+  BOOST_REQUIRE_EQUAL(size_t(0), index);\n+  BOOST_REQUIRE_EQUAL(4711, (*sut.get_try()).member());\n+  BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());\n+  BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());\n+}\n+\n+BOOST_AUTO_TEST_SUITE_END()\n\\ No newline at end of file"},{"sha":"4c712def60261da4bd6ee246b454b721fa9daa02","filename":"test/future_when_any_range_tests.cpp","status":"modified","additions":62,"deletions":62,"changes":124,"blob_url":"https://github.com/stlab/stlab/blob/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_when_any_range_tests.cpp","raw_url":"https://github.com/stlab/stlab/raw/fa815b4531904cb9a68b11a0026964e0b197c9de/test%2Ffuture_when_any_range_tests.cpp","contents_url":"https://api.github.com/repos/stlab/stlab/contents/test%2Ffuture_when_any_range_tests.cpp?ref=fa815b4531904cb9a68b11a0026964e0b197c9de","patch":"@@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(future_when_any_void_void_empty_range) {\n     bool check{false};\n     std::vector<stlab::future<void>> emptyFutures;\n \n-    sut = when_any(custom_scheduler<0>(), [& _check = check](size_t) { _check = true; },\n+    sut = when_any(make_executor<0>(), [& _check = check](size_t) { _check = true; },\n                    std::make_pair(emptyFutures.begin(), emptyFutures.end()));\n \n     wait_until_future_fails<stlab::future_error>(sut);\n@@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_empty_range) {\n     bool check{false};\n     std::vector<stlab::future<int>> emptyFutures;\n \n-    sut = when_any(custom_scheduler<0>(), [& _check = check](int, size_t) { _check = true; },\n+    sut = when_any(make_executor<0>(), [& _check = check](int, size_t) { _check = true; },\n                    std::make_pair(emptyFutures.begin(), emptyFutures.end()));\n \n     wait_until_future_fails<stlab::future_error>(sut);\n@@ -57,10 +57,10 @@ BOOST_AUTO_TEST_CASE(future_when_any_void_void_range_with_one_element) {\n     int interim_result = 0;\n     int result = 0;\n     std::vector<stlab::future<void>> futures;\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             [& _interim_result = interim_result] { _interim_result = 42; }));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _result = result, &_interim_result = interim_result](size_t) {\n                        _result = _interim_result;\n                    },\n@@ -78,9 +78,9 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_range_with_one_element) {\n     size_t index = 4711;\n     size_t result = 0;\n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 42; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 42; }));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _i = index, &_r = result](int x, size_t index) {\n                        _i = index;\n                        _r = x;\n@@ -106,22 +106,22 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_range_with_many_elements_first_suc\n     size_t result = 0;\n \n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), make_non_blocking_functor(\n+    futures.push_back(async(make_executor<0>(), make_non_blocking_functor(\n                                                        [& _context = block_context]() {\n                                                            _context._may_proceed = true;\n                                                            return 1;\n                                                        },\n                                                        _task_counter)));\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 2; }, _task_counter, block_context)));\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 3; }, _task_counter, block_context)));\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 5; }, _task_counter, block_context)));\n     {\n         lock_t block(*block_context._mutex);\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _r = result, &_used_future_index = used_future_index,\n                         &_counter = any_task_execution_counter](int x, size_t index) {\n                            _used_future_index = index;\n@@ -154,23 +154,23 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_range_with_many_elements_middle_su\n     size_t result = 0;\n \n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 1; }, _task_counter, block_context)));\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 2; }, _task_counter, block_context)));\n-    futures.push_back(async(custom_scheduler<0>(), make_non_blocking_functor(\n+    futures.push_back(async(make_executor<0>(), make_non_blocking_functor(\n                                                        [& _context = block_context] {\n                                                            _context._may_proceed = true;\n                                                            return 3;\n                                                        },\n                                                        _task_counter)));\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 5; }, _task_counter, block_context)));\n \n     {\n         lock_t lock(*block_context._mutex);\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _r = result, &_used_future_index = used_future_index,\n                         &_counter = any_task_execution_counter](int x, size_t index) {\n                            _used_future_index = index;\n@@ -204,13 +204,13 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_range_with_many_elements_last_succ\n     size_t result = 0;\n \n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 1; }, _task_counter, block_context)));\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 2; }, _task_counter, block_context)));\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_blocking_functor([] { return 3; }, _task_counter, block_context)));\n-    futures.push_back(async(custom_scheduler<0>(), make_non_blocking_functor(\n+    futures.push_back(async(make_executor<0>(), make_non_blocking_functor(\n                                                        [& _context = block_context] {\n                                                            _context._may_proceed = true;\n                                                            return 5;\n@@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_range_with_many_elements_last_succ\n     {\n         lock_t lock(*block_context._mutex);\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _r = result, &_used_future_index = used_future_index,\n                         &_counter = any_task_execution_counter](int x, size_t index) {\n                            _used_future_index = index;\n@@ -254,15 +254,15 @@ BOOST_AUTO_TEST_CASE(\n \n     std::vector<stlab::future<int>> futures;\n     futures.push_back(\n-        async(custom_scheduler<0>(), make_failing_functor([] { return 1; }, _task_counter)));\n+        async(make_executor<0>(), make_failing_functor([] { return 1; }, _task_counter)));\n     futures.push_back(\n-        async(custom_scheduler<0>(), make_failing_functor([] { return 1; }, _task_counter)));\n+        async(make_executor<0>(), make_failing_functor([] { return 1; }, _task_counter)));\n     futures.push_back(\n-        async(custom_scheduler<0>(), make_non_blocking_functor([] { return 3; }, _task_counter)));\n+        async(make_executor<0>(), make_non_blocking_functor([] { return 3; }, _task_counter)));\n     futures.push_back(\n-        async(custom_scheduler<0>(), make_failing_functor([] { return 1; }, _task_counter)));\n+        async(make_executor<0>(), make_failing_functor([] { return 1; }, _task_counter)));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _i = index, &_result = result,\n                     &_counter = any_task_execution_counter](int x, size_t index) {\n                        ++_counter;\n@@ -290,15 +290,15 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_void_range_with_many_elements_all_fails\n \n     std::vector<stlab::future<int>> futures;\n     futures.push_back(\n-        async(custom_scheduler<0>(), make_failing_functor([] { return 0; }, _task_counter)));\n+        async(make_executor<0>(), make_failing_functor([] { return 0; }, _task_counter)));\n     futures.push_back(\n-        async(custom_scheduler<0>(), make_failing_functor([] { return 0; }, _task_counter)));\n+        async(make_executor<0>(), make_failing_functor([] { return 0; }, _task_counter)));\n     futures.push_back(\n-        async(custom_scheduler<0>(), make_failing_functor([] { return 0; }, _task_counter)));\n+        async(make_executor<0>(), make_failing_functor([] { return 0; }, _task_counter)));\n     futures.push_back(\n-        async(custom_scheduler<0>(), make_failing_functor([] { return 0; }, _task_counter)));\n+        async(make_executor<0>(), make_failing_functor([] { return 0; }, _task_counter)));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _i = index, &_r = r](int x, size_t index) {\n                        _i = index;\n                        _r = x;\n@@ -332,30 +332,30 @@ BOOST_AUTO_TEST_CASE(future_when_any_void_range_with_diamond_formation_elements)\n \n     {\n         lock_t block(*block_context._mutex);\n-        auto start = async(custom_scheduler<0>(), [] { return 4711; });\n+        auto start = async(make_executor<0>(), [] { return 4711; });\n         std::vector<future<void>> futures;\n         futures.push_back(start.then(\n-            custom_scheduler<0>(),\n+            make_executor<0>(),\n             make_blocking_functor([& _result = intrim_results](auto x) { _result[0] = x + 1; },\n                                   _task_counter, block_context)));\n         futures.push_back(start.then(\n-            custom_scheduler<0>(),\n+            make_executor<0>(),\n             make_blocking_functor([& _result = intrim_results](auto x) { _result[1] = x + 2; },\n                                   _task_counter, block_context)));\n         futures.push_back(start.then(\n-            custom_scheduler<0>(),\n+            make_executor<0>(),\n             make_blocking_functor([& _result = intrim_results](auto x) { _result[2] = x + 3; },\n                                   _task_counter, block_context)));\n         futures.push_back(\n-            start.then(custom_scheduler<0>(),\n+            start.then(make_executor<0>(),\n                        make_non_blocking_functor(\n                            [& _context = block_context, &_result = intrim_results](auto x) {\n                                _result[3] = x;\n                                _context._may_proceed = true;\n                            },\n                            _task_counter)));\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _result = result, &_counter = any_task_execution_counter,\n                         &_interim_results = intrim_results](size_t index) {\n                            _result = _interim_results[index];\n@@ -383,7 +383,7 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_int_empty_range) {\n \n     bool check{false};\n     std::vector<stlab::future<int>> emptyFutures;\n-    sut = when_any(custom_scheduler<0>(),\n+    sut = when_any(make_executor<0>(),\n                    [& _check = check](int x, size_t) {\n                        _check = true;\n                        return x + 42;\n@@ -401,9 +401,9 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_int_range_with_one_element) {\n     size_t index = 42;\n \n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), [] { return 4711; }));\n+    futures.push_back(async(make_executor<0>(), [] { return 4711; }));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _i = index](int x, size_t index) {\n                        _i = index;\n                        return x;\n@@ -431,15 +431,15 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_int_range_with_many_elements) {\n \n     std::vector<stlab::future<int>> futures;\n     futures.push_back(\n-        async(custom_scheduler<0>(),\n+        async(make_executor<0>(),\n               make_blocking_functor([] { return 42; }, _task_counter, block_context)));\n     futures.push_back(\n-        async(custom_scheduler<0>(),\n+        async(make_executor<0>(),\n               make_blocking_functor([] { return 815; }, _task_counter, block_context)));\n     futures.push_back(\n-        async(custom_scheduler<0>(),\n+        async(make_executor<0>(),\n               make_blocking_functor([] { return 4711; }, _task_counter, block_context)));\n-    futures.push_back(async(custom_scheduler<0>(), make_non_blocking_functor(\n+    futures.push_back(async(make_executor<0>(), make_non_blocking_functor(\n                                                        [& _context = block_context] {\n                                                            _context._may_proceed = true;\n                                                            return 5;\n@@ -448,7 +448,7 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_int_range_with_many_elements) {\n \n     {\n         lock_t lock(*block_context._mutex);\n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _used_future_index = used_future_index,\n                         &_counter = any_task_execution_counter](int x, size_t index) {\n                            _used_future_index = index;\n@@ -476,28 +476,28 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_int_range_with_many_elements_all_but_al\n     size_t index = 0;\n     std::atomic_int failures{0};\n     std::vector<stlab::future<int>> futures;\n-    futures.push_back(async(custom_scheduler<0>(), make_failing_functor(\n+    futures.push_back(async(make_executor<0>(), make_failing_functor(\n                                                        [& _f = failures]() -> int {\n                                                            ++_f;\n                                                            return 0;\n                                                        },\n                                                        _task_counter)));\n-    futures.push_back(async(custom_scheduler<0>(), make_failing_functor(\n+    futures.push_back(async(make_executor<0>(), make_failing_functor(\n                                                        [& _f = failures]() -> int {\n                                                            ++_f;\n                                                            return 0;\n                                                        },\n                                                        _task_counter)));\n-    futures.push_back(async(custom_scheduler<0>(),\n+    futures.push_back(async(make_executor<0>(),\n                             make_non_blocking_functor([]() -> int { return 3; }, _task_counter)));\n-    futures.push_back(async(custom_scheduler<0>(), make_failing_functor(\n+    futures.push_back(async(make_executor<0>(), make_failing_functor(\n                                                        [& _f = failures]() -> int {\n                                                            ++_f;\n                                                            return 0;\n                                                        },\n                                                        _task_counter)));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n                    [& _index = index](int x, size_t index) {\n                        _index = index;\n                        return x;\n@@ -530,26 +530,26 @@ BOOST_AUTO_TEST_CASE(future_when_any_int_range_with_diamond_formation_elements)\n     size_t index = 0;\n     {\n         lock_t lock(*block_context._mutex);\n-        auto start = async(custom_scheduler<0>(), [] { return 4711; });\n+        auto start = async(make_executor<0>(), [] { return 4711; });\n         std::vector<stlab::future<int>> futures;\n         futures.push_back(\n-            start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return x + 1; },\n+            start.then(make_executor<0>(), make_blocking_functor([](int x) { return x + 1; },\n                                                                     _task_counter, block_context)));\n         futures.push_back(\n-            start.then(custom_scheduler<0>(), make_non_blocking_functor(\n+            start.then(make_executor<0>(), make_non_blocking_functor(\n                                                   [& _context = block_context](auto x) {\n                                                       _context._may_proceed = true;\n                                                       return x + 2;\n                                                   },\n                                                   _task_counter)));\n         futures.push_back(\n-            start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return x + 3; },\n+            start.then(make_executor<0>(), make_blocking_functor([](int x) { return x + 3; },\n                                                                     _task_counter, block_context)));\n         futures.push_back(\n-            start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return x + 5; },\n+            start.then(make_executor<0>(), make_blocking_functor([](int x) { return x + 5; },\n                                                                     _task_counter, block_context)));\n \n-        sut = when_any(custom_scheduler<1>(),\n+        sut = when_any(make_executor<1>(),\n                        [& _i = index](int x, size_t index) {\n                            _i = index;\n                            return x;\n@@ -583,26 +583,26 @@ BOOST_AUTO_TEST_CASE(future_when_any_move_only_range_with_diamond_formation_elem\n   size_t index = 0;\n   {\n     lock_t lock(*block_context._mutex);\n-    auto start = async(custom_scheduler<0>(), [] { return 4711; });\n+    auto start = async(make_executor<0>(), [] { return 4711; });\n     std::vector<stlab::future<stlab::move_only>> futures;\n     futures.push_back(\n-      start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return stlab::move_only{ x + 1 }; },\n+      start.then(make_executor<0>(), make_blocking_functor([](int x) { return stlab::move_only{ x + 1 }; },\n         _task_counter, block_context)));\n     futures.push_back(\n-      start.then(custom_scheduler<0>(), make_non_blocking_functor(\n+      start.then(make_executor<0>(), make_non_blocking_functor(\n         [&_context = block_context](auto x) {\n       _context._may_proceed = true;\n       return stlab::move_only{ x + 2 };\n     },\n         _task_counter)));\n     futures.push_back(\n-      start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return stlab::move_only{ x + 3 }; },\n+      start.then(make_executor<0>(), make_blocking_functor([](int x) { return stlab::move_only{ x + 3 }; },\n         _task_counter, block_context)));\n     futures.push_back(\n-      start.then(custom_scheduler<0>(), make_blocking_functor([](int x) { return stlab::move_only{ x + 5 }; },\n+      start.then(make_executor<0>(), make_blocking_functor([](int x) { return stlab::move_only{ x + 5 }; },\n         _task_counter, block_context)));\n \n-    sut = when_any(custom_scheduler<1>(),\n+    sut = when_any(make_executor<1>(),\n       [&_i = index](stlab::move_only x, size_t index) {\n       _i = index;\n       return x;"}]}