├── .clang-format ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── TARGETS ├── benchmarks ├── CMakeLists.txt ├── PushmiBenchmarks.cpp ├── report.html └── runner.cpp ├── buildSingleHeader.cmake ├── cmake └── CMakeLists.txt.in ├── examples ├── CMakeLists.txt ├── bulk.h ├── composition │ ├── CMakeLists.txt │ ├── composition_2.cpp │ ├── composition_3.cpp │ └── composition_4.cpp ├── for_each.h ├── for_each │ ├── CMakeLists.txt │ ├── for_each_2.cpp │ └── for_each_3.cpp ├── no_fail.h ├── pool.h ├── readme.md ├── reduce.h ├── reduce │ ├── CMakeLists.txt │ ├── reduce_2.cpp │ └── reduce_3.cpp ├── set_done │ ├── CMakeLists.txt │ └── set_done_2.cpp ├── set_error │ ├── CMakeLists.txt │ └── set_error_2.cpp ├── then_execute │ ├── CMakeLists.txt │ └── then_execute_2.cpp └── twoway_execute │ ├── CMakeLists.txt │ └── twoway_execute_2.cpp ├── external └── nonius │ └── include │ └── nonius │ └── nonius.h++ ├── include ├── pushmi-single-footer.h ├── pushmi-single-header.h ├── pushmi.h └── pushmi │ ├── boosters.h │ ├── concepts.h │ ├── detail │ ├── concept_def.h │ ├── functional.h │ ├── if_constexpr.h │ └── opt.h │ ├── entangle.h │ ├── executor.h │ ├── extension_points.h │ ├── flow_many_sender.h │ ├── flow_receiver.h │ ├── flow_single_sender.h │ ├── forwards.h │ ├── inline.h │ ├── many_sender.h │ ├── new_thread.h │ ├── o │ ├── defer.h │ ├── empty.h │ ├── error.h │ ├── extension_operators.h │ ├── filter.h │ ├── for_each.h │ ├── from.h │ ├── just.h │ ├── on.h │ ├── request_via.h │ ├── schedule.h │ ├── share.h │ ├── submit.h │ ├── switch_on_error.h │ ├── tap.h │ ├── transform.h │ └── via.h │ ├── piping.h │ ├── properties.h │ ├── receiver.h │ ├── single_sender.h │ ├── strand.h │ ├── subject.h │ ├── time_source.h │ ├── traits.h │ └── trampoline.h ├── install_libcxx.sh └── test ├── CMakeLists.txt ├── CompileTest.cpp ├── FlowManyTest.cpp ├── FlowTest.cpp ├── NewThreadTest.cpp ├── PushmiTest.cpp └── TrampolineTest.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | DisableFormat: true 3 | SortIncludes: false 4 | ... 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | include/pushmi.h -diff -merge 2 | include/pushmi.h linguist-generated=true 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | build/** -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/networking-ts-impl"] 2 | path = external/networking-ts-impl 3 | url = https://github.com/chriskohlhoff/networking-ts-impl.git 4 | [submodule "external/Catch2"] 5 | path = external/Catch2 6 | url = https://github.com/catchorg/Catch2.git 7 | [submodule "external/executors-impl"] 8 | path = external/executors-impl 9 | url = https://github.com/executors/executors-impl.git 10 | [submodule "external/futures-impl"] 11 | path = external/futures-impl 12 | url = https://github.com/executors/futures-impl.git 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright Louis Dionne 2013-2016 2 | # Copyright Gonzalo BG 2014-2017 3 | # Copyright Julian Becker 2015 4 | # Copyright Manu Sánchez 2015 5 | # Copyright Casey Carter 2015-2017 6 | # Copyright Eric Niebler 2015-2016 7 | # Copyright Paul Fultz II 2015-2016 8 | # Copyright Jakub Szuppe 2016. 9 | 10 | # Distributed under the Boost Software License, Version 1.0. 11 | # (See accompanying file LICENSE.txt or copy at http://boost.org/LICENSE_1_0.txt) 12 | 13 | # Adapted from various sources, including: 14 | # - Louis Dionne's Hana: https://github.com/ldionne/hana 15 | # - Paul Fultz II's FIT: https://github.com/pfultz2/Fit 16 | language: cpp 17 | script: cmake 18 | 19 | env: 20 | global: 21 | - DEPS_DIR=${TRAVIS_BUILD_DIR}/deps 22 | - CMAKE_VERSION="3.12.0" 23 | 24 | cache: 25 | directories: 26 | - ${DEPS_DIR}/cmake-${CMAKE_VERSION} 27 | 28 | matrix: 29 | include: 30 | # - env: BUILD_TYPE=Release CPP=14 SYSTEM_LIBCXX=On 31 | # os: osx 32 | # compiler: clang 33 | 34 | - os: osx 35 | osx_image: xcode10.1 36 | env: 37 | - GCC_VERSION=8 38 | - BUILD_TYPE=Debug 39 | - CPP=17 40 | - BREW_INSTALL_CXX="brew install gcc" 41 | 42 | - os: osx 43 | osx_image: xcode10.1 44 | env: 45 | - GCC_VERSION=8 46 | - BUILD_TYPE=Release 47 | - CPP=17 48 | - BREW_INSTALL_CXX="brew install gcc" 49 | 50 | # clang 5 C++11/14/1z Debug/Release libc++, 11 Debug libstdc++ 51 | - env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=14 LIBCXX=On 52 | os: linux 53 | addons: &clang6 54 | apt: 55 | packages: 56 | - clang-6.0 57 | sources: 58 | - llvm-toolchain-trusty-6.0 59 | - ubuntu-toolchain-r-test 60 | 61 | - env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=14 LIBCXX=On 62 | os: linux 63 | addons: *clang6 64 | 65 | - env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=17 LIBCXX=On 66 | os: linux 67 | addons: *clang6 68 | 69 | - env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=17 LIBCXX=On 70 | os: linux 71 | addons: *clang6 72 | 73 | # gcc-6 C++11/14/1z Debug/Release 74 | - env: GCC_VERSION=6 BUILD_TYPE=Debug CPP=14 75 | os: linux 76 | addons: &gcc6 77 | apt: 78 | packages: 79 | - g++-6 80 | sources: 81 | - ubuntu-toolchain-r-test 82 | 83 | - env: GCC_VERSION=6 BUILD_TYPE=Release CPP=14 84 | os: linux 85 | addons: *gcc6 86 | 87 | - env: GCC_VERSION=6 BUILD_TYPE=Debug CPP=17 88 | os: linux 89 | addons: *gcc6 90 | 91 | - env: GCC_VERSION=6 BUILD_TYPE=Release CPP=17 92 | os: linux 93 | addons: *gcc6 94 | 95 | - env: GCC_VERSION=6 BUILD_TYPE=Debug CPP=17 CONCEPTS=On 96 | os: linux 97 | addons: *gcc6 98 | 99 | - env: GCC_VERSION=6 BUILD_TYPE=Release CPP=17 CONCEPTS=On 100 | os: linux 101 | addons: *gcc6 102 | 103 | # gcc-7 C++11/14/1z Debug/Release 104 | - env: GCC_VERSION=7 BUILD_TYPE=Debug CPP=14 105 | os: linux 106 | addons: &gcc7 107 | apt: 108 | packages: 109 | - g++-7 110 | sources: 111 | - ubuntu-toolchain-r-test 112 | 113 | - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=14 114 | os: linux 115 | addons: *gcc7 116 | 117 | - env: GCC_VERSION=7 BUILD_TYPE=Debug CPP=17 118 | os: linux 119 | addons: *gcc7 120 | 121 | - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=17 122 | os: linux 123 | addons: *gcc7 124 | 125 | - env: GCC_VERSION=7 BUILD_TYPE=Debug CPP=17 CONCEPTS=On 126 | os: linux 127 | addons: *gcc7 128 | 129 | - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=17 CONCEPTS=On 130 | os: linux 131 | addons: *gcc7 132 | 133 | # gcc-8 C++11/14/1z Debug/Release 134 | - env: GCC_VERSION=8 BUILD_TYPE=Debug CPP=14 135 | os: linux 136 | addons: &gcc8 137 | apt: 138 | packages: 139 | - g++-8 140 | sources: 141 | - ubuntu-toolchain-r-test 142 | 143 | - env: GCC_VERSION=8 BUILD_TYPE=Release CPP=14 144 | os: linux 145 | addons: *gcc8 146 | 147 | - env: GCC_VERSION=8 BUILD_TYPE=Debug CPP=17 148 | os: linux 149 | addons: *gcc8 150 | 151 | - env: GCC_VERSION=8 BUILD_TYPE=Release CPP=17 152 | os: linux 153 | addons: *gcc8 154 | 155 | - env: GCC_VERSION=8 BUILD_TYPE=Debug CPP=17 CONCEPTS=On 156 | os: linux 157 | addons: *gcc8 158 | 159 | - env: GCC_VERSION=8 BUILD_TYPE=Release CPP=17 CONCEPTS=On 160 | os: linux 161 | addons: *gcc8 162 | 163 | # Install dependencies 164 | before_install: 165 | - export CHECKOUT_PATH=`pwd`; 166 | - | 167 | if [ "$TRAVIS_OS_NAME" == "osx" ]; then 168 | brew update 169 | brew install gnu-sed 170 | brew install gnu-which 171 | eval "${BREW_INSTALL_CXX}" 172 | fi 173 | - | 174 | if [ "${TRAVIS_OS_NAME}" == "linux" ]; then 175 | if [ -z "$(ls -A ${DEPS_DIR}/cmake-${CMAKE_VERSION}/cached)" ]; then 176 | CMAKE_URL="https://cmake.org/files/v3.12/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz" 177 | mkdir -p ${DEPS_DIR}/cmake-${CMAKE_VERSION} 178 | travis_retry wget --no-check-certificate --quiet -O - "${CMAKE_URL}" | tar --strip-components=1 -xz -C ${DEPS_DIR}/cmake-${CMAKE_VERSION} 179 | touch ${DEPS_DIR}/cmake-${CMAKE_VERSION}/cached 180 | else 181 | echo "Using cached cmake version ${CMAKE_VERSION}." 182 | fi 183 | export PATH="${DEPS_DIR}/cmake-${CMAKE_VERSION}/bin:${PATH}" 184 | else 185 | if ! brew ls --version cmake &>/dev/null; then brew install cmake; fi 186 | fi 187 | - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi 188 | - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi 189 | - which $CXX 190 | - which $CC 191 | - $CXX --version 192 | - if [ "$ASAN" == "On" ]; then export SANITIZER="Address;Undefined"; fi 193 | - if [ "$MSAN" == "On" ]; then export SANITIZER="MemoryWithOrigins"; fi 194 | - if [ -n "$CLANG_VERSION" ]; then sudo PATH="${PATH}" CXX="$CXX" CC="$CC" ./install_libcxx.sh; fi 195 | 196 | install: 197 | - cd $CHECKOUT_PATH 198 | - mkdir -p build 199 | - cd build 200 | - | 201 | if [ "$LIBCXX" == "On" ]; then 202 | CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -nostdinc++ -cxx-isystem /usr/include/c++/v1/ -Wno-unused-command-line-argument" 203 | CXX_LINKER_FLAGS="${CXX_LINKER_FLAGS} -lc++abi" 204 | fi 205 | # Required to test the C++ compiler since libc++ is compiled with ASan enabled: 206 | - if [ -n "$CLANG_VERSION" -a "$ASAN" == "On" -a "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -fsanitize=address"; fi 207 | # Required to test the C++ compiler since libc++ is compiled with MSan enabled: 208 | - if [ -n "$CLANG_VERSION" -a "$MSAN" == "On" -a "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -fsanitize=memory"; fi 209 | - if [ -n "$GCC_VERSION" -a "$CONCEPTS" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -fconcepts"; fi 210 | - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" -DCMAKE_EXE_LINKER_FLAGS="${CXX_LINKER_FLAGS}" -DCMAKE_CXX_STANDARD=$CPP -DPUSHMI_CONCEPTS=$CONCEPTS -Wdev 211 | - make VERBOSE=1 212 | 213 | script: 214 | - ctest -VV ${CTEST_FLAGS} 215 | 216 | notifications: 217 | email: false 218 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-present, Facebook, Inc. 2 | # 3 | # This source code is licensed under the Apache License found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | cmake_minimum_required(VERSION 3.7) 7 | 8 | project(pushmi-project CXX) 9 | 10 | option(PUSHMI_USE_CONCEPTS_EMULATION "Use C++14 Concepts Emulation" ON) 11 | option(PUSHMI_USE_CPP_2A "Use C++2a with concepts emulation" OFF) 12 | option(PUSHMI_USE_CPP_17 "Use C++17 with concepts emulation" OFF) 13 | option(PUSHMI_ONE_TEST_BINARY "Compile all the tests into one binary" OFF) 14 | 15 | FIND_PACKAGE (Threads REQUIRED) 16 | 17 | add_library(pushmi INTERFACE) 18 | 19 | target_include_directories(pushmi INTERFACE 20 | $ 21 | $ 22 | $ 23 | $ 24 | $ 25 | $/include> 26 | $/include/executors> 27 | $/include/futures> 28 | $/include/net> 29 | $/include/nonius> 30 | ) 31 | 32 | if (PUSHMI_USE_CONCEPTS_EMULATION) 33 | 34 | message("Using concepts emulation!") 35 | 36 | if(${CMAKE_VERSION} VERSION_LESS "3.8.0" OR PUSHMI_USE_CPP_17 OR PUSHMI_USE_CPP_2A) 37 | 38 | if (PUSHMI_USE_CPP_17) 39 | message("Using c++17!") 40 | target_compile_options(pushmi INTERFACE 41 | $<$:-std=c++17> 42 | $<$:-std=c++17> 43 | $<$:-std=c++17> 44 | ) 45 | elseif (PUSHMI_USE_CPP_2A) 46 | message("Using c++2a!") 47 | target_compile_options(pushmi INTERFACE 48 | $<$:-std=c++2a> 49 | $<$:-std=c++2a> 50 | $<$:-std=c++2a> 51 | ) 52 | else() 53 | message("Using c++14!") 54 | target_compile_options(pushmi INTERFACE 55 | $<$:-std=c++14> 56 | $<$:-std=c++14> 57 | $<$:-std=c++14> 58 | ) 59 | endif() 60 | 61 | else() 62 | 63 | target_compile_features(pushmi INTERFACE cxx_std_14) 64 | 65 | endif() 66 | 67 | else(PUSHMI_USE_CONCEPTS_EMULATION) 68 | 69 | message("Using real concepts!") 70 | message("Using c++2a!") 71 | 72 | target_compile_options(pushmi INTERFACE 73 | $<$:-std=c++2a> 74 | $<$:-fconcepts>) 75 | 76 | endif(PUSHMI_USE_CONCEPTS_EMULATION) 77 | 78 | target_compile_options(pushmi INTERFACE 79 | $<$:-ftemplate-backtrace-limit=0> 80 | $<$:-ftemplate-backtrace-limit=0> 81 | $<$:-ftemplate-backtrace-limit=0>) 82 | 83 | target_compile_options(pushmi INTERFACE 84 | $<$:-stdlib=libc++> 85 | $<$:-stdlib=libc++>) 86 | 87 | # target_compile_options(pushmi INTERFACE 88 | # $<$:-fsanitize=thread -fno-omit-frame-pointer>) 89 | # target_link_options(pushmi INTERFACE 90 | # $<$:-fsanitize=thread>) 91 | 92 | if (PUSHMI_CONCEPTS) 93 | target_compile_options(pushmi INTERFACE $<$:-fconcepts>) 94 | endif () 95 | 96 | add_custom_target(buildSingleHeader 97 | COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/buildSingleHeader.cmake 98 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 99 | add_dependencies(pushmi buildSingleHeader) 100 | 101 | install( 102 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ 103 | DESTINATION include) 104 | install( 105 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external/executors-impl/include/ 106 | DESTINATION include/executors) 107 | install( 108 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external/futures-impl/future-executor-interaction/include/ 109 | DESTINATION include/futures) 110 | install( 111 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external/networking-ts-impl/include/ 112 | DESTINATION include/net) 113 | install( 114 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external/Catch2/ 115 | DESTINATION include/Catch2) 116 | install( 117 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external/nonius/include/ 118 | DESTINATION include/nonius) 119 | install(TARGETS pushmi EXPORT pushmi-project) 120 | install(EXPORT pushmi-project DESTINATION pushmi-project) 121 | 122 | add_subdirectory(examples) 123 | add_subdirectory(benchmarks) 124 | 125 | enable_testing() 126 | 127 | # Download and unpack googletest at configure time 128 | configure_file(cmake/CMakeLists.txt.in googletest-download/CMakeLists.txt) 129 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 130 | RESULT_VARIABLE result 131 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) 132 | if(result) 133 | message(FATAL_ERROR "CMake step for googletest failed: ${result}") 134 | endif() 135 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 136 | RESULT_VARIABLE result 137 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) 138 | if(result) 139 | message(FATAL_ERROR "Build step for googletest failed: ${result}") 140 | endif() 141 | 142 | # Prevent overriding the parent project's compiler/linker 143 | # settings on Windows 144 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 145 | 146 | # Add googletest directly to our build. This defines 147 | # the gtest and gtest_main targets. 148 | add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src 149 | ${CMAKE_CURRENT_BINARY_DIR}/googletest-build 150 | EXCLUDE_FROM_ALL) 151 | 152 | # The gtest/gtest_main targets carry header search path 153 | # dependencies automatically when using CMake 2.8.11 or 154 | # later. Otherwise we have to add them here ourselves. 155 | if (CMAKE_VERSION VERSION_LESS 2.8.11) 156 | include_directories("${gtest_SOURCE_DIR}/include") 157 | endif() 158 | 159 | include(CTest) 160 | add_subdirectory(test) 161 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read the [full text](https://code.fb.com/codeofconduct/) so that you can understand what actions will and will not be tolerated. 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to pushmi 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Our Development Process 6 | All Development on this project is public. 7 | 8 | ## Pull Requests 9 | We actively welcome your pull requests. 10 | 11 | 1. Fork the repo and create your branch from `master`. 12 | 2. If you've added code that should be tested, add tests. 13 | 3. If you've changed APIs, update the documentation. 14 | 4. Ensure the test suite passes. 15 | 5. Make sure your code lints. 16 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 17 | 18 | ## Contributor License Agreement ("CLA") 19 | In order to accept your pull request, we need you to submit a CLA. You only need 20 | to do this once to work on any of Facebook's open source projects. 21 | 22 | Complete your CLA here: 23 | 24 | ## Issues 25 | We use GitHub issues to track public bugs. Please ensure your description is 26 | clear and has sufficient instructions to be able to reproduce the issue. 27 | 28 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 29 | disclosure of security bugs. In those cases, please go through the process 30 | outlined on that page and do not file a public issue. 31 | 32 | ## Coding Style 33 | * 2 spaces for indentation rather than tabs 34 | * 80 character line length 35 | 36 | ## License 37 | By contributing to pushmi, you agree that your contributions will be licensed 38 | under the LICENSE file in the root directory of this source tree. 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Copyright (c) 2018-present, Facebook, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /TARGETS: -------------------------------------------------------------------------------- 1 | cpp_library( 2 | name = "pushmi_core", 3 | srcs = [], 4 | headers = [ 5 | "include/boosters.h", 6 | "include/concepts.h", 7 | "include/executor.h", 8 | "include/extension_points.h", 9 | "include/flowsingle.h", 10 | "include/flowsinglesender.h", 11 | "include/none.h", 12 | "include/piping.h", 13 | "include/receiver.h", 14 | "include/singlesender.h", 15 | "include/traits.h", 16 | "include/trampoline.h", 17 | ], 18 | deps = [ 19 | "//folly:function", 20 | "//folly:optional", 21 | "//folly:poly", 22 | "//folly/executors:cpu_thread_pool_executor", 23 | "//folly/executors:sequenced_executor", 24 | "//folly/executors:serial_executor", 25 | "//folly/futures:futures", 26 | "//folly/io/async:async", 27 | "//folly/poly:basic_interfaces", 28 | ], 29 | external_deps = [ 30 | ], 31 | ) 32 | 33 | cpp_library( 34 | name = "pushmi_operators", 35 | srcs = [], 36 | headers = [ 37 | "include/o/empty.h", 38 | "include/o/just.h", 39 | "include/o/submit.h", 40 | "include/o/on.h", 41 | "include/o/via.h", 42 | ], 43 | deps = [ 44 | ":pushmi_core", 45 | "//folly:function", 46 | "//folly:poly", 47 | "//folly/futures:futures", 48 | "//folly/io/async:async", 49 | "//folly/poly:basic_interfaces", 50 | ], 51 | external_deps = [ 52 | ], 53 | ) 54 | 55 | cpp_unittest( 56 | name = "pushmi_test", 57 | srcs = [ 58 | "PushmiTest.cpp", 59 | ], 60 | deps = [ 61 | ":pushmi_core", 62 | ":pushmi_operators", 63 | "//folly:conv", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /benchmarks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-present, Facebook, Inc. 2 | # 3 | # This source code is licensed under the Apache License found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | FIND_PACKAGE (Boost) 7 | 8 | if (Boost_FOUND) 9 | 10 | add_executable(PushmiBenchmarks 11 | runner.cpp 12 | PushmiBenchmarks.cpp 13 | ) 14 | target_include_directories(PushmiBenchmarks 15 | PUBLIC ${Boost_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/../examples/include 16 | ) 17 | target_link_libraries(PushmiBenchmarks 18 | pushmi 19 | Threads::Threads 20 | ) 21 | 22 | else() 23 | 24 | message(WARNING "Boost not found, no benchmarks will be built") 25 | 26 | endif() 27 | -------------------------------------------------------------------------------- /benchmarks/runner.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, Facebook, Inc. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #define NONIUS_RUNNER 7 | #define concept Concept 8 | #include 9 | -------------------------------------------------------------------------------- /buildSingleHeader.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-present, Facebook, Inc. 2 | # 3 | # This source code is licensed under the Apache License found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | function(BuildSingleHeader HeaderName) 7 | set(header_files ${ARGN}) 8 | # write out single-header 9 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/${HeaderName}-single-header.h" header) 10 | string(APPEND incls "${header}") 11 | 12 | foreach(f ${header_files}) 13 | message("processing ${f}") 14 | file(READ ${f} contents) 15 | string(REGEX REPLACE "(([\t ]*#[\t ]*pragma[\t ]+once)|([\t ]*#[\t ]*include))" "//\\1" filtered "${contents}") 16 | string(APPEND incls "${filtered}") 17 | endforeach() 18 | 19 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/${HeaderName}-single-footer.h" footer) 20 | string(APPEND incls "${footer}") 21 | 22 | file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/include/${HeaderName}.h "${incls}") 23 | endfunction() 24 | 25 | set(header_files 26 | # keep in inclusion order 27 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/detail/concept_def.h" 28 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/detail/if_constexpr.h" 29 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/detail/functional.h" 30 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/detail/opt.h" 31 | 32 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/traits.h" 33 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/forwards.h" 34 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/extension_points.h" 35 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/properties.h" 36 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/concepts.h" 37 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/boosters.h" 38 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/executor.h" 39 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/receiver.h" 40 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/piping.h" 41 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/trampoline.h" 42 | 43 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/flow_single_sender.h" 44 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/flow_many_sender.h" 45 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/flow_receiver.h" 46 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/single_sender.h" 47 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/many_sender.h" 48 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/inline.h" 49 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/strand.h" 50 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/new_thread.h" 51 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/time_source.h" 52 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/entangle.h" 53 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/extension_operators.h" 54 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/subject.h" 55 | 56 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/submit.h" 57 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/just.h" 58 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/error.h" 59 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/switch_on_error.h" 60 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/for_each.h" 61 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/via.h" 62 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/empty.h" 63 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/request_via.h" 64 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/share.h" 65 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/on.h" 66 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/transform.h" 67 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/schedule.h" 68 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/defer.h" 69 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/from.h" 70 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/tap.h" 71 | "${CMAKE_CURRENT_SOURCE_DIR}/include/pushmi/o/filter.h" 72 | ) 73 | 74 | BuildSingleHeader("pushmi" ${header_files}) 75 | -------------------------------------------------------------------------------- /cmake/CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-present, Facebook, Inc. 2 | # 3 | # This source code is licensed under the Apache License found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | cmake_minimum_required(VERSION 2.8.2) 7 | 8 | project(googletest-download NONE) 9 | 10 | include(ExternalProject) 11 | ExternalProject_Add(googletest 12 | GIT_REPOSITORY https://github.com/google/googletest.git 13 | GIT_TAG master 14 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" 15 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" 16 | CONFIGURE_COMMAND "" 17 | BUILD_COMMAND "" 18 | INSTALL_COMMAND "" 19 | TEST_COMMAND "" 20 | ) 21 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-present, Facebook, Inc. 2 | # 3 | # This source code is licensed under the Apache License found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | add_library(examples INTERFACE) 7 | 8 | target_include_directories(examples INTERFACE 9 | $) 10 | 11 | add_subdirectory(twoway_execute) 12 | add_subdirectory(then_execute) 13 | add_subdirectory(composition) 14 | add_subdirectory(for_each) 15 | add_subdirectory(reduce) 16 | add_subdirectory(set_done) 17 | add_subdirectory(set_error) 18 | -------------------------------------------------------------------------------- /examples/bulk.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | namespace pushmi { 21 | namespace operators { 22 | 23 | PUSHMI_INLINE_VAR constexpr struct bulk_fn { 24 | template < 25 | class F, 26 | class ShapeBegin, 27 | class ShapeEnd, 28 | class Target, 29 | class IF, 30 | class RS> 31 | auto operator()( 32 | F&& func, 33 | ShapeBegin sb, 34 | ShapeEnd se, 35 | Target&& driver, 36 | IF&& initFunc, 37 | RS&& selector) const { 38 | return [func, sb, se, driver, initFunc, selector](auto in) mutable { 39 | return make_single_sender( 40 | [in, func, sb, se, driver, initFunc, selector](auto out) mutable { 41 | using Out = decltype(out); 42 | struct data : Out { 43 | data(Out out) : Out(std::move(out)) {} 44 | bool empty = true; 45 | }; 46 | submit( 47 | in, 48 | make_receiver( 49 | data{std::move(out)}, 50 | [func, sb, se, driver, initFunc, selector]( 51 | auto& out_, auto input) mutable noexcept { 52 | out_.empty = false; 53 | driver( 54 | initFunc, 55 | selector, 56 | std::move(input), 57 | func, 58 | sb, 59 | se, 60 | std::move(static_cast(out_))); 61 | }, 62 | // forward to output 63 | [](auto o, auto e) noexcept {set_error(o, e);}, 64 | // only pass done through when empty 65 | [](auto o){ if (o.empty) { set_done(o); }})); 66 | }); 67 | }; 68 | } 69 | } bulk{}; 70 | 71 | } // namespace operators 72 | } // namespace pushmi 73 | -------------------------------------------------------------------------------- /examples/composition/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_executable(composition_2 composition_2.cpp) 4 | target_link_libraries(composition_2 5 | pushmi 6 | examples 7 | Threads::Threads) 8 | 9 | add_executable(composition_3 composition_3.cpp) 10 | target_link_libraries(composition_3 11 | pushmi 12 | examples 13 | Threads::Threads) 14 | 15 | add_executable(composition_4 composition_4.cpp) 16 | target_link_libraries(composition_4 17 | pushmi 18 | examples 19 | Threads::Threads) 20 | -------------------------------------------------------------------------------- /examples/composition/composition_2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../pool.h" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace pushmi::aliases; 28 | 29 | struct f_t {}; 30 | f_t f() { 31 | return {}; 32 | } 33 | struct g_t {}; 34 | g_t g(f_t) { 35 | return {}; 36 | } 37 | 38 | // these expressions are read backward, bottom-right to top-left 39 | template 40 | void lisp(CPUExecutor cpu, IOExecutor io) { 41 | // f on cpu - g on cpu (implicit: a single task on the cpu executor runs all 42 | // the functions) 43 | op::submit([](g_t) {})(op::transform([](f_t ft) { return g(ft); })( 44 | op::transform([](auto) { return f(); })(cpu.schedule()))); 45 | 46 | // f on cpu - g on cpu (explicit: the first cpu task runs f and a second cpu 47 | // task runs g) 48 | op::submit([](g_t) {})(op::transform([](f_t ft) { return g(ft); })( 49 | op::via(mi::strands(cpu))(op::transform([](auto) { return f(); })(cpu.schedule())))); 50 | 51 | // f on io - g on cpu 52 | op::submit([](g_t) {})(op::transform([](f_t ft) { return g(ft); })( 53 | op::via(mi::strands(cpu))(op::transform([](auto) { return f(); })(io.schedule())))); 54 | } 55 | 56 | template 57 | void sugar(CPUExecutor cpu, IOExecutor io) { 58 | // f on cpu - g on cpu (implicit: a single task on the cpu executor runs all 59 | // the functions) 60 | cpu.schedule() | op::transform([](auto) { return f(); }) | 61 | op::transform([](f_t ft) { return g(ft); }) | op::submit([](g_t) {}); 62 | 63 | // f on cpu - g on cpu (explicit: the first cpu task runs f and a second cpu 64 | // task runs g) 65 | cpu.schedule() | op::transform([](auto) { return f(); }) | op::via(mi::strands(cpu)) | 66 | op::transform([](f_t ft) { return g(ft); }) | op::submit([](g_t) {}); 67 | 68 | // f on io - g on cpu 69 | io.schedule() | op::transform([](auto) { return f(); }) | op::via(mi::strands(cpu)) | 70 | op::transform([](f_t ft) { return g(ft); }) | op::submit([](g_t) {}); 71 | } 72 | 73 | template 74 | void pipe(CPUExecutor cpu, IOExecutor io) { 75 | // f on cpu - g on cpu (implicit: a single task on the cpu executor runs all 76 | // the functions) 77 | mi::pipe( 78 | cpu.schedule(), 79 | op::transform([](auto) { return f(); }), 80 | op::transform([](f_t ft) { return g(ft); }), 81 | op::submit([](g_t) {})); 82 | 83 | // f on cpu - g on cpu (explicit: the first cpu task runs f and a second cpu 84 | // task runs g) 85 | mi::pipe( 86 | cpu.schedule(), 87 | op::transform([](auto) { return f(); }), 88 | op::via(mi::strands(cpu)), 89 | op::transform([](f_t ft) { return g(ft); }), 90 | op::submit([](g_t) {})); 91 | 92 | // f on io - g on cpu 93 | mi::pipe( 94 | io.schedule(), 95 | op::transform([](auto) { return f(); }), 96 | op::via(mi::strands(cpu)), 97 | op::transform([](f_t ft) { return g(ft); }), 98 | op::submit([](g_t) {})); 99 | } 100 | 101 | int main() { 102 | mi::pool cpuPool{std::max(1u, std::thread::hardware_concurrency())}; 103 | mi::pool ioPool{std::max(1u, std::thread::hardware_concurrency())}; 104 | 105 | lisp(cpuPool.executor(), ioPool.executor()); 106 | sugar(cpuPool.executor(), ioPool.executor()); 107 | pipe(cpuPool.executor(), ioPool.executor()); 108 | 109 | ioPool.wait(); 110 | cpuPool.wait(); 111 | 112 | std::cout << "OK" << std::endl; 113 | } 114 | -------------------------------------------------------------------------------- /examples/composition/composition_3.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | // https://godbolt.org/g/rVLMTu 28 | 29 | using namespace pushmi::aliases; 30 | 31 | // three models of submission deferral 32 | // (none of these use an executor, they are all running 33 | // synchronously on the main thread) 34 | 35 | // this constructs eagerly and submits just() lazily 36 | auto defer_execution() { 37 | printf("construct just\n"); 38 | return op::just(42) | op::tap([](int v) { printf("just - %d\n", v); }); 39 | } 40 | 41 | // this constructs defer() eagerly, constructs just() and submits just() lazily 42 | auto defer_construction() { 43 | return op::defer([] { return defer_execution(); }); 44 | } 45 | 46 | // this constructs defer(), constructs just() and submits just() eagerly 47 | auto eager_execution() { 48 | return defer_execution() | op::share(); 49 | } 50 | 51 | int main() { 52 | printf("\ncall defer_execution\n"); 53 | auto de = defer_execution(); 54 | printf("submit defer_execution\n"); 55 | de | op::submit(); 56 | // call defer_execution 57 | // construct just 58 | // submit defer_execution 59 | // just - 42 60 | 61 | printf("\ncall defer_construction\n"); 62 | auto dc = defer_construction(); 63 | printf("submit defer_construction\n"); 64 | dc | op::submit(); 65 | // call defer_construction 66 | // submit defer_construction 67 | // construct just 68 | // just - 42 69 | 70 | printf("\ncall eager_execution\n"); 71 | auto ee = eager_execution(); 72 | printf("submit eager_execution\n"); 73 | ee | op::submit(); 74 | // call eager_execution 75 | // construct just 76 | // just - 42 77 | // submit eager_execution 78 | 79 | std::cout << "OK" << std::endl; 80 | // OK 81 | } 82 | -------------------------------------------------------------------------------- /examples/composition/composition_4.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../pool.h" 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | using namespace pushmi::aliases; 31 | 32 | template 33 | auto io_operation(Io io) { 34 | return io.schedule() | op::transform([](auto) { return 42; }) | 35 | op::tap([](int v) { printf("io pool producing, %d\n", v); }) | 36 | op::request_via(); 37 | } 38 | 39 | int main() { 40 | mi::pool cpuPool{std::max(1u, std::thread::hardware_concurrency())}; 41 | mi::pool ioPool{std::max(1u, std::thread::hardware_concurrency())}; 42 | 43 | auto io = ioPool.executor(); 44 | auto cpu = cpuPool.executor(); 45 | 46 | io_operation(io).via(mi::strands(cpu)) | 47 | op::tap([](int v) { printf("cpu pool processing, %d\n", v); }) | 48 | op::submit(); 49 | 50 | // when the caller is not going to process the result (only side-effect 51 | // matters) or the caller is just going to push the result into a queue. 52 | // provide a way to skip the transition to a different executor and make it 53 | // stand out so that it has to be justified in code reviews. 54 | mi::via_cast>(io_operation(io)) | op::submit(); 55 | 56 | ioPool.wait(); 57 | cpuPool.wait(); 58 | 59 | std::cout << "OK" << std::endl; 60 | } 61 | -------------------------------------------------------------------------------- /examples/for_each.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "bulk.h" 19 | #include 20 | #include 21 | 22 | namespace pushmi { 23 | 24 | PUSHMI_INLINE_VAR constexpr struct for_each_fn { 25 | private: 26 | template 27 | struct fn { 28 | Function f_; 29 | template 30 | void operator()(detail::any, Cursor cursor) const { 31 | f_(*cursor); 32 | } 33 | }; 34 | struct identity { 35 | template 36 | auto operator()(T&& t) const { 37 | return (T &&) t; 38 | } 39 | }; 40 | struct zero { 41 | int operator()(detail::any) const noexcept { 42 | return 0; 43 | } 44 | }; 45 | 46 | public: 47 | template 48 | void operator()( 49 | ExecutionPolicy&& policy, 50 | RandomAccessIterator begin, 51 | RandomAccessIterator end, 52 | Function f) const { 53 | operators::just(0) | 54 | operators::bulk( 55 | fn{f}, begin, end, policy, identity{}, zero{}) | 56 | operators::blocking_submit(); 57 | } 58 | } for_each{}; 59 | 60 | } // namespace pushmi 61 | -------------------------------------------------------------------------------- /examples/for_each/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_executable(for_each_2 for_each_2.cpp) 4 | target_link_libraries(for_each_2 5 | pushmi 6 | examples 7 | Threads::Threads) 8 | 9 | add_executable(for_each_3 for_each_3.cpp) 10 | target_link_libraries(for_each_3 11 | pushmi 12 | examples 13 | Threads::Threads) 14 | -------------------------------------------------------------------------------- /examples/for_each/for_each_2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../for_each.h" 22 | #include "../pool.h" 23 | 24 | using namespace pushmi::aliases; 25 | 26 | template > 27 | auto naive_executor_bulk_target(Executor e, Allocator a = Allocator{}) { 28 | return [e, a]( 29 | auto init, 30 | auto selector, 31 | auto input, 32 | auto&& func, 33 | auto sb, 34 | auto se, 35 | auto out) mutable { 36 | using RS = decltype(selector); 37 | using F = std::conditional_t< 38 | std::is_lvalue_reference::value, 39 | decltype(func), 40 | typename std::remove_reference::type>; 41 | using Out = decltype(out); 42 | try { 43 | typename std::allocator_traits::template rebind_alloc 44 | allocState(a); 45 | auto shared_state = std::allocate_shared, // accumulation 51 | std::atomic, // pending 52 | std::atomic // exception count (protects assignment to 53 | // first exception) 54 | >>( 55 | allocState, 56 | std::exception_ptr{}, 57 | std::move(out), 58 | std::move(selector), 59 | (decltype(func)&&)func, 60 | init(std::move(input)), 61 | 1, 62 | 0); 63 | e.schedule() | op::submit([e, sb, se, shared_state](auto) mutable { 64 | auto stepDone = [](auto shared_state) { 65 | // pending 66 | if (--std::get<5>(*shared_state) == 0) { 67 | // first exception 68 | if (std::get<0>(*shared_state)) { 69 | mi::set_error( 70 | std::get<1>(*shared_state), std::get<0>(*shared_state)); 71 | return; 72 | } 73 | try { 74 | // selector(accumulation) 75 | auto result = std::get<2>(*shared_state)( 76 | std::move(std::get<4>(*shared_state).load())); 77 | mi::set_value(std::get<1>(*shared_state), std::move(result)); 78 | mi::set_done(std::get<1>(*shared_state)); 79 | } catch (...) { 80 | mi::set_error( 81 | std::get<1>(*shared_state), std::current_exception()); 82 | } 83 | } 84 | }; 85 | for (decltype(sb) idx{sb}; idx != se; ++idx) { 86 | ++std::get<5>(*shared_state); 87 | e.schedule() | op::submit([shared_state, idx, stepDone](auto ex) { 88 | try { 89 | // this indicates to me that bulk is not the right abstraction 90 | auto old = std::get<4>(*shared_state).load(); 91 | auto step = old; 92 | do { 93 | step = old; 94 | // func(accumulation, idx) 95 | std::get<3> (*shared_state)(step, idx); 96 | } while (!std::get<4>(*shared_state) 97 | .compare_exchange_strong(old, step)); 98 | } catch (...) { 99 | // exception count 100 | if (std::get<6>(*shared_state)++ == 0) { 101 | // store first exception 102 | std::get<0>(*shared_state) = std::current_exception(); 103 | } // else eat the exception 104 | } 105 | stepDone(shared_state); 106 | }); 107 | } 108 | stepDone(shared_state); 109 | }); 110 | } catch (...) { 111 | e.schedule() | 112 | op::submit([out = std::move(out), ep = std::current_exception()]( 113 | auto) mutable { mi::set_error(out, ep); }); 114 | } 115 | }; 116 | } 117 | 118 | int main() { 119 | mi::pool p{std::max(1u, std::thread::hardware_concurrency())}; 120 | 121 | std::vector vec(10); 122 | 123 | mi::for_each( 124 | naive_executor_bulk_target(p.executor()), 125 | vec.begin(), 126 | vec.end(), 127 | [](int& x) { x = 42; }); 128 | 129 | assert( 130 | std::count(vec.begin(), vec.end(), 42) == static_cast(vec.size())); 131 | 132 | std::cout << "OK" << std::endl; 133 | 134 | p.stop(); 135 | p.wait(); 136 | } 137 | -------------------------------------------------------------------------------- /examples/for_each/for_each_3.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../for_each.h" 22 | 23 | using namespace pushmi::aliases; 24 | 25 | auto inline_bulk_target() { 26 | return [](auto init, 27 | auto selector, 28 | auto input, 29 | auto&& func, 30 | auto sb, 31 | auto se, 32 | auto out) { 33 | try { 34 | auto acc = init(input); 35 | for (decltype(sb) idx{sb}; idx != se; ++idx) { 36 | func(acc, idx); 37 | } 38 | auto result = selector(std::move(acc)); 39 | mi::set_value(out, std::move(result)); 40 | mi::set_done(out); 41 | } catch (...) { 42 | mi::set_error(out, std::current_exception()); 43 | } 44 | }; 45 | } 46 | 47 | int main() { 48 | std::vector vec(10); 49 | 50 | mi::for_each( 51 | inline_bulk_target(), vec.begin(), vec.end(), [](int& x) { x = 42; }); 52 | 53 | assert( 54 | std::count(vec.begin(), vec.end(), 42) == static_cast(vec.size())); 55 | 56 | std::cout << "OK" << std::endl; 57 | } 58 | -------------------------------------------------------------------------------- /examples/no_fail.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | namespace detail { 23 | 24 | struct no_fail_fn { 25 | private: 26 | struct on_error_impl { 27 | [[noreturn]] void operator()(any, any) noexcept { 28 | std::terminate(); 29 | } 30 | }; 31 | template 32 | struct out_impl { 33 | PUSHMI_TEMPLATE(class SIn, class Out) 34 | (requires Receiver) // 35 | void operator()(SIn&& in, Out out) const { 36 | submit((In&&)in, ::pushmi::detail::receiver_from_fn()( 37 | std::move(out), ::pushmi::on_error(on_error_impl{}))); 38 | } 39 | }; 40 | struct in_impl { 41 | PUSHMI_TEMPLATE(class In) 42 | (requires Sender)auto operator()(In in) const { 43 | return ::pushmi::detail::sender_from( 44 | std::move(in), 45 | out_impl{}); 46 | } 47 | }; 48 | 49 | public: 50 | auto operator()() const { 51 | return in_impl{}; 52 | } 53 | }; 54 | 55 | } // namespace detail 56 | 57 | namespace operators { 58 | PUSHMI_INLINE_VAR constexpr detail::no_fail_fn no_fail{}; 59 | 60 | } // namespace operators 61 | } // namespace pushmi 62 | -------------------------------------------------------------------------------- /examples/pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Copyright (c) 2018-present, Facebook, Inc. 4 | // 5 | // This source code is licensed under the MIT license found in the 6 | // LICENSE file in the root directory of this source tree. 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #if __cpp_deduction_guides >= 201703 17 | #define MAKE(x) x MAKE_ 18 | #define MAKE_(...) {__VA_ARGS__} 19 | #else 20 | #define MAKE(x) make_ ## x 21 | #endif 22 | 23 | namespace pushmi { 24 | 25 | using std::experimental::static_thread_pool; 26 | namespace execution = std::experimental::execution; 27 | 28 | template 29 | class pool_executor { 30 | Executor e; 31 | 32 | struct task { 33 | using properties = 34 | property_set< 35 | is_sender<>, is_never_blocking<>, is_single<>>; 36 | 37 | explicit task(pool_executor e) 38 | : pool_ex_(std::move(e)) 39 | {} 40 | 41 | PUSHMI_TEMPLATE(class Out) 42 | (requires ReceiveValue) 43 | void submit(Out out) && { 44 | pool_ex_.e.execute([e = pool_ex_, out = std::move(out)]() mutable { 45 | ::pushmi::set_value(out, e); 46 | ::pushmi::set_done(out); 47 | }); 48 | } 49 | 50 | private: 51 | pool_executor pool_ex_; 52 | }; 53 | 54 | public: 55 | using properties = property_set, is_concurrent_sequence<>>; 56 | 57 | explicit pool_executor(Executor e) 58 | : e(std::move(e)) 59 | {} 60 | 61 | task schedule() { 62 | return task{*this}; 63 | } 64 | }; 65 | 66 | class pool { 67 | static_thread_pool p; 68 | 69 | public: 70 | explicit pool(std::size_t threads) 71 | : p(threads) 72 | {} 73 | 74 | auto executor() { 75 | auto exec = execution::require(p.executor(), execution::never_blocking, execution::oneway); 76 | return pool_executor{exec}; 77 | } 78 | 79 | void stop() { p.stop(); } 80 | void wait() { p.wait(); } 81 | }; 82 | 83 | } // namespace pushmi 84 | -------------------------------------------------------------------------------- /examples/reduce.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "bulk.h" 19 | #include 20 | #include 21 | 22 | namespace pushmi { 23 | 24 | PUSHMI_INLINE_VAR constexpr struct reduce_fn { 25 | private: 26 | template 27 | struct fn { 28 | BinaryOp binary_op_; 29 | template 30 | void operator()(Acc& acc, Cursor cursor) const { 31 | acc = binary_op_(acc, *cursor); 32 | } 33 | }; 34 | struct identity { 35 | template 36 | auto operator()(T&& t) const { 37 | return (T &&) t; 38 | } 39 | }; 40 | 41 | public: 42 | template 43 | T operator()( 44 | ExecutionPolicy&& policy, 45 | ForwardIt begin, 46 | ForwardIt end, 47 | T init, 48 | BinaryOp binary_op) const { 49 | return operators::just(std::move(init)) | 50 | operators::bulk( 51 | fn{binary_op}, 52 | begin, 53 | end, 54 | policy, 55 | identity{}, 56 | identity{}) | 57 | operators::get; 58 | } 59 | } reduce{}; 60 | 61 | } // namespace pushmi 62 | -------------------------------------------------------------------------------- /examples/reduce/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_executable(reduce_2 reduce_2.cpp) 4 | target_link_libraries(reduce_2 5 | pushmi 6 | examples 7 | Threads::Threads) 8 | 9 | add_executable(reduce_3 reduce_3.cpp) 10 | target_link_libraries(reduce_3 11 | pushmi 12 | examples 13 | Threads::Threads) 14 | -------------------------------------------------------------------------------- /examples/reduce/reduce_2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "../pool.h" 24 | #include "../reduce.h" 25 | 26 | using namespace pushmi::aliases; 27 | 28 | template > 29 | auto naive_executor_bulk_target(Executor e, Allocator a = Allocator{}) { 30 | return [e, a]( 31 | auto init, 32 | auto selector, 33 | auto input, 34 | auto&& func, 35 | auto sb, 36 | auto se, 37 | auto out) mutable { 38 | using RS = decltype(selector); 39 | using F = std::conditional_t< 40 | std::is_lvalue_reference::value, 41 | decltype(func), 42 | typename std::remove_reference::type>; 43 | using Out = decltype(out); 44 | try { 45 | typename std::allocator_traits::template rebind_alloc 46 | allocState(a); 47 | auto shared_state = std::allocate_shared, // accumulation 53 | std::atomic, // pending 54 | std::atomic // exception count (protects assignment to 55 | // first exception) 56 | >>( 57 | allocState, 58 | std::exception_ptr{}, 59 | std::move(out), 60 | std::move(selector), 61 | (decltype(func)&&)func, 62 | init(std::move(input)), 63 | 1, 64 | 0); 65 | e.schedule() | op::submit([e, sb, se, shared_state](auto) mutable { 66 | auto stepDone = [](auto shared_state) { 67 | // pending 68 | if (--std::get<5>(*shared_state) == 0) { 69 | // first exception 70 | if (std::get<0>(*shared_state)) { 71 | mi::set_error( 72 | std::get<1>(*shared_state), std::get<0>(*shared_state)); 73 | return; 74 | } 75 | try { 76 | // selector(accumulation) 77 | auto result = std::get<2>(*shared_state)( 78 | std::move(std::get<4>(*shared_state).load())); 79 | mi::set_value(std::get<1>(*shared_state), std::move(result)); 80 | mi::set_done(std::get<1>(*shared_state)); 81 | } catch (...) { 82 | mi::set_error( 83 | std::get<1>(*shared_state), std::current_exception()); 84 | } 85 | } 86 | }; 87 | for (decltype(sb) idx{sb}; idx != se; ++idx) { 88 | ++std::get<5>(*shared_state); 89 | e.schedule() | op::submit([shared_state, idx, stepDone](auto ex) { 90 | try { 91 | // this indicates to me that bulk is not the right abstraction 92 | auto old = std::get<4>(*shared_state).load(); 93 | auto step = old; 94 | do { 95 | step = old; 96 | // func(accumulation, idx) 97 | std::get<3>(*shared_state)(step, idx); 98 | } while (!std::get<4>(*shared_state) 99 | .compare_exchange_strong(old, step)); 100 | } catch (...) { 101 | // exception count 102 | if (std::get<6>(*shared_state)++ == 0) { 103 | // store first exception 104 | std::get<0>(*shared_state) = std::current_exception(); 105 | } // else eat the exception 106 | } 107 | stepDone(shared_state); 108 | }); 109 | } 110 | stepDone(shared_state); 111 | }); 112 | } catch (...) { 113 | e.schedule() | 114 | op::submit([out = std::move(out), ep = std::current_exception()]( 115 | auto) mutable { mi::set_error(out, ep); }); 116 | } 117 | }; 118 | } 119 | 120 | int main() { 121 | mi::pool p{std::max(1u, std::thread::hardware_concurrency())}; 122 | 123 | std::vector vec(10); 124 | std::fill(vec.begin(), vec.end(), 4); 125 | 126 | auto fortyTwo = mi::reduce( 127 | naive_executor_bulk_target(p.executor()), 128 | vec.begin(), 129 | vec.end(), 130 | 2, 131 | std::plus<>{}); 132 | 133 | assert(std::accumulate(vec.begin(), vec.end(), 2) == fortyTwo); 134 | 135 | std::cout << "OK" << std::endl; 136 | 137 | p.wait(); 138 | } 139 | -------------------------------------------------------------------------------- /examples/reduce/reduce_3.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "../reduce.h" 23 | 24 | using namespace pushmi::aliases; 25 | 26 | auto inline_bulk_target() { 27 | return [](auto init, 28 | auto selector, 29 | auto input, 30 | auto&& func, 31 | auto sb, 32 | auto se, 33 | auto out) { 34 | try { 35 | auto acc = init(input); 36 | for (decltype(sb) idx{sb}; idx != se; ++idx) { 37 | func(acc, idx); 38 | } 39 | auto result = selector(std::move(acc)); 40 | mi::set_value(out, std::move(result)); 41 | mi::set_done(out); 42 | } catch (...) { 43 | mi::set_error(out, std::current_exception()); 44 | } 45 | }; 46 | } 47 | 48 | int main() { 49 | std::vector vec(10); 50 | std::fill(vec.begin(), vec.end(), 4); 51 | 52 | auto fortyTwo = mi::reduce( 53 | inline_bulk_target(), vec.begin(), vec.end(), 2, std::plus<>{}); 54 | 55 | assert(std::accumulate(vec.begin(), vec.end(), 2) == fortyTwo); 56 | 57 | std::cout << fortyTwo << std::endl; 58 | 59 | std::cout << "OK" << std::endl; 60 | } 61 | -------------------------------------------------------------------------------- /examples/set_done/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_executable(set_done_2 set_done_2.cpp) 4 | target_link_libraries(set_done_2 5 | pushmi 6 | examples 7 | Threads::Threads) 8 | -------------------------------------------------------------------------------- /examples/set_done/set_done_2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace pushmi::aliases; 28 | 29 | const bool setting_exists = false; 30 | 31 | auto get_setting() { 32 | return mi::make_single_sender([](auto out) { 33 | if (setting_exists) { 34 | op::just(42) | op::submit(out); 35 | } else { 36 | op::empty() | op::submit(out); 37 | } 38 | }); 39 | } 40 | 41 | auto println = [](auto v) { std::cout << v << std::endl; }; 42 | 43 | // concat not yet implemented 44 | template 45 | auto concat() { 46 | return [](auto in) { 47 | return mi::make_single_sender([in](auto out) mutable { 48 | mi::submit(in, mi::make_receiver(out, [](auto out_, auto v) { 49 | mi::submit(v, mi::any_receiver(out_)); 50 | })); 51 | }); 52 | }; 53 | } 54 | 55 | int main() { 56 | get_setting() | op::transform([](int i) { return std::to_string(i); }) | 57 | op::submit(println); 58 | 59 | op::just(42) | op::filter([](int i) { return i < 42; }) | 60 | op::transform([](int i) { return std::to_string(i); }) | 61 | op::submit(println); 62 | 63 | op::just(42) | op::transform([](int i) { 64 | if (i < 42) { 65 | return mi::any_single_sender{ 66 | op::empty()}; 67 | } 68 | return mi::any_single_sender{ 69 | op::just(std::to_string(i))}; 70 | }) | concat() | 71 | op::submit(println); 72 | 73 | std::cout << "OK" << std::endl; 74 | } 75 | -------------------------------------------------------------------------------- /examples/set_error/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_executable(set_error_2 set_error_2.cpp) 4 | target_link_libraries(set_error_2 5 | pushmi 6 | examples 7 | Threads::Threads) 8 | -------------------------------------------------------------------------------- /examples/set_error/set_error_2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../no_fail.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | using namespace pushmi::aliases; 29 | 30 | // concat not yet implemented 31 | template 32 | auto concat() { 33 | return [](auto in) { 34 | return mi::make_single_sender([in](auto out) mutable { 35 | mi::submit(in, mi::make_receiver(out, [](auto out_, auto v) { 36 | mi::submit(v, mi::any_receiver(out_)); 37 | })); 38 | }); 39 | }; 40 | } 41 | 42 | int main() { 43 | auto stop_abort = mi::on_error([](auto) noexcept {}); 44 | // support all error value types 45 | 46 | op::error(std::exception_ptr{}) | op::submit(stop_abort); 47 | 48 | op::error(std::errc::argument_list_too_long) | op::submit(stop_abort); 49 | 50 | // transform an error 51 | 52 | op::error(std::errc::argument_list_too_long) | 53 | op::switch_on_error([](auto) noexcept { 54 | return op::error(std::exception_ptr{}); 55 | }) | 56 | op::submit(stop_abort); 57 | 58 | // use default value if an error occurs 59 | 60 | op::just(42) | 61 | op::switch_on_error([](auto) noexcept { return op::just(0); }) | 62 | op::submit(); 63 | 64 | // suppress if an error occurs 65 | 66 | op::error(std::errc::argument_list_too_long) | 67 | op::switch_on_error([](auto) noexcept { return op::empty(); }) | 68 | op::submit(); 69 | 70 | // abort if an error occurs 71 | 72 | op::just(42) | op::no_fail() | op::submit(); 73 | 74 | // transform value to error_ 75 | 76 | op::just(42) | op::transform([](auto v) { 77 | using r_t = mi::any_single_sender; 78 | if (v < 40) { 79 | return r_t{op::error(std::exception_ptr{})}; 80 | } else { 81 | return r_t{op::just(v)}; 82 | } 83 | }) | concat() | 84 | op::submit(); 85 | 86 | // retry on error 87 | 88 | // http.get(ex) | 89 | // op::timeout(ex, 1s) | 90 | // op::switch_on_error([](auto e) noexcept { return op::timer(ex, 1s); }) | 91 | // op::repeat() | 92 | // op::timeout(ex, 10s) | 93 | // op::submit(); 94 | 95 | std::cout << "OK" << std::endl; 96 | } 97 | -------------------------------------------------------------------------------- /examples/then_execute/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_executable(then_execute_2 then_execute_2.cpp) 4 | target_link_libraries(then_execute_2 5 | pushmi 6 | examples 7 | Threads::Threads) 8 | -------------------------------------------------------------------------------- /examples/then_execute/then_execute_2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "../pool.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | using namespace pushmi::aliases; 38 | 39 | struct inline_executor { 40 | public: 41 | friend bool operator==( 42 | const inline_executor&, 43 | const inline_executor&) noexcept { 44 | return true; 45 | } 46 | friend bool operator!=( 47 | const inline_executor&, 48 | const inline_executor&) noexcept { 49 | return false; 50 | } 51 | template 52 | void execute(Function f) const noexcept { 53 | f(); 54 | } 55 | constexpr bool query(std::experimental::execution::oneway_t) { 56 | return true; 57 | } 58 | constexpr bool query(std::experimental::execution::twoway_t) { 59 | return false; 60 | } 61 | constexpr bool query(std::experimental::execution::single_t) { 62 | return true; 63 | } 64 | }; 65 | 66 | namespace p1054 { 67 | // A promise refers to a promise and is associated with a future, 68 | // either through type-erasure or through construction of an 69 | // underlying promise with an overload of make_promise_contract(). 70 | 71 | // make_promise_contract() cannot be written to produce a lazy future. 72 | // the promise has to exist prior to .then() getting a continuation. 73 | // there must be a shared allocation to connect the promise and future. 74 | template 75 | std::pair< 76 | std::experimental::standard_promise, 77 | std::experimental::standard_future>> 78 | make_promise_contract(const Executor& e) { 79 | std::experimental::standard_promise promise; 80 | auto ex = e; 81 | return {promise, promise.get_future(std::move(ex))}; 82 | } 83 | 84 | template 85 | std::experimental::standard_future< 86 | std::result_of_t< 87 | Function(std::decay_t::value_type>&&)>, 88 | std::decay_t> 89 | then_execute(Executor&& e, Function&& f, Future&& pred) { 90 | using V = std::decay_t::value_type>; 91 | using T = std::result_of_t; 92 | auto pc = make_promise_contract(e); 93 | auto p = std::get<0>(pc); 94 | auto r = std::get<1>(pc); 95 | ((Future &&) pred).then([e, p, f](V v) mutable { 96 | e.execute([p, f, v]() mutable { p.set_value(f(v)); }); 97 | return 0; 98 | }); 99 | return r; 100 | } 101 | 102 | } // namespace p1054 103 | 104 | namespace p1055 { 105 | 106 | template 107 | auto then_execute(Executor&& e, Function&& f, Future&& pred) { 108 | return pred | op::via(mi::strands(e)) | 109 | op::transform([f](auto v) { return f(v); }); 110 | } 111 | 112 | } // namespace p1055 113 | 114 | int main() { 115 | mi::pool p{std::max(1u, std::thread::hardware_concurrency())}; 116 | 117 | std::experimental::futures_static_thread_pool sp{ 118 | std::max(1u, std::thread::hardware_concurrency())}; 119 | 120 | auto pc = p1054::make_promise_contract(inline_executor{}); 121 | auto& pr = std::get<0>(pc); 122 | auto& r = std::get<1>(pc); 123 | auto f = p1054::then_execute( 124 | sp.executor(), [](int v) { return v * 2; }, std::move(r)); 125 | pr.set_value(42); 126 | f.get(); 127 | 128 | p1055::then_execute(p.executor(), [](int v) { return v * 2; }, op::just(21)) | 129 | op::get; 130 | 131 | sp.stop(); 132 | sp.wait(); 133 | p.stop(); 134 | p.wait(); 135 | 136 | std::cout << "OK" << std::endl; 137 | } 138 | -------------------------------------------------------------------------------- /examples/twoway_execute/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_executable(twoway_execute_2 twoway_execute_2.cpp) 4 | target_link_libraries(twoway_execute_2 5 | pushmi 6 | examples 7 | Threads::Threads) 8 | -------------------------------------------------------------------------------- /examples/twoway_execute/twoway_execute_2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include "../pool.h" 30 | 31 | #include 32 | 33 | using namespace pushmi::aliases; 34 | 35 | namespace p1054 { 36 | // A promise refers to a promise and is associated with a future, 37 | // either through type-erasure or through construction of an 38 | // underlying promise with an overload of make_promise_contract(). 39 | 40 | // make_promise_contract() cannot be written to produce a lazy future. 41 | // the promise has to exist prior to .then() getting a continuation. 42 | // there must be a shared allocation to connect the promise and future. 43 | template 44 | std::pair< 45 | std::experimental::standard_promise, 46 | std::experimental::standard_future>> 47 | make_promise_contract(const Executor& e) { 48 | std::experimental::standard_promise promise; 49 | auto ex = e; 50 | return {promise, promise.get_future(std::move(ex))}; 51 | } 52 | 53 | template 54 | std::experimental::standard_future< 55 | std::result_of_t()>, 56 | std::decay_t> 57 | twoway_execute(Executor&& e, Function&& f) { 58 | using T = std::result_of_t()>; 59 | auto pc = make_promise_contract(e); 60 | auto p = std::get<0>(pc); 61 | auto r = std::get<1>(pc); 62 | e.execute([p, f]() mutable { p.set_value(f()); }); 63 | return r; 64 | } 65 | } // namespace p1054 66 | 67 | namespace p1055 { 68 | 69 | template 70 | auto twoway_execute(Executor&& e, Function&& f) { 71 | return e.schedule() | op::transform([f](auto) { return f(); }); 72 | } 73 | 74 | } // namespace p1055 75 | 76 | int main() { 77 | mi::pool p{std::max(1u, std::thread::hardware_concurrency())}; 78 | 79 | std::experimental::static_thread_pool sp{ 80 | std::max(1u, std::thread::hardware_concurrency())}; 81 | 82 | p1054::twoway_execute(sp.executor(), []() { return 42; }).get(); 83 | p1055::twoway_execute(p.executor(), []() { return 42; }) | op::get; 84 | 85 | sp.stop(); 86 | sp.wait(); 87 | p.stop(); 88 | p.wait(); 89 | 90 | std::cout << "OK" << std::endl; 91 | } 92 | -------------------------------------------------------------------------------- /include/pushmi-single-footer.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2018-present, Facebook, Inc. 3 | // 4 | // This source code is licensed under the Apache License found in the 5 | // LICENSE file in the root directory of this source tree. 6 | 7 | #endif // PUSHMI_SINGLE_HEADER 8 | -------------------------------------------------------------------------------- /include/pushmi-single-header.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef PUSHMI_SINGLE_HEADER 3 | #define PUSHMI_SINGLE_HEADER 4 | 5 | // Copyright (c) 2018-present, Facebook, Inc. 6 | // 7 | // This source code is licensed under the Apache License found in the 8 | // LICENSE file in the root directory of this source tree. 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #if __cpp_lib_optional >= 201606 31 | #include 32 | #endif 33 | -------------------------------------------------------------------------------- /include/pushmi/detail/functional.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | #include 21 | 22 | namespace pushmi { 23 | 24 | PUSHMI_INLINE_VAR constexpr struct invoke_fn { 25 | private: 26 | template 27 | using mem_fn_t = decltype(std::mem_fn(std::declval())); 28 | 29 | public: 30 | PUSHMI_TEMPLATE(class F, class... As) 31 | (requires // 32 | requires( 33 | std::declval()(std::declval()...))) // 34 | auto operator()(F&& f, As&&... as) const 35 | noexcept(noexcept(((F &&) f)((As &&) as...))) { 36 | return ((F &&) f)((As &&) as...); 37 | } 38 | PUSHMI_TEMPLATE(class F, class... As) 39 | (requires // 40 | requires( 41 | std::mem_fn(std::declval())(std::declval()...))) // 42 | auto operator()(F&& f, As&&... as) const 43 | noexcept(noexcept(std::declval>()((As &&) as...))) { 44 | return std::mem_fn(f)((As &&) as...); 45 | } 46 | } invoke{}; 47 | 48 | template 49 | using invoke_result_t = 50 | decltype(pushmi::invoke(std::declval(), std::declval()...)); 51 | 52 | PUSHMI_CONCEPT_DEF( 53 | template (class F, class... Args) 54 | (concept Invocable)(F, Args...), 55 | requires(F&& f) ( 56 | ::pushmi::invoke((F &&) f, std::declval()...) 57 | ) 58 | ); 59 | 60 | PUSHMI_CONCEPT_DEF( 61 | template (class F, class... Args) 62 | (concept NothrowInvocable)(F, Args...), 63 | requires(F&& f) ( 64 | requires_()...))> 65 | ) && 66 | Invocable 67 | ); 68 | 69 | } // namespace pushmi 70 | -------------------------------------------------------------------------------- /include/pushmi/detail/if_constexpr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | 19 | // Usage: 20 | // 21 | // PUSHMI_IF_CONSTEXPR((condition)( 22 | // stmt1; 23 | // stmt2; 24 | // ) else ( 25 | // stmt3; 26 | // stmt4; 27 | // )) 28 | // 29 | // If the statements could potentially be ill-formed, you can give some 30 | // part of the expression a dependent type by wrapping it in `id`. For 31 | 32 | /** 33 | * Maybe_unused indicates that a function, variable or parameter might or 34 | * might not be used, e.g. 35 | * 36 | * int foo(FOLLY_MAYBE_UNUSED int x) { 37 | * #ifdef USE_X 38 | * return x; 39 | * #else 40 | * return 0; 41 | * #endif 42 | * } 43 | */ 44 | 45 | #ifndef __has_attribute 46 | #define PUSHMI_HAS_ATTRIBUTE(x) 0 47 | #else 48 | #define PUSHMI_HAS_ATTRIBUTE(x) __has_attribute(x) 49 | #endif 50 | 51 | #ifndef __has_cpp_attribute 52 | #define PUSHMI_HAS_CPP_ATTRIBUTE(x) 0 53 | #else 54 | #define PUSHMI_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) 55 | #endif 56 | 57 | #if PUSHMI_HAS_CPP_ATTRIBUTE(maybe_unused) 58 | #define PUSHMI_MAYBE_UNUSED [[maybe_unused]] 59 | #elif PUSHMI_HAS_ATTRIBUTE(__unused__) || __GNUC__ 60 | #define PUSHMI_MAYBE_UNUSED __attribute__((__unused__)) 61 | #else 62 | #define PUSHMI_MAYBE_UNUSED 63 | #endif 64 | 65 | // disable buggy compatibility warning about "requires" and "concept" being 66 | // C++20 keywords. 67 | #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 5) 68 | #define PUSHMI_PP_IGNORE_SHADOW_BEGIN \ 69 | _Pragma("GCC diagnostic push") \ 70 | _Pragma("GCC diagnostic ignored \"-Wshadow\"") \ 71 | /**/ 72 | #define PUSHMI_PP_IGNORE_SHADOW_END \ 73 | _Pragma("GCC diagnostic pop") 74 | #else 75 | 76 | #define PUSHMI_PP_IGNORE_SHADOW_BEGIN 77 | #define PUSHMI_PP_IGNORE_SHADOW_END 78 | #endif 79 | 80 | #define PUSHMI_COMMA , 81 | 82 | #define PUSHMI_EVAL(F, ...) F(__VA_ARGS__) 83 | 84 | #define PUSHMI_STRIP(...) __VA_ARGS__ 85 | 86 | namespace pushmi { 87 | namespace detail { 88 | struct id_fn { 89 | constexpr explicit operator bool() const noexcept { 90 | return false; 91 | } 92 | template 93 | constexpr T&& operator()(T&& t) const noexcept { 94 | return (T&&) t; 95 | } 96 | }; 97 | } // namespace detail 98 | } // namespace pushmi 99 | 100 | #if __cpp_if_constexpr >= 201606 101 | 102 | #define PUSHMI_IF_CONSTEXPR(LIST) \ 103 | if constexpr (::pushmi::detail::id_fn id = {}) { \ 104 | } else if constexpr \ 105 | PUSHMI_EVAL(PUSHMI_IF_CONSTEXPR_ELSE_, PUSHMI_IF_CONSTEXPR_IF_ LIST) 106 | 107 | #define PUSHMI_IF_CONSTEXPR_RETURN(LIST)\ 108 | PUSHMI_PP_IGNORE_SHADOW_BEGIN \ 109 | PUSHMI_IF_CONSTEXPR(LIST)\ 110 | PUSHMI_PP_IGNORE_SHADOW_END \ 111 | /**/ 112 | 113 | #define PUSHMI_IF_CONSTEXPR_IF_(...) \ 114 | (__VA_ARGS__) PUSHMI_COMMA PUSHMI_IF_CONSTEXPR_THEN_ 115 | 116 | #define PUSHMI_IF_CONSTEXPR_THEN_(...) \ 117 | ({__VA_ARGS__}) PUSHMI_COMMA 118 | 119 | #define PUSHMI_IF_CONSTEXPR_ELSE_(A, B, C) \ 120 | A PUSHMI_STRIP B PUSHMI_IF_CONSTEXPR_ ## C 121 | 122 | #define PUSHMI_IF_CONSTEXPR_else(...) \ 123 | else {__VA_ARGS__} 124 | 125 | #else 126 | 127 | #include 128 | 129 | #define PUSHMI_IF_CONSTEXPR(LIST)\ 130 | PUSHMI_EVAL(PUSHMI_IF_CONSTEXPR_ELSE_, PUSHMI_IF_CONSTEXPR_IF_ LIST)\ 131 | /**/ 132 | 133 | #define PUSHMI_IF_CONSTEXPR_RETURN(LIST)\ 134 | return PUSHMI_EVAL(PUSHMI_IF_CONSTEXPR_ELSE_, PUSHMI_IF_CONSTEXPR_IF_ LIST)\ 135 | /**/ 136 | 137 | #define PUSHMI_IF_CONSTEXPR_IF_(...) \ 138 | (::pushmi::detail::select() ->* PUSHMI_IF_CONSTEXPR_THEN_ \ 139 | /**/ 140 | 141 | #define PUSHMI_IF_CONSTEXPR_THEN_(...) \ 142 | ([&](PUSHMI_MAYBE_UNUSED auto id)mutable->decltype(auto){__VA_ARGS__})) PUSHMI_COMMA \ 143 | /**/ 144 | 145 | #define PUSHMI_IF_CONSTEXPR_ELSE_(A, B) \ 146 | A ->* PUSHMI_IF_CONSTEXPR_ ## B \ 147 | /**/ 148 | 149 | #define PUSHMI_IF_CONSTEXPR_else(...) \ 150 | ([&](PUSHMI_MAYBE_UNUSED auto id)mutable->decltype(auto){__VA_ARGS__});\ 151 | /**/ 152 | 153 | namespace pushmi { 154 | namespace detail { 155 | 156 | template 157 | struct select { 158 | template ::value>> 159 | struct eat_return { 160 | R value_; 161 | template 162 | constexpr R operator->*(T&&) { 163 | return static_cast(value_); 164 | } 165 | }; 166 | struct eat { 167 | template 168 | constexpr void operator->*(T&&) {} 169 | }; 170 | template 171 | constexpr auto operator->*(T&& t) 172 | -> eat_return { 173 | return {t(::pushmi::detail::id_fn{})}; 174 | } 175 | template 176 | constexpr auto operator->*(T&& t) const -> eat { 177 | return t(::pushmi::detail::id_fn{}), void(), eat{}; 178 | } 179 | }; 180 | 181 | template <> 182 | struct select { 183 | struct eat { 184 | template 185 | constexpr auto operator->*(T&& t) -> decltype(auto) { 186 | return t(::pushmi::detail::id_fn{}); 187 | } 188 | }; 189 | template 190 | constexpr eat operator->*(T&&) { 191 | return {}; 192 | } 193 | }; 194 | } // namespace detail 195 | } // namespace pushmi 196 | #endif 197 | -------------------------------------------------------------------------------- /include/pushmi/detail/opt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #if __cpp_lib_optional >= 201606 19 | #include // @manual 20 | #endif 21 | #include 22 | #include 23 | 24 | namespace pushmi { 25 | namespace detail { 26 | #if __cpp_lib_optional >= 201606 27 | template 28 | struct opt : private std::optional { 29 | opt() = default; 30 | opt& operator=(T&& t) { 31 | this->std::optional::operator=(std::move(t)); 32 | return *this; 33 | } 34 | using std::optional::operator*; 35 | using std::optional::operator bool; 36 | }; 37 | #else 38 | template 39 | struct opt { 40 | private: 41 | bool empty_ = true; 42 | std::aligned_union_t<0, T> data_; 43 | T* ptr() { 44 | return static_cast((void*)&data_); 45 | } 46 | const T* ptr() const { 47 | return static_cast((const void*)&data_); 48 | } 49 | void reset() { 50 | if (!empty_) { 51 | ptr()->~T(); 52 | empty_ = true; 53 | } 54 | } 55 | 56 | public: 57 | opt() = default; 58 | opt(T&& t) noexcept(std::is_nothrow_move_constructible::value) { 59 | ::new (ptr()) T(std::move(t)); 60 | empty_ = false; 61 | } 62 | opt(const T& t) { 63 | ::new (ptr()) T(t); 64 | empty_ = false; 65 | } 66 | opt(opt&& that) noexcept(std::is_nothrow_move_constructible::value) { 67 | if (that) { 68 | ::new (ptr()) T(std::move(*that)); 69 | empty_ = false; 70 | that.reset(); 71 | } 72 | } 73 | opt(const opt& that) { 74 | if (that) { 75 | ::new (ptr()) T(*that); 76 | empty_ = false; 77 | } 78 | } 79 | ~opt() { 80 | reset(); 81 | } 82 | opt& operator=(opt&& that) noexcept( 83 | std::is_nothrow_move_constructible::value&& 84 | std::is_nothrow_move_assignable::value) { 85 | if (*this && that) { 86 | **this = std::move(*that); 87 | that.reset(); 88 | } else if (*this) { 89 | reset(); 90 | } else if (that) { 91 | ::new (ptr()) T(std::move(*that)); 92 | empty_ = false; 93 | } 94 | return *this; 95 | } 96 | opt& operator=(const opt& that) { 97 | if (*this && that) { 98 | **this = *that; 99 | } else if (*this) { 100 | reset(); 101 | } else if (that) { 102 | ::new (ptr()) T(*that); 103 | empty_ = false; 104 | } 105 | return *this; 106 | } 107 | opt& operator=(T&& t) noexcept( 108 | std::is_nothrow_move_constructible::value&& 109 | std::is_nothrow_move_assignable::value) { 110 | if (*this) 111 | **this = std::move(t); 112 | else { 113 | ::new (ptr()) T(std::move(t)); 114 | empty_ = false; 115 | } 116 | return *this; 117 | } 118 | opt& operator=(const T& t) { 119 | if (*this) 120 | **this = t; 121 | else { 122 | ::new (ptr()) T(t); 123 | empty_ = false; 124 | } 125 | return *this; 126 | } 127 | explicit operator bool() const noexcept { 128 | return !empty_; 129 | } 130 | T& operator*() noexcept { 131 | return *ptr(); 132 | } 133 | const T& operator*() const noexcept { 134 | return *ptr(); 135 | } 136 | }; 137 | #endif 138 | 139 | } // namespace detail 140 | } // namespace pushmi 141 | -------------------------------------------------------------------------------- /include/pushmi/flow_many_sender.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace pushmi { 24 | 25 | template 26 | class any_flow_many_sender { 27 | union data { 28 | void* pobj_ = nullptr; 29 | std::aligned_storage_t< 30 | sizeof(std::tuple), alignof(std::tuple)> buffer_; 31 | } data_{}; 32 | template 33 | static constexpr bool insitu() { 34 | return sizeof(Wrapped) <= sizeof(data::buffer_) && 35 | std::is_nothrow_move_constructible::value; 36 | } 37 | struct vtable { 38 | static void s_op(data&, data*) {} 39 | static void s_submit(data&, any_flow_receiver) {} 40 | void (*op_)(data&, data*) = vtable::s_op; 41 | void (*submit_)(data&, any_flow_receiver) = vtable::s_submit; 42 | }; 43 | static constexpr vtable const noop_ {}; 44 | vtable const* vptr_ = &noop_; 45 | template 46 | any_flow_many_sender(Wrapped obj, std::false_type) : any_flow_many_sender() { 47 | struct s { 48 | static void op(data& src, data* dst) { 49 | if (dst) 50 | dst->pobj_ = std::exchange(src.pobj_, nullptr); 51 | delete static_cast(src.pobj_); 52 | } 53 | static void submit(data& src, any_flow_receiver out) { 54 | ::pushmi::submit( 55 | *static_cast(src.pobj_), std::move(out)); 56 | } 57 | }; 58 | static const vtable vtbl{s::op, s::submit}; 59 | data_.pobj_ = new Wrapped(std::move(obj)); 60 | vptr_ = &vtbl; 61 | } 62 | template 63 | any_flow_many_sender(Wrapped obj, std::true_type) noexcept 64 | : any_flow_many_sender() { 65 | struct s { 66 | static void op(data& src, data* dst) { 67 | if (dst) 68 | new (&dst->buffer_) Wrapped( 69 | std::move(*static_cast((void*)&src.buffer_))); 70 | static_cast((void*)&src.buffer_)->~Wrapped(); 71 | } 72 | static void submit(data& src, any_flow_receiver out) { 73 | ::pushmi::submit( 74 | *static_cast((void*)&src.buffer_), std::move(out)); 75 | } 76 | }; 77 | static const vtable vtbl{s::op, s::submit}; 78 | new (&data_.buffer_) Wrapped(std::move(obj)); 79 | vptr_ = &vtbl; 80 | } 81 | template > 82 | using wrapped_t = 83 | std::enable_if_t::value, U>; 84 | public: 85 | using properties = property_set, is_flow<>, is_many<>>; 86 | 87 | any_flow_many_sender() = default; 88 | any_flow_many_sender(any_flow_many_sender&& that) noexcept 89 | : any_flow_many_sender() { 90 | that.vptr_->op_(that.data_, &data_); 91 | std::swap(that.vptr_, vptr_); 92 | } 93 | PUSHMI_TEMPLATE (class Wrapped) 94 | (requires FlowSender, is_many<>>) 95 | explicit any_flow_many_sender(Wrapped obj) noexcept(insitu()) 96 | : any_flow_many_sender{std::move(obj), bool_()>{}} {} 97 | ~any_flow_many_sender() { 98 | vptr_->op_(data_, nullptr); 99 | } 100 | any_flow_many_sender& operator=(any_flow_many_sender&& that) noexcept { 101 | this->~any_flow_many_sender(); 102 | new ((void*)this) any_flow_many_sender(std::move(that)); 103 | return *this; 104 | } 105 | PUSHMI_TEMPLATE(class Out) 106 | (requires ReceiveError&& ReceiveValue) // 107 | void submit(Out&& out) { 108 | vptr_->submit_(data_, any_flow_receiver{(Out &&) out}); 109 | } 110 | }; 111 | 112 | // Class static definitions: 113 | template 114 | constexpr typename any_flow_many_sender::vtable const 115 | any_flow_many_sender::noop_; 116 | 117 | template 118 | class flow_many_sender { 119 | SF sf_; 120 | 121 | public: 122 | using properties = property_set, is_flow<>, is_many<>>; 123 | 124 | constexpr flow_many_sender() = default; 125 | constexpr explicit flow_many_sender(SF sf) 126 | : sf_(std::move(sf)) {} 127 | 128 | PUSHMI_TEMPLATE(class Out) 129 | (requires FlowReceiver && Invocable) 130 | void submit(Out out) { 131 | sf_(std::move(out)); 132 | } 133 | }; 134 | 135 | template , is_flow<>>) Data, class DSF> 136 | class flow_many_sender { 137 | Data data_; 138 | DSF sf_; 139 | 140 | public: 141 | using properties = property_set_insert_t, property_set, is_flow<>, is_many<>>>; 142 | 143 | constexpr flow_many_sender() = default; 144 | constexpr explicit flow_many_sender(Data data) 145 | : data_(std::move(data)) {} 146 | constexpr flow_many_sender(Data data, DSF sf) 147 | : data_(std::move(data)), sf_(std::move(sf)) {} 148 | 149 | PUSHMI_TEMPLATE(class Out) 150 | (requires PUSHMI_EXP(lazy::FlowReceiver PUSHMI_AND 151 | lazy::Invocable)) 152 | void submit(Out out) { 153 | sf_(data_, std::move(out)); 154 | } 155 | }; 156 | 157 | template <> 158 | class flow_many_sender<> 159 | : public flow_many_sender { 160 | public: 161 | flow_many_sender() = default; 162 | }; 163 | 164 | //////////////////////////////////////////////////////////////////////////////// 165 | // make_flow_many_sender 166 | PUSHMI_INLINE_VAR constexpr struct make_flow_many_sender_fn { 167 | inline auto operator()() const { 168 | return flow_many_sender{}; 169 | } 170 | PUSHMI_TEMPLATE(class SF) 171 | (requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender)) 172 | auto operator()(SF sf) const { 173 | return flow_many_sender{std::move(sf)}; 174 | } 175 | PUSHMI_TEMPLATE(class Data) 176 | (requires True<> && Sender, is_flow<>>) 177 | auto operator()(Data d) const { 178 | return flow_many_sender{std::move(d)}; 179 | } 180 | PUSHMI_TEMPLATE(class Data, class DSF) 181 | (requires Sender, is_flow<>>) 182 | auto operator()(Data d, DSF sf) const { 183 | return flow_many_sender{std::move(d), std::move(sf)}; 184 | } 185 | } const make_flow_many_sender {}; 186 | 187 | //////////////////////////////////////////////////////////////////////////////// 188 | // deduction guides 189 | #if __cpp_deduction_guides >= 201703 190 | flow_many_sender() -> flow_many_sender; 191 | 192 | PUSHMI_TEMPLATE(class SF) 193 | (requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender)) 194 | flow_many_sender(SF) -> flow_many_sender; 195 | 196 | PUSHMI_TEMPLATE(class Data) 197 | (requires True<> && Sender, is_flow<>>) 198 | flow_many_sender(Data) -> flow_many_sender; 199 | 200 | PUSHMI_TEMPLATE(class Data, class DSF) 201 | (requires Sender, is_flow<>>) 202 | flow_many_sender(Data, DSF) -> flow_many_sender; 203 | #endif 204 | 205 | template<> 206 | struct construct_deduced 207 | : make_flow_many_sender_fn {}; 208 | 209 | // // TODO constrain me 210 | // template 211 | // auto erase_cast(Wrapped w) { 212 | // return flow_many_sender{std::move(w)}; 213 | // } 214 | 215 | } // namespace pushmi 216 | -------------------------------------------------------------------------------- /include/pushmi/flow_single_sender.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace pushmi { 24 | 25 | template 26 | class any_flow_single_sender { 27 | union data { 28 | void* pobj_ = nullptr; 29 | std::aligned_storage_t< 30 | sizeof(std::tuple), alignof(std::tuple)> buffer_; 31 | } data_{}; 32 | template 33 | static constexpr bool insitu() { 34 | return sizeof(Wrapped) <= sizeof(data::buffer_) && 35 | std::is_nothrow_move_constructible::value; 36 | } 37 | struct vtable { 38 | static void s_op(data&, data*) {} 39 | static void s_submit(data&, any_flow_receiver) {} 40 | void (*op_)(data&, data*) = vtable::s_op; 41 | void (*submit_)(data&, any_flow_receiver) = 42 | vtable::s_submit; 43 | }; 44 | static constexpr vtable const noop_ {}; 45 | vtable const* vptr_ = &noop_; 46 | template 47 | any_flow_single_sender(Wrapped obj, std::false_type) 48 | : any_flow_single_sender() { 49 | struct s { 50 | static void op(data& src, data* dst) { 51 | if (dst) 52 | dst->pobj_ = std::exchange(src.pobj_, nullptr); 53 | delete static_cast(src.pobj_); 54 | } 55 | static void submit( 56 | data& src, 57 | any_flow_receiver out) { 58 | ::pushmi::submit( 59 | *static_cast(src.pobj_), std::move(out)); 60 | } 61 | }; 62 | static const vtable vtbl{s::op, s::submit}; 63 | data_.pobj_ = new Wrapped(std::move(obj)); 64 | vptr_ = &vtbl; 65 | } 66 | template 67 | any_flow_single_sender(Wrapped obj, std::true_type) noexcept 68 | : any_flow_single_sender() { 69 | struct s { 70 | static void op(data& src, data* dst) { 71 | if (dst) 72 | new (&dst->buffer_) 73 | Wrapped(std::move(*static_cast((void*)&src.buffer_))); 74 | static_cast((void*)&src.buffer_)->~Wrapped(); 75 | } 76 | static void submit( 77 | data& src, 78 | any_flow_receiver out) { 79 | ::pushmi::submit( 80 | *static_cast((void*)&src.buffer_), std::move(out)); 81 | } 82 | }; 83 | static const vtable vtbl{s::op, s::submit}; 84 | new (&data_.buffer_) Wrapped(std::move(obj)); 85 | vptr_ = &vtbl; 86 | } 87 | template > 88 | using wrapped_t = 89 | std::enable_if_t::value, U>; 90 | public: 91 | using properties = property_set, is_flow<>, is_single<>>; 92 | 93 | any_flow_single_sender() = default; 94 | any_flow_single_sender(any_flow_single_sender&& that) noexcept 95 | : any_flow_single_sender() { 96 | that.vptr_->op_(that.data_, &data_); 97 | std::swap(that.vptr_, vptr_); 98 | } 99 | PUSHMI_TEMPLATE (class Wrapped) 100 | (requires FlowSender, is_single<>>) 101 | explicit any_flow_single_sender(Wrapped obj) noexcept(insitu()) 102 | : any_flow_single_sender{std::move(obj), bool_()>{}} {} 103 | ~any_flow_single_sender() { 104 | vptr_->op_(data_, nullptr); 105 | } 106 | any_flow_single_sender& operator=(any_flow_single_sender&& that) noexcept { 107 | this->~any_flow_single_sender(); 108 | new ((void*)this) any_flow_single_sender(std::move(that)); 109 | return *this; 110 | } 111 | PUSHMI_TEMPLATE(class Out) 112 | (requires ReceiveError&& ReceiveValue) // 113 | void submit(Out&& out) { 114 | vptr_->submit_(data_, any_flow_receiver{(Out &&) out}); 115 | } 116 | }; 117 | 118 | // Class static definitions: 119 | template 120 | constexpr typename any_flow_single_sender::vtable const 121 | any_flow_single_sender::noop_; 122 | 123 | template 124 | class flow_single_sender { 125 | SF sf_; 126 | 127 | public: 128 | using properties = property_set, is_flow<>, is_single<>>; 129 | 130 | constexpr flow_single_sender() = default; 131 | constexpr explicit flow_single_sender(SF sf) 132 | : sf_(std::move(sf)) {} 133 | 134 | PUSHMI_TEMPLATE(class Out) 135 | (requires Receiver && Invocable) 136 | void submit(Out out) { 137 | sf_(std::move(out)); 138 | } 139 | }; 140 | 141 | template < 142 | PUSHMI_TYPE_CONSTRAINT(Sender, is_flow<>>) Data, 143 | class DSF> 144 | class flow_single_sender { 145 | Data data_; 146 | DSF sf_; 147 | 148 | public: 149 | using properties = property_set_insert_t< 150 | properties_t, 151 | property_set, is_flow<>, is_single<>>>; 152 | 153 | constexpr flow_single_sender() = default; 154 | constexpr explicit flow_single_sender(Data data) 155 | : data_(std::move(data)) {} 156 | constexpr flow_single_sender(Data data, DSF sf) 157 | : data_(std::move(data)), sf_(std::move(sf)) {} 158 | 159 | PUSHMI_TEMPLATE(class Out) 160 | (requires PUSHMI_EXP(lazy::Receiver PUSHMI_AND 161 | lazy::Invocable)) 162 | void submit(Out out) { 163 | sf_(data_, std::move(out)); 164 | } 165 | }; 166 | 167 | template <> 168 | class flow_single_sender<> 169 | : public flow_single_sender { 170 | public: 171 | flow_single_sender() = default; 172 | }; 173 | 174 | //////////////////////////////////////////////////////////////////////////////// 175 | // make_flow_single_sender 176 | PUSHMI_INLINE_VAR constexpr struct make_flow_single_sender_fn { 177 | inline auto operator()() const { 178 | return flow_single_sender{}; 179 | } 180 | PUSHMI_TEMPLATE(class SF) 181 | (requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender)) 182 | auto operator()(SF sf) const { 183 | return flow_single_sender{std::move(sf)}; 184 | } 185 | PUSHMI_TEMPLATE(class Data) 186 | (requires True<> && Sender, is_flow<>>) 187 | auto operator()(Data d) const { 188 | return flow_single_sender{std::move(d)}; 189 | } 190 | PUSHMI_TEMPLATE(class Data, class DSF) 191 | (requires Sender, is_flow<>>) 192 | auto operator()(Data d, DSF sf) const { 193 | return flow_single_sender{std::move(d), std::move(sf)}; 194 | } 195 | } const make_flow_single_sender {}; 196 | 197 | //////////////////////////////////////////////////////////////////////////////// 198 | // deduction guides 199 | #if __cpp_deduction_guides >= 201703 200 | flow_single_sender() -> flow_single_sender; 201 | 202 | PUSHMI_TEMPLATE(class SF) 203 | (requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender)) 204 | flow_single_sender(SF) -> flow_single_sender; 205 | 206 | PUSHMI_TEMPLATE(class Data) 207 | (requires True<> && Sender, is_flow<>>) 208 | flow_single_sender(Data) -> flow_single_sender; 209 | 210 | PUSHMI_TEMPLATE(class Data, class DSF) 211 | (requires Sender, is_flow<>>) 212 | flow_single_sender(Data, DSF) -> flow_single_sender; 213 | #endif 214 | 215 | template<> 216 | struct construct_deduced 217 | : make_flow_single_sender_fn {}; 218 | 219 | 220 | } // namespace pushmi 221 | -------------------------------------------------------------------------------- /include/pushmi/forwards.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace pushmi { 23 | 24 | // property_set 25 | 26 | template 27 | struct property_traits; 28 | 29 | template 30 | struct property_set_traits; 31 | 32 | template 33 | struct property_set; 34 | 35 | // trait & tag types 36 | template 37 | struct is_single; 38 | template 39 | struct is_many; 40 | 41 | template 42 | struct is_flow; 43 | 44 | template 45 | struct is_receiver; 46 | 47 | template 48 | struct is_sender; 49 | 50 | template 51 | struct is_executor; 52 | 53 | template 54 | struct is_time; 55 | template 56 | struct is_constrained; 57 | 58 | template 59 | struct is_always_blocking; 60 | 61 | template 62 | struct is_never_blocking; 63 | 64 | template 65 | struct is_maybe_blocking; 66 | 67 | template 68 | struct is_fifo_sequence; 69 | 70 | template 71 | struct is_concurrent_sequence; 72 | 73 | // implementation types 74 | 75 | template 76 | class executor; 77 | 78 | template 79 | class constrained_executor; 80 | 81 | template 82 | class time_executor; 83 | 84 | template 85 | class receiver; 86 | 87 | template 88 | class flow_receiver; 89 | 90 | template 91 | class single_sender; 92 | 93 | template 94 | class many_sender; 95 | 96 | template 97 | class flow_single_sender; 98 | 99 | template 100 | class flow_many_sender; 101 | 102 | template 103 | class any_receiver; 104 | 105 | template < 106 | class PE = std::exception_ptr, 107 | class PV = std::ptrdiff_t, 108 | class E = PE, 109 | class... VN> 110 | class any_flow_receiver; 111 | 112 | template 113 | class any_single_sender; 114 | 115 | template 116 | class any_many_sender; 117 | 118 | template 119 | class any_flow_single_sender; 120 | 121 | template < 122 | class PE = std::exception_ptr, 123 | class PV = std::ptrdiff_t, 124 | class E = PE, 125 | class... VN> 126 | class any_flow_many_sender; 127 | 128 | template 129 | class any_executor; 130 | 131 | template 132 | struct any_executor_ref; 133 | 134 | template 135 | class any_constrained_executor; 136 | 137 | template 138 | struct any_constrained_executor_ref; 139 | 140 | template < 141 | class E = std::exception_ptr, 142 | class TP = std::chrono::system_clock::time_point, 143 | class... VN> 144 | class any_time_executor; 145 | 146 | template < 147 | class E = std::exception_ptr, 148 | class TP = std::chrono::system_clock::time_point, 149 | class... VN> 150 | struct any_time_executor_ref; 151 | 152 | namespace operators {} 153 | namespace extension_operators {} 154 | namespace aliases { 155 | namespace v = ::pushmi; 156 | namespace mi = ::pushmi; 157 | namespace op = ::pushmi::operators; 158 | namespace ep = ::pushmi::extension_operators; 159 | } // namespace aliases 160 | 161 | namespace detail { 162 | struct any { 163 | template 164 | constexpr any(T&&) noexcept {} 165 | }; 166 | } // namespace detail 167 | 168 | } // namespace pushmi 169 | -------------------------------------------------------------------------------- /include/pushmi/inline.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | namespace pushmi { 21 | 22 | class inline_constrained_executor_t { 23 | public: 24 | using properties = property_set< 25 | is_constrained<>, 26 | is_fifo_sequence<>>; 27 | 28 | template 29 | struct task { 30 | CV cv_; 31 | using properties = property_set< 32 | is_sender<>, 33 | is_always_blocking<>, 34 | is_single<>>; 35 | 36 | PUSHMI_TEMPLATE(class Out) 37 | (requires Receiver) // 38 | void submit(Out out) && { 39 | set_value(out, inline_constrained_executor_t{}); 40 | set_done(out); 41 | } 42 | }; 43 | auto top() { 44 | return 0; 45 | } 46 | task schedule() { 47 | return {top()}; 48 | } 49 | template 50 | task schedule(CV cv) { 51 | return {cv}; 52 | } 53 | }; 54 | 55 | struct inlineConstrainedEXF { 56 | inline_constrained_executor_t operator()() { 57 | return {}; 58 | } 59 | }; 60 | 61 | inline inline_constrained_executor_t inline_constrained_executor() { 62 | return {}; 63 | } 64 | 65 | class inline_time_executor_t { 66 | public: 67 | using properties = property_set< 68 | is_time<>, 69 | is_fifo_sequence<>>; 70 | 71 | template 72 | struct task { 73 | TP tp_; 74 | using properties = property_set< 75 | is_sender<>, 76 | is_always_blocking<>, 77 | is_single<>>; 78 | 79 | PUSHMI_TEMPLATE(class Out) 80 | (requires Receiver) // 81 | void submit(Out out) && { 82 | std::this_thread::sleep_until(tp_); 83 | set_value(out, inline_time_executor_t{}); 84 | set_done(out); 85 | } 86 | }; 87 | 88 | auto top() { 89 | return std::chrono::system_clock::now(); 90 | } 91 | task schedule() { 92 | return {top()}; 93 | } 94 | template 95 | task schedule(TP tp) { 96 | return {tp}; 97 | } 98 | }; 99 | 100 | struct inlineTimeEXF { 101 | inline_time_executor_t operator()() { 102 | return {}; 103 | } 104 | }; 105 | 106 | inline inline_time_executor_t inline_time_executor() { 107 | return {}; 108 | } 109 | 110 | class inline_executor_t { 111 | public: 112 | using properties = property_set< 113 | is_executor<>, 114 | is_fifo_sequence<>>; 115 | 116 | struct task { 117 | using properties = property_set< 118 | is_sender<>, 119 | is_always_blocking<>, 120 | is_single<>>; 121 | 122 | PUSHMI_TEMPLATE(class Out) 123 | (requires Receiver) // 124 | void submit(Out out) && { 125 | set_value(out, inline_executor_t{}); 126 | set_done(out); 127 | } 128 | }; 129 | 130 | task schedule() { 131 | return {}; 132 | } 133 | }; 134 | 135 | struct inlineEXF { 136 | inline_executor_t operator()() { 137 | return {}; 138 | } 139 | }; 140 | 141 | inline inline_executor_t inline_executor() { 142 | return {}; 143 | } 144 | 145 | } // namespace pushmi 146 | -------------------------------------------------------------------------------- /include/pushmi/many_sender.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace pushmi { 24 | 25 | template 26 | class any_many_sender { 27 | union data { 28 | void* pobj_ = nullptr; 29 | std::aligned_storage_t< 30 | sizeof(std::tuple), alignof(std::tuple)> buffer_; 31 | } data_{}; 32 | template 33 | static constexpr bool insitu() { 34 | return sizeof(Wrapped) <= sizeof(data::buffer_) && 35 | std::is_nothrow_move_constructible::value; 36 | } 37 | struct vtable { 38 | static void s_op(data&, data*) {} 39 | static void s_submit(data&, any_receiver) {} 40 | void (*op_)(data&, data*) = vtable::s_op; 41 | void (*submit_)(data&, any_receiver) = vtable::s_submit; 42 | }; 43 | static constexpr vtable const noop_{}; 44 | vtable const* vptr_ = &noop_; 45 | template 46 | any_many_sender(Wrapped obj, std::false_type) : any_many_sender() { 47 | struct s { 48 | static void op(data& src, data* dst) { 49 | if (dst) 50 | dst->pobj_ = std::exchange(src.pobj_, nullptr); 51 | delete static_cast(src.pobj_); 52 | } 53 | static void submit(data& src, any_receiver out) { 54 | ::pushmi::submit( 55 | *static_cast(src.pobj_), std::move(out)); 56 | } 57 | }; 58 | static const vtable vtbl{s::op, s::submit}; 59 | data_.pobj_ = new Wrapped(std::move(obj)); 60 | vptr_ = &vtbl; 61 | } 62 | template 63 | any_many_sender(Wrapped obj, std::true_type) noexcept : any_many_sender() { 64 | struct s { 65 | static void op(data& src, data* dst) { 66 | if (dst) 67 | new (&dst->buffer_) 68 | Wrapped(std::move(*static_cast((void*)&src.buffer_))); 69 | static_cast((void*)&src.buffer_)->~Wrapped(); 70 | } 71 | static void submit(data& src, any_receiver out) { 72 | ::pushmi::submit( 73 | *static_cast((void*)&src.buffer_), std::move(out)); 74 | } 75 | }; 76 | static const vtable vtbl{s::op, s::submit}; 77 | new (&data_.buffer_) Wrapped(std::move(obj)); 78 | vptr_ = &vtbl; 79 | } 80 | template > 81 | using wrapped_t = 82 | std::enable_if_t::value, U>; 83 | 84 | public: 85 | using properties = property_set, is_many<>>; 86 | 87 | any_many_sender() = default; 88 | any_many_sender(any_many_sender&& that) noexcept : any_many_sender() { 89 | that.vptr_->op_(that.data_, &data_); 90 | std::swap(that.vptr_, vptr_); 91 | } 92 | 93 | PUSHMI_TEMPLATE(class Wrapped) 94 | (requires SenderTo< 95 | wrapped_t, 96 | any_receiver, 97 | is_many<>>) // 98 | explicit any_many_sender(Wrapped obj) // 99 | noexcept(insitu()) 100 | : any_many_sender{std::move(obj), bool_()>{}} {} 101 | ~any_many_sender() { 102 | vptr_->op_(data_, nullptr); 103 | } 104 | any_many_sender& operator=(any_many_sender&& that) noexcept { 105 | this->~any_many_sender(); 106 | new ((void*)this) any_many_sender(std::move(that)); 107 | return *this; 108 | } 109 | PUSHMI_TEMPLATE(class Out) 110 | (requires ReceiveError&& ReceiveValue) // 111 | void submit(Out&& out) { 112 | vptr_->submit_(data_, any_receiver{(Out &&) out}); 113 | } 114 | }; 115 | 116 | // Class static definitions: 117 | template 118 | constexpr typename any_many_sender::vtable const 119 | any_many_sender::noop_; 120 | 121 | template 122 | class many_sender { 123 | SF sf_; 124 | 125 | public: 126 | using properties = property_set, is_many<>>; 127 | 128 | constexpr many_sender() = default; 129 | constexpr explicit many_sender(SF sf) : sf_(std::move(sf)) {} 130 | 131 | PUSHMI_TEMPLATE(class Out) 132 | (requires PUSHMI_EXP( 133 | lazy::Receiver PUSHMI_AND lazy::Invocable)) // 134 | void submit(Out out) { 135 | sf_(std::move(out)); 136 | } 137 | }; 138 | 139 | template >) Data, class DSF> 140 | class many_sender { 141 | Data data_; 142 | DSF sf_; 143 | 144 | public: 145 | using properties = property_set_insert_t< 146 | properties_t, 147 | property_set, is_many<>>>; 148 | 149 | constexpr many_sender() = default; 150 | constexpr explicit many_sender(Data data) : data_(std::move(data)) {} 151 | constexpr many_sender(Data data, DSF sf) 152 | : data_(std::move(data)), sf_(std::move(sf)) {} 153 | 154 | PUSHMI_TEMPLATE(class Out) 155 | (requires PUSHMI_EXP( 156 | lazy::Receiver PUSHMI_AND lazy::Invocable)) // 157 | void submit(Out out) & { 158 | sf_(data_, std::move(out)); 159 | } 160 | PUSHMI_TEMPLATE(class Out) 161 | (requires PUSHMI_EXP( 162 | lazy::Receiver PUSHMI_AND lazy::Invocable)) // 163 | void submit(Out out) && { 164 | sf_(std::move(data_), std::move(out)); 165 | } 166 | }; 167 | 168 | template <> 169 | class many_sender<> : public many_sender { 170 | public: 171 | many_sender() = default; 172 | }; 173 | 174 | //////////////////////////////////////////////////////////////////////////////// 175 | // make_many_sender 176 | PUSHMI_INLINE_VAR constexpr struct make_many_sender_fn { 177 | inline auto operator()() const { 178 | return many_sender{}; 179 | } 180 | PUSHMI_TEMPLATE(class SF) 181 | (requires True<> PUSHMI_BROKEN_SUBSUMPTION(&¬ Sender)) // 182 | auto 183 | operator()(SF sf) const { 184 | return many_sender{std::move(sf)}; 185 | } 186 | PUSHMI_TEMPLATE(class Data) 187 | (requires True<>&& Sender>) // 188 | auto 189 | operator()(Data d) const { 190 | return many_sender{std::move(d)}; 191 | } 192 | PUSHMI_TEMPLATE(class Data, class DSF) 193 | (requires Sender>) // 194 | auto 195 | operator()(Data d, DSF sf) const { 196 | return many_sender{std::move(d), std::move(sf)}; 197 | } 198 | } const make_many_sender{}; 199 | 200 | //////////////////////////////////////////////////////////////////////////////// 201 | // deduction guides 202 | #if __cpp_deduction_guides >= 201703 203 | many_sender()->many_sender; 204 | 205 | PUSHMI_TEMPLATE(class SF) 206 | (requires True<> PUSHMI_BROKEN_SUBSUMPTION(&¬ Sender)) // 207 | many_sender(SF) 208 | ->many_sender; 209 | 210 | PUSHMI_TEMPLATE(class Data) 211 | (requires True<>&& Sender>) // 212 | many_sender(Data) 213 | ->many_sender; 214 | 215 | PUSHMI_TEMPLATE(class Data, class DSF) 216 | (requires Sender>) // 217 | many_sender(Data, DSF) 218 | ->many_sender; 219 | #endif 220 | 221 | template <> 222 | struct construct_deduced : make_many_sender_fn {}; 223 | 224 | } // namespace pushmi 225 | -------------------------------------------------------------------------------- /include/pushmi/new_thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | 23 | // very poor perf example executor. 24 | // 25 | 26 | struct new_thread_executor; 27 | 28 | struct new_thread_task { 29 | using properties = property_set< 30 | is_sender<>, 31 | is_never_blocking<>, 32 | is_single<>>; 33 | 34 | PUSHMI_TEMPLATE(class Out) 35 | (requires Receiver) 36 | void submit(Out out) && { 37 | std::thread t{[out = std::move(out)]() mutable { 38 | auto tr = ::pushmi::trampoline(); 39 | ::pushmi::submit(::pushmi::schedule(tr), std::move(out)); 40 | }}; 41 | // pass ownership of thread to out 42 | t.detach(); 43 | } 44 | }; 45 | 46 | struct new_thread_executor { 47 | using properties = property_set, is_concurrent_sequence<>>; 48 | 49 | inline new_thread_task schedule() { 50 | return {}; 51 | } 52 | }; 53 | 54 | 55 | inline new_thread_executor new_thread() { 56 | return {}; 57 | } 58 | 59 | } // namespace pushmi 60 | -------------------------------------------------------------------------------- /include/pushmi/o/defer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace pushmi { 24 | 25 | namespace operators { 26 | 27 | PUSHMI_INLINE_VAR constexpr struct defer_fn { 28 | private: 29 | template 30 | struct impl { 31 | F f_; 32 | PUSHMI_TEMPLATE(class Data, class Out) 33 | (requires Receiver) 34 | void operator()(Data&, Out out) { 35 | auto sender = f_(); 36 | submit(sender, std::move(out)); 37 | } 38 | }; 39 | 40 | public: 41 | PUSHMI_TEMPLATE(class F) 42 | (requires Invocable) 43 | auto operator()(F f) const { 44 | struct sender_base : single_sender<> { 45 | using properties = properties_t>; 46 | }; 47 | return make_single_sender(sender_base{}, impl{std::move(f)}); 48 | } 49 | } defer{}; 50 | 51 | } // namespace operators 52 | 53 | } // namespace pushmi 54 | -------------------------------------------------------------------------------- /include/pushmi/o/empty.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace pushmi { 23 | namespace detail { 24 | struct single_empty_impl : pipeorigin { 25 | using properties = property_set< 26 | is_sender<>, 27 | is_single<>, 28 | is_always_blocking<>>; 29 | 30 | PUSHMI_TEMPLATE(class Out) 31 | (requires Receiver&& Invocable) // 32 | void submit(Out&& out) { 33 | set_done(out); 34 | } 35 | }; 36 | } // namespace detail 37 | 38 | namespace operators { 39 | inline detail::single_empty_impl empty() { 40 | return {}; 41 | } 42 | 43 | } // namespace operators 44 | } // namespace pushmi 45 | -------------------------------------------------------------------------------- /include/pushmi/o/error.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | namespace detail { 23 | struct single_error_sender_base : single_sender { 24 | using properties = property_set< 25 | is_sender<>, 26 | is_single<>, 27 | is_always_blocking<>>; 28 | }; 29 | template 30 | struct single_error_impl { 31 | E e_; 32 | PUSHMI_TEMPLATE(class Base, class Out) 33 | (requires ReceiveError) 34 | void operator()( 35 | Base&&, 36 | Out&& out) const & { 37 | set_error(out, e_); 38 | } 39 | PUSHMI_TEMPLATE(class Base, class Out) 40 | (requires ReceiveError) 41 | void operator()( 42 | Base&&, 43 | Out&& out) && { 44 | set_error(out, std::move(e_)); 45 | } 46 | }; 47 | } // namespace detail 48 | 49 | namespace operators { 50 | 51 | PUSHMI_TEMPLATE(class E) 52 | (requires MoveConstructible) 53 | auto error(E e) { 54 | return make_single_sender( 55 | detail::single_error_sender_base{}, 56 | detail::single_error_impl{std::move(e)}); 57 | } 58 | 59 | } // namespace operators 60 | } // namespace pushmi 61 | -------------------------------------------------------------------------------- /include/pushmi/o/filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | 23 | namespace detail { 24 | 25 | struct filter_fn { 26 | private: 27 | template 28 | struct on_value_impl { 29 | Predicate p_; 30 | PUSHMI_TEMPLATE(class Out, class... VN) 31 | (requires Receiver) 32 | void operator()(Out& out, VN&&... vn) const { 33 | if (p_(as_const(vn)...)) { 34 | set_value(out, (VN &&) vn...); 35 | } 36 | } 37 | }; 38 | template 39 | struct submit_impl { 40 | Predicate p_; 41 | PUSHMI_TEMPLATE(class SIn, class Out) 42 | (requires Receiver) 43 | auto operator()(SIn&& in, Out out) const { 44 | submit((In&&)in, ::pushmi::detail::receiver_from_fn()( 45 | std::move(out), 46 | // copy 'p' to allow multiple calls to submit 47 | on_value_impl{p_})); 48 | } 49 | }; 50 | template 51 | struct adapt_impl { 52 | Predicate p_; 53 | PUSHMI_TEMPLATE(class In) 54 | (requires Sender) 55 | auto operator()(In in) const { 56 | return ::pushmi::detail::sender_from( 57 | std::move(in), 58 | submit_impl{p_}); 59 | } 60 | }; 61 | 62 | public: 63 | PUSHMI_TEMPLATE(class Predicate) 64 | (requires SemiMovable) 65 | auto operator()(Predicate p) const { 66 | return adapt_impl{std::move(p)}; 67 | } 68 | }; 69 | 70 | } // namespace detail 71 | 72 | namespace operators { 73 | PUSHMI_INLINE_VAR constexpr detail::filter_fn filter{}; 74 | } // namespace operators 75 | 76 | } // namespace pushmi 77 | -------------------------------------------------------------------------------- /include/pushmi/o/for_each.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | namespace detail { 23 | 24 | struct for_each_fn { 25 | private: 26 | template 27 | struct subset { 28 | using properties = property_set; 29 | }; 30 | template 31 | struct request_fn { 32 | Up up_; 33 | explicit request_fn(Up up) : up_(std::move(up)) {} 34 | // workaround for std::function::swap 35 | request_fn(const request_fn& o) : up_(std::move(const_cast(o).up_)) {} 36 | request_fn(request_fn&& o) : up_(std::move(o.up_)) {} 37 | void operator()(std::ptrdiff_t requested) { 38 | ::pushmi::set_value(up_, requested); 39 | } 40 | }; 41 | template 42 | struct Pull { 43 | Out out_; 44 | explicit Pull(Out out) : out_(std::move(out)) {} 45 | using properties = 46 | property_set_insert_t, property_set>>; 47 | std::function pull; 48 | template 49 | void value(VN&&... vn) { 50 | ::pushmi::set_value(out_, (VN &&) vn...); 51 | pull(1); 52 | } 53 | template 54 | void error(E&& e) noexcept { 55 | // break circular reference 56 | pull = nullptr; 57 | ::pushmi::set_error(out_, (E &&) e); 58 | } 59 | void done() { 60 | // break circular reference 61 | pull = nullptr; 62 | ::pushmi::set_done(out_); 63 | } 64 | PUSHMI_TEMPLATE(class Up) 65 | (requires ReceiveValue) 66 | void starting(Up up) { 67 | pull = request_fn{std::move(up)}; 68 | pull(1); 69 | } 70 | PUSHMI_TEMPLATE(class Up) 71 | (requires ReceiveValue && not ReceiveValue) 72 | void starting(Up) {} 73 | }; 74 | template 75 | struct fn { 76 | std::tuple args_; 77 | PUSHMI_TEMPLATE(class In) 78 | (requires Sender&& Flow&& Many) 79 | In operator()(In in) { 80 | auto out{::pushmi::detail::receiver_from_fn, 82 | property_set_index_t, is_single<>>>>()( 83 | std::move(args_))}; 84 | using Out = decltype(out); 85 | ::pushmi::submit( 86 | in, 87 | ::pushmi::detail::receiver_from_fn()( 88 | Pull{std::move(out)})); 89 | return in; 90 | } 91 | }; 92 | 93 | public: 94 | template 95 | auto operator()(AN&&... an) const { 96 | return for_each_fn::fn{std::tuple{(AN &&) an...}}; 97 | } 98 | }; 99 | 100 | } // namespace detail 101 | 102 | namespace operators { 103 | PUSHMI_INLINE_VAR constexpr detail::for_each_fn for_each{}; 104 | } // namespace operators 105 | 106 | } // namespace pushmi 107 | -------------------------------------------------------------------------------- /include/pushmi/o/from.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace pushmi { 25 | 26 | PUSHMI_CONCEPT_DEF( 27 | template(class R) // 28 | concept Range, // 29 | requires(R&& r)( // 30 | implicitly_convertible_to(std::begin(r) == std::end(r)))); 31 | 32 | namespace operators { 33 | 34 | PUSHMI_INLINE_VAR constexpr struct from_fn { 35 | private: 36 | struct sender_base : many_sender<> { 37 | using properties = property_set< 38 | is_sender<>, 39 | is_many<>, 40 | is_always_blocking<>>; 41 | }; 42 | template 43 | struct out_impl { 44 | I begin_; 45 | S end_; 46 | PUSHMI_TEMPLATE(class In, class Out) 47 | (requires ReceiveValue< 48 | Out, 49 | typename std::iterator_traits::value_type>) // 50 | void 51 | operator()(In&&, Out out) const { 52 | auto c = begin_; 53 | for (; c != end_; ++c) { 54 | set_value(out, *c); 55 | } 56 | set_done(out); 57 | } 58 | }; 59 | 60 | public: 61 | PUSHMI_TEMPLATE(class I, class S) 62 | (requires DerivedFrom< 63 | typename std::iterator_traits::iterator_category, 64 | std::forward_iterator_tag>) // 65 | auto 66 | operator()(I begin, S end) const { 67 | return make_many_sender(sender_base{}, out_impl{begin, end}); 68 | } 69 | 70 | PUSHMI_TEMPLATE(class R) 71 | (requires Range) // 72 | auto 73 | operator()(R&& range) const { 74 | return (*this)(std::begin(range), std::end(range)); 75 | } 76 | } from{}; 77 | 78 | template 79 | struct flow_from_producer { 80 | flow_from_producer(I begin, S end_, Out out_, Exec exec_, bool s) 81 | : c(begin), 82 | end(end_), 83 | out(std::move(out_)), 84 | exec(std::move(exec_)), 85 | stop(s) {} 86 | I c; 87 | S end; 88 | Out out; 89 | Exec exec; 90 | std::atomic stop; 91 | }; 92 | 93 | template 94 | struct flow_from_up { 95 | using properties = properties_t>; 96 | 97 | explicit flow_from_up(std::shared_ptr p_) : p(std::move(p_)) {} 98 | std::shared_ptr p; 99 | 100 | void value(std::ptrdiff_t requested) { 101 | if (requested < 1) { 102 | return; 103 | } 104 | // submit work to exec 105 | ::pushmi::submit( 106 | ::pushmi::schedule(p->exec), make_receiver([p = p, requested](auto) { 107 | auto remaining = requested; 108 | // this loop is structured to work when there is 109 | // re-entrancy out.value in the loop may call up.value. 110 | // to handle this the state of p->c must be captured and 111 | // the remaining and p->c must be changed before 112 | // out.value is called. 113 | while (remaining-- > 0 && !p->stop && p->c != p->end) { 114 | auto i = (p->c)++; 115 | set_value(p->out, ::pushmi::detail::as_const(*i)); 116 | } 117 | if (p->c == p->end) { 118 | set_done(p->out); 119 | } 120 | })); 121 | } 122 | 123 | template 124 | void error(E) noexcept { 125 | p->stop.store(true); 126 | ::pushmi::submit( 127 | ::pushmi::schedule(p->exec), make_receiver([p = p](auto) { set_done(p->out); })); 128 | } 129 | 130 | void done() { 131 | p->stop.store(true); 132 | ::pushmi::submit( 133 | ::pushmi::schedule(p->exec), make_receiver([p = p](auto) { set_done(p->out); })); 134 | } 135 | }; 136 | 137 | PUSHMI_INLINE_VAR constexpr struct flow_from_fn { 138 | private: 139 | template 140 | struct out_impl { 141 | I begin_; 142 | S end_; 143 | Exec exec_; 144 | PUSHMI_TEMPLATE(class Out) 145 | (requires ReceiveValue< 146 | Out, 147 | typename std::iterator_traits::value_type>) // 148 | void 149 | operator()(Out out) { 150 | using Producer = flow_from_producer; 151 | auto p = std::make_shared( 152 | begin_, end_, std::move(out), exec_, false); 153 | 154 | ::pushmi::submit( 155 | ::pushmi::schedule(exec_), make_receiver([p](auto) { 156 | // pass reference for cancellation. 157 | set_starting(p->out, make_receiver(flow_from_up{p})); 158 | })); 159 | } 160 | }; 161 | 162 | public: 163 | PUSHMI_TEMPLATE(class I, class S) 164 | (requires DerivedFrom< 165 | typename std::iterator_traits::iterator_category, 166 | std::forward_iterator_tag>) // 167 | auto 168 | operator()(I begin, S end) const { 169 | return (*this)(begin, end, trampoline()); 170 | } 171 | 172 | PUSHMI_TEMPLATE(class R) 173 | (requires Range) // 174 | auto 175 | operator()(R&& range) const { 176 | return (*this)(std::begin(range), std::end(range), trampoline()); 177 | } 178 | 179 | PUSHMI_TEMPLATE(class I, class S, class Exec) 180 | (requires DerivedFrom< 181 | typename std::iterator_traits::iterator_category, 182 | std::forward_iterator_tag>&& Executor) // 183 | auto 184 | operator()(I begin, S end, Exec exec) const { 185 | return make_flow_many_sender(out_impl{begin, end, exec}); 186 | } 187 | 188 | PUSHMI_TEMPLATE(class R, class Exec) 189 | (requires Range&& Executor) // 190 | auto 191 | operator()(R&& range, Exec exec) const { 192 | return (*this)(std::begin(range), std::end(range), exec); 193 | } 194 | } flow_from{}; 195 | 196 | } // namespace operators 197 | 198 | } // namespace pushmi 199 | -------------------------------------------------------------------------------- /include/pushmi/o/just.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace pushmi { 23 | 24 | namespace operators { 25 | 26 | PUSHMI_INLINE_VAR constexpr struct just_fn { 27 | private: 28 | struct sender_base : single_sender<> { 29 | using properties = property_set< 30 | is_sender<>, 31 | is_single<>, 32 | is_always_blocking<>>; 33 | }; 34 | template 35 | struct impl { 36 | std::tuple vn_; 37 | PUSHMI_TEMPLATE(class In, class Out) 38 | (requires ReceiveValue) // 39 | void 40 | operator()(In&&, Out&& out) { 41 | ::pushmi::apply( 42 | ::pushmi::set_value, 43 | std::tuple_cat(std::tuple{out}, std::move(vn_))); 44 | set_done(out); 45 | } 46 | }; 47 | 48 | public: 49 | PUSHMI_TEMPLATE(class... VN) 50 | (requires And...>) // 51 | auto 52 | operator()(VN... vn) const { 53 | return make_single_sender( 54 | sender_base{}, impl{std::tuple{std::move(vn)...}}); 55 | } 56 | } just{}; 57 | } // namespace operators 58 | 59 | } // namespace pushmi 60 | -------------------------------------------------------------------------------- /include/pushmi/o/on.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace pushmi { 23 | 24 | namespace detail { 25 | 26 | struct on_fn { 27 | private: 28 | template 29 | struct on_value_impl { 30 | In in_; 31 | Out out_; 32 | void operator()(any) { 33 | submit(in_, std::move(out_)); 34 | } 35 | }; 36 | template 37 | struct submit_impl { 38 | Factory ef_; 39 | PUSHMI_TEMPLATE(class In, class Out) 40 | (requires SenderTo) // 41 | void 42 | operator()(In&& in, Out out) const { 43 | auto exec = ::pushmi::make_strand(ef_); 44 | submit( 45 | ::pushmi::schedule(exec), 46 | ::pushmi::make_receiver(on_value_impl, Out>{ 47 | (In&&) in, std::move(out)})); 48 | } 49 | }; 50 | template 51 | struct adapt_impl { 52 | Factory ef_; 53 | PUSHMI_TEMPLATE(class In) 54 | (requires Sender>) // 55 | auto 56 | operator()(In&& in) const { 57 | return ::pushmi::detail::sender_from( 58 | (In&&) in, submit_impl{ef_}); 59 | } 60 | }; 61 | 62 | public: 63 | PUSHMI_TEMPLATE(class Factory) 64 | (requires StrandFactory) // 65 | auto 66 | operator()(Factory ef) const { 67 | return adapt_impl{std::move(ef)}; 68 | } 69 | }; 70 | 71 | } // namespace detail 72 | 73 | namespace operators { 74 | 75 | PUSHMI_INLINE_VAR constexpr detail::on_fn on{}; 76 | 77 | } // namespace operators 78 | 79 | } // namespace pushmi 80 | -------------------------------------------------------------------------------- /include/pushmi/o/request_via.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace pushmi { 24 | 25 | template 26 | struct send_via { 27 | In in; 28 | PUSHMI_TEMPLATE(class... AN) 29 | (requires Invocable&& 30 | Invocable< 31 | invoke_result_t, 32 | In>) 33 | auto via(AN&&... an) { 34 | return in | ::pushmi::operators::via((AN &&) an...); 35 | } 36 | }; 37 | 38 | namespace detail { 39 | 40 | struct request_via_fn { 41 | private: 42 | struct impl { 43 | PUSHMI_TEMPLATE(class In) 44 | (requires Sender) 45 | auto operator()(In in) const { 46 | return send_via{in}; 47 | } 48 | }; 49 | 50 | public: 51 | inline auto operator()() const { 52 | return impl{}; 53 | } 54 | }; 55 | 56 | } // namespace detail 57 | 58 | namespace operators { 59 | 60 | PUSHMI_INLINE_VAR constexpr detail::request_via_fn request_via{}; 61 | 62 | } // namespace operators 63 | 64 | PUSHMI_TEMPLATE(class To, class In) 65 | (requires Same>&& Sender) 66 | auto via_cast(In in) { 67 | return in; 68 | } 69 | 70 | PUSHMI_TEMPLATE(class To, class In) 71 | (requires Same>) 72 | auto via_cast(send_via ss) { 73 | return ss.in; 74 | } 75 | 76 | } // namespace pushmi 77 | -------------------------------------------------------------------------------- /include/pushmi/o/schedule.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace pushmi { 25 | namespace detail { 26 | 27 | struct schedule_fn { 28 | private: 29 | // TODO - only move, move-only types.. 30 | // if out can be copied, then schedule can be called multiple 31 | // times.. 32 | struct fn { 33 | PUSHMI_TEMPLATE(class Exec) 34 | (requires Executor) // 35 | auto 36 | operator()(Exec& ex) { 37 | return schedule(ex); 38 | } 39 | }; 40 | 41 | public: 42 | auto operator()() const { 43 | return schedule_fn::fn{}; 44 | } 45 | }; 46 | 47 | struct schedule_at_fn { 48 | private: 49 | // TODO - only move, move-only types.. 50 | // if out can be copied, then schedule can be called multiple 51 | // times.. 52 | template 53 | struct fn { 54 | CV at_; 55 | PUSHMI_TEMPLATE(class Exec) 56 | (requires ConstrainedExecutor) // 57 | auto 58 | operator()(Exec& ex) { 59 | return schedule(ex, std::move(at_)); 60 | } 61 | }; 62 | 63 | public: 64 | template 65 | auto operator()(CV&& at) const { 66 | return schedule_at_fn::fn{(CV &&) at}; 67 | } 68 | }; 69 | 70 | struct schedule_after_fn { 71 | private: 72 | // TODO - only move, move-only types.. 73 | // if out can be copied, then schedule can be called multiple 74 | // times.. 75 | template 76 | struct fn { 77 | Dur after_; 78 | PUSHMI_TEMPLATE(class Exec) 79 | (requires TimeExecutor) // 80 | auto 81 | operator()(Exec& ex) { 82 | return schedule(ex, now(ex) + after_); 83 | } 84 | }; 85 | 86 | public: 87 | template 88 | auto operator()(Dur&& after) const { 89 | return schedule_after_fn::fn{(Dur &&) after}; 90 | } 91 | }; 92 | 93 | } // namespace detail 94 | 95 | namespace operators { 96 | PUSHMI_INLINE_VAR constexpr detail::schedule_fn schedule{}; 97 | PUSHMI_INLINE_VAR constexpr detail::schedule_at_fn schedule_at{}; 98 | PUSHMI_INLINE_VAR constexpr detail::schedule_after_fn schedule_after{}; 99 | } // namespace operators 100 | 101 | } // namespace pushmi 102 | -------------------------------------------------------------------------------- /include/pushmi/o/share.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | namespace pushmi { 25 | 26 | namespace detail { 27 | 28 | template 29 | struct share_fn { 30 | private: 31 | struct impl { 32 | PUSHMI_TEMPLATE(class In) 33 | (requires Sender) 34 | auto operator()(In in) const { 35 | subject, TN...> sub; 36 | submit(in, sub.receiver()); 37 | return sub; 38 | } 39 | }; 40 | 41 | public: 42 | auto operator()() const { 43 | return impl{}; 44 | } 45 | }; 46 | 47 | } // namespace detail 48 | 49 | namespace operators { 50 | 51 | template 52 | PUSHMI_INLINE_VAR constexpr detail::share_fn share{}; 53 | 54 | } // namespace operators 55 | 56 | } // namespace pushmi 57 | -------------------------------------------------------------------------------- /include/pushmi/o/switch_on_error.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | 23 | namespace detail { 24 | 25 | struct switch_on_error_fn { 26 | private: 27 | template 28 | struct on_error_impl { 29 | ErrorSelector es_; 30 | PUSHMI_TEMPLATE(class Out, class E) 31 | (requires Receiver&& Invocable&& 32 | SenderTo<::pushmi::invoke_result_t, Out>) 33 | void operator()(Out& out, E&& e) noexcept { 34 | static_assert( 35 | ::pushmi::NothrowInvocable, 36 | "switch_on_error - error selector function must be noexcept"); 37 | submit(es_((E &&) e), out); 38 | } 39 | }; 40 | template 41 | struct out_impl { 42 | ErrorSelector es_; 43 | PUSHMI_TEMPLATE(class SIn, class Out) 44 | (requires Receiver>) 45 | auto operator()(SIn&& in, Out&& out) const { 46 | submit((In&&)in, ::pushmi::detail::receiver_from_fn()( 47 | (Out&&)out, 48 | // copy 'es' to allow multiple calls to submit 49 | ::pushmi::on_error(on_error_impl{es_}))); 50 | } 51 | }; 52 | template 53 | struct in_impl { 54 | ErrorSelector es_; 55 | PUSHMI_TEMPLATE(class In) 56 | (requires Sender) 57 | auto operator()(In&& in) { 58 | return ::pushmi::detail::sender_from( 59 | (In&&)in, 60 | out_impl{std::move(es_)}); 61 | } 62 | }; 63 | 64 | public: 65 | PUSHMI_TEMPLATE(class ErrorSelector) 66 | (requires SemiMovable) 67 | auto operator()(ErrorSelector es) const { 68 | return in_impl{std::move(es)}; 69 | } 70 | }; 71 | 72 | } // namespace detail 73 | 74 | namespace operators { 75 | PUSHMI_INLINE_VAR constexpr detail::switch_on_error_fn switch_on_error{}; 76 | } // namespace operators 77 | 78 | } // namespace pushmi 79 | -------------------------------------------------------------------------------- /include/pushmi/o/tap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | 23 | namespace detail { 24 | 25 | PUSHMI_TEMPLATE(class SideEffects, class Out) 26 | (requires Receiver&& Receiver)struct tap_ { 27 | SideEffects sideEffects; 28 | Out out; 29 | 30 | // side effect has no effect on the properties. 31 | using properties = properties_t; 32 | 33 | PUSHMI_TEMPLATE(class... VN) 34 | (requires ReceiveValue&...>&& 35 | ReceiveValue) // 36 | void value(VN&&... vn) { 37 | set_value(sideEffects, as_const(vn)...); 38 | set_value(out, (VN &&) vn...); 39 | } 40 | PUSHMI_TEMPLATE(class E) 41 | (requires ReceiveError&& 42 | ReceiveError) // 43 | void error(E&& e) noexcept { 44 | set_error(sideEffects, as_const(e)); 45 | set_error(out, (E&&) e); 46 | } 47 | void done() { 48 | set_done(sideEffects); 49 | set_done(out); 50 | } 51 | PUSHMI_TEMPLATE(class Up, class UUp = std::remove_reference_t) 52 | (requires FlowReceiver&& FlowReceiver) // 53 | void starting( 54 | Up&& up) { 55 | // up is not made const because sideEffects is allowed to call methods on up 56 | set_starting(sideEffects, static_cast(up)); 57 | set_starting(out, (Up &&) up); 58 | } 59 | }; 60 | 61 | PUSHMI_INLINE_VAR constexpr struct make_tap_fn { 62 | PUSHMI_TEMPLATE(class SideEffects, class Out) 63 | (requires Receiver>&& Receiver>&& 64 | Receiver, std::decay_t>>) // 65 | auto operator()(const SideEffects& se, Out&& out) const { 66 | return tap_, std::decay_t>{se, (Out &&) out}; 67 | } 68 | } const make_tap{}; 69 | 70 | struct tap_fn { 71 | private: 72 | PUSHMI_TEMPLATE(class In, class SideEffects) 73 | (requires SenderTo) // 74 | static auto impl( 75 | In&& in, 76 | SideEffects&& sideEffects) { 77 | return ::pushmi::detail::sender_from( 78 | (In &&) in, 79 | submit_impl>{(SideEffects &&) 80 | sideEffects}); 81 | } 82 | 83 | template 84 | struct adapt_impl { 85 | std::tuple args_; 86 | PUSHMI_TEMPLATE(class In) 87 | (requires Sender>) // 88 | auto operator()(In&& in) { 89 | return tap_fn::impl( 90 | (In &&) in, 91 | ::pushmi::detail::receiver_from_fn()(std::move(args_))); 92 | } 93 | }; 94 | 95 | PUSHMI_TEMPLATE(class In, class SideEffects) 96 | (requires Sender>&& 97 | Receiver) // 98 | struct submit_impl { 99 | SideEffects sideEffects_; 100 | template 101 | using tap_t = decltype(detail::make_tap( 102 | std::declval(), 103 | std::declval())); 104 | template 105 | using receiver_t = 106 | invoke_result_t>, tap_t>; 107 | PUSHMI_TEMPLATE(class Data, class Out) 108 | (requires Receiver> 109 | && SenderTo&& 110 | SenderTo>) // 111 | auto operator()(Data&& in, Out&& out) const { 112 | auto gang{::pushmi::detail::receiver_from_fn>()( 113 | detail::make_tap(sideEffects_, (Out &&) out))}; 114 | submit((In &&) in, std::move(gang)); 115 | } 116 | }; 117 | 118 | public: 119 | template 120 | auto operator()(AN... an) const { 121 | return adapt_impl{std::tuple{std::move(an)...}}; 122 | } 123 | }; 124 | 125 | } // namespace detail 126 | 127 | namespace operators { 128 | PUSHMI_INLINE_VAR constexpr detail::tap_fn tap{}; 129 | } // namespace operators 130 | 131 | } // namespace pushmi 132 | -------------------------------------------------------------------------------- /include/pushmi/o/transform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace pushmi { 24 | 25 | namespace detail { 26 | 27 | template 28 | struct transform_on; 29 | 30 | template 31 | struct transform_on> { 32 | F f_; 33 | transform_on() = default; 34 | constexpr explicit transform_on(F f) : f_(std::move(f)) {} 35 | struct value_fn { 36 | F f_; 37 | value_fn() = default; 38 | constexpr explicit value_fn(F f) : f_(std::move(f)) {} 39 | template 40 | auto operator()(Out& out, V0&& v0, VN&&... vn) { 41 | using Result = ::pushmi::invoke_result_t; 42 | static_assert( 43 | ::pushmi::SemiMovable, 44 | "none of the functions supplied to transform can convert this value"); 45 | static_assert( 46 | ::pushmi::ReceiveValue, 47 | "Result of value transform cannot be delivered to Out"); 48 | set_value(out, f_((V0 &&) v0, (VN &&) vn...)); 49 | } 50 | }; 51 | template 52 | auto operator()(Out out) const { 53 | return ::pushmi::make_receiver(std::move(out), value_fn{f_}); 54 | } 55 | }; 56 | 57 | template 58 | struct transform_on, true> { 59 | F f_; 60 | transform_on() = default; 61 | constexpr explicit transform_on(F f) : f_(std::move(f)) {} 62 | template 63 | auto operator()(Out out) const { 64 | return make_flow_single(std::move(out), on_value(*this)); 65 | } 66 | template 67 | auto operator()(Out& out, V0&& v0, VN&&... vn) { 68 | using Result = ::pushmi::invoke_result_t; 69 | static_assert( 70 | ::pushmi::SemiMovable, 71 | "none of the functions supplied to transform can convert this value"); 72 | static_assert( 73 | ::pushmi::Flow && 74 | ::pushmi::ReceiveValue, 75 | "Result of value transform cannot be delivered to Out"); 76 | set_value(out, f_((V0 &&) v0, (VN &&) vn...)); 77 | } 78 | }; 79 | 80 | template 81 | struct transform_on> { 82 | F f_; 83 | transform_on() = default; 84 | constexpr explicit transform_on(F f) : f_(std::move(f)) {} 85 | template 86 | auto operator()(Out out) const { 87 | return ::pushmi::make_receiver(std::move(out), on_value(*this)); 88 | } 89 | template 90 | auto operator()(Out& out, V0&& v0, VN&&... vn) { 91 | using Result = ::pushmi::invoke_result_t; 92 | static_assert( 93 | ::pushmi::SemiMovable, 94 | "none of the functions supplied to transform can convert this value"); 95 | static_assert( 96 | ::pushmi::ReceiveValue, 97 | "Result of value transform cannot be delivered to Out"); 98 | set_value(out, f_((V0 &&) v0, (VN &&) vn...)); 99 | } 100 | }; 101 | 102 | template 103 | struct transform_on, true> { 104 | F f_; 105 | transform_on() = default; 106 | constexpr explicit transform_on(F f) : f_(std::move(f)) {} 107 | template 108 | auto operator()(Out out) const { 109 | return make_flow_receiver(std::move(out), on_value(*this)); 110 | } 111 | template 112 | void operator()(Out& out, V0&& v0, VN&&... vn) { 113 | using Result = ::pushmi::invoke_result_t; 114 | static_assert( 115 | ::pushmi::SemiMovable, 116 | "none of the functions supplied to transform can convert this value"); 117 | static_assert( 118 | ::pushmi::Flow && 119 | ::pushmi::ReceiveValue, 120 | "Result of value transform cannot be delivered to Out"); 121 | set_value(out, f_((V0 &&) v0, (VN &&) vn...)); 122 | } 123 | }; 124 | 125 | struct transform_fn { 126 | private: 127 | template 128 | struct submit_impl { 129 | F f_; 130 | PUSHMI_TEMPLATE(class SIn, class Out) 131 | (requires Receiver>) // 132 | auto 133 | operator()(SIn&& in, Out&& out) const { 134 | using Cardinality = property_set_index_t, is_single<>>; 135 | // copy 'f_' to allow multiple calls to connect to multiple 'in' 136 | ::pushmi::submit( 137 | (In &&) in, 138 | transform_on< 139 | F, 140 | Cardinality, 141 | property_query_v, is_flow<>>>{f_}((Out &&) out)); 142 | } 143 | }; 144 | 145 | template 146 | struct adapt_impl { 147 | F f_; 148 | PUSHMI_TEMPLATE(class In) 149 | (requires Sender>) // 150 | auto 151 | operator()(In&& in) const { 152 | // copy 'f_' to allow multiple calls to connect to multiple 'in' 153 | return ::pushmi::detail::sender_from( 154 | (In &&) in, submit_impl{f_}); 155 | } 156 | }; 157 | 158 | public: 159 | template 160 | auto operator()(FN... fn) const { 161 | auto f = ::pushmi::overload(std::move(fn)...); 162 | using F = decltype(f); 163 | return adapt_impl{std::move(f)}; 164 | } 165 | }; 166 | 167 | } // namespace detail 168 | 169 | namespace operators { 170 | PUSHMI_INLINE_VAR constexpr detail::transform_fn transform{}; 171 | } // namespace operators 172 | 173 | } // namespace pushmi 174 | -------------------------------------------------------------------------------- /include/pushmi/o/via.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace pushmi { 23 | 24 | namespace detail { 25 | 26 | template 27 | struct via_fn_base { 28 | Exec exec_; 29 | bool done_; 30 | explicit via_fn_base(Exec exec) : exec_(std::move(exec)), done_(false) {} 31 | via_fn_base& via_fn_base_ref() { 32 | return *this; 33 | } 34 | }; 35 | template 36 | struct via_fn_data : flow_receiver<>, via_fn_base { 37 | via_fn_data(Out out, Exec exec) 38 | : via_fn_base(std::move(exec)), out_(std::make_shared(std::move(out))) {} 39 | 40 | using properties = properties_t; 41 | using flow_receiver<>::value; 42 | using flow_receiver<>::error; 43 | using flow_receiver<>::done; 44 | 45 | template 46 | struct impl { 47 | Up up_; 48 | std::shared_ptr out_; 49 | void operator()(any) { 50 | set_starting(out_, std::move(up_)); 51 | } 52 | }; 53 | template 54 | void starting(Up&& up) { 55 | if (this->via_fn_base_ref().done_) { 56 | return; 57 | } 58 | submit( 59 | ::pushmi::schedule(this->via_fn_base_ref().exec_), 60 | ::pushmi::make_receiver(impl>{ 61 | (Up &&) up, out_})); 62 | } 63 | std::shared_ptr out_; 64 | }; 65 | 66 | template 67 | auto make_via_fn_data(Out out, Exec ex) -> via_fn_data { 68 | return {std::move(out), std::move(ex)}; 69 | } 70 | 71 | struct via_fn { 72 | private: 73 | template 74 | struct on_value_impl { 75 | template 76 | struct impl { 77 | V v_; 78 | std::shared_ptr out_; 79 | void operator()(any) { 80 | set_value(out_, std::move(v_)); 81 | } 82 | }; 83 | template 84 | void operator()(Data& data, V&& v) const { 85 | if (data.via_fn_base_ref().done_) { 86 | return; 87 | } 88 | submit( 89 | ::pushmi::schedule(data.via_fn_base_ref().exec_), 90 | ::pushmi::make_receiver(impl>{ 91 | (V &&) v, data.out_})); 92 | } 93 | }; 94 | template 95 | struct on_error_impl { 96 | template 97 | struct impl { 98 | E e_; 99 | std::shared_ptr out_; 100 | void operator()(any) noexcept { 101 | set_error(out_, std::move(e_)); 102 | } 103 | }; 104 | template 105 | void operator()(Data& data, E e) const noexcept { 106 | if (data.via_fn_base_ref().done_) { 107 | return; 108 | } 109 | data.via_fn_base_ref().done_ = true; 110 | submit( 111 | ::pushmi::schedule(data.via_fn_base_ref().exec_), 112 | ::pushmi::make_receiver( 113 | impl{std::move(e), std::move(data.out_)})); 114 | } 115 | }; 116 | template 117 | struct on_done_impl { 118 | struct impl { 119 | std::shared_ptr out_; 120 | void operator()(any) { 121 | set_done(out_); 122 | } 123 | }; 124 | template 125 | void operator()(Data& data) const { 126 | if (data.via_fn_base_ref().done_) { 127 | return; 128 | } 129 | data.via_fn_base_ref().done_ = true; 130 | submit( 131 | ::pushmi::schedule(data.via_fn_base_ref().exec_), 132 | ::pushmi::make_receiver( 133 | impl{std::move(data.out_)})); 134 | } 135 | }; 136 | template 137 | struct submit_impl { 138 | Factory ef_; 139 | PUSHMI_TEMPLATE(class SIn, class Out) 140 | (requires Receiver) // 141 | void 142 | operator()(SIn&& in, Out out) const { 143 | auto exec = ::pushmi::make_strand(ef_); 144 | ::pushmi::submit( 145 | (In &&) in, 146 | ::pushmi::detail::receiver_from_fn>()( 147 | make_via_fn_data(std::move(out), std::move(exec)), 148 | on_value_impl{}, 149 | on_error_impl{}, 150 | on_done_impl{})); 151 | } 152 | }; 153 | template 154 | struct adapt_impl { 155 | Factory ef_; 156 | PUSHMI_TEMPLATE(class In) 157 | (requires Sender) // 158 | auto 159 | operator()(In&& in) const { 160 | return ::pushmi::detail::sender_from( 161 | (In&&)in, 162 | submit_impl{ef_}); 163 | } 164 | }; 165 | 166 | public: 167 | PUSHMI_TEMPLATE(class Factory) 168 | (requires StrandFactory) 169 | auto operator()(Factory ef) const { 170 | return adapt_impl{std::move(ef)}; 171 | } 172 | }; 173 | 174 | } // namespace detail 175 | 176 | namespace operators { 177 | PUSHMI_INLINE_VAR constexpr detail::via_fn via{}; 178 | } // namespace operators 179 | 180 | } // namespace pushmi 181 | -------------------------------------------------------------------------------- /include/pushmi/piping.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | 23 | struct pipeorigin {}; 24 | 25 | PUSHMI_TEMPLATE(class In, class Op) 26 | (requires PUSHMI_EXP(lazy::Sender PUSHMI_AND 27 | lazy::Invocable)) // 28 | decltype(auto) 29 | operator|(In&& in, Op&& op) { 30 | return op((In &&) in); 31 | } 32 | PUSHMI_TEMPLATE(class Ex, class Op) 33 | (requires PUSHMI_EXP(lazy::Executor PUSHMI_AND 34 | lazy::Invocable)) // 35 | decltype(auto) 36 | operator|(Ex&& ex, Op&& op) { 37 | return op((Ex &&) ex); 38 | } 39 | PUSHMI_TEMPLATE(class Out, class Op) 40 | (requires PUSHMI_EXP(lazy::Receiver PUSHMI_AND 41 | lazy::Invocable)) // 42 | decltype(auto) 43 | operator|(Out&& out, Op&& op) { 44 | return op((Out &&) out); 45 | } 46 | 47 | PUSHMI_INLINE_VAR constexpr struct pipe_fn { 48 | #if __cpp_fold_expressions >= 201603 49 | template 50 | auto operator()(T&& t, FN&&... fn) const 51 | -> decltype(((T &&) t | ... | (FN &&) fn)) { 52 | return ((T &&) t | ... | (FN &&) fn); 53 | } 54 | #else 55 | template 56 | auto operator()(T&& t, F&& f) const -> decltype((T &&) t | (F &&) f) { 57 | return (T &&) t | (F &&) f; 58 | } 59 | template 60 | auto operator()(T&& t, F&& f, FN&&... fn) const 61 | -> decltype(This()(((T &&) t | (F &&) f), (FN &&) fn...)) { 62 | return This()(((T &&) t | (F &&) f), (FN &&) fn...); 63 | } 64 | #endif 65 | } const pipe{}; 66 | 67 | } // namespace pushmi 68 | -------------------------------------------------------------------------------- /include/pushmi/properties.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | namespace pushmi { 22 | 23 | // property_set implements a map of category-type to property-type. 24 | // for each category only one property in that category is allowed in the set. 25 | 26 | // customization point for a property with a category 27 | 28 | template 29 | using __property_category_t = typename T::property_category; 30 | 31 | // allow specializations to use enable_if to constrain 32 | template 33 | struct property_traits {}; 34 | template 35 | struct property_traits< 36 | T, 37 | void_t<__property_category_t>>> { 38 | using property_category = __property_category_t>; 39 | }; 40 | 41 | template 42 | using property_category_t = __property_category_t>; 43 | 44 | PUSHMI_CONCEPT_DEF( 45 | template(class T) 46 | concept Property, 47 | True<__property_category_t>> 48 | ); 49 | 50 | // in cases where Set contains T, allow T to find itself only once 51 | PUSHMI_CONCEPT_DEF( 52 | template(class T, class... Set)( 53 | concept FoundExactlyOnce)(T, Set...), 54 | sum_v<(PUSHMI_PP_IS_SAME(T, Set) ? 1 : 0)...> == 1 55 | ); 56 | 57 | PUSHMI_CONCEPT_DEF( 58 | template(class... PropertyN)( 59 | concept UniqueCategory)(PropertyN...), 60 | And, 62 | property_category_t...>...>&& And...> 63 | ); 64 | 65 | namespace detail { 66 | template > 67 | struct property_set_element {}; 68 | } // namespace detail 69 | 70 | template 71 | struct property_set : detail::property_set_element... { 72 | static_assert( 73 | and_v...>, 74 | "property_set only supports types that match the Property concept"); 75 | static_assert( 76 | UniqueCategory, 77 | "property_set has multiple properties from the same category"); 78 | using properties = property_set; 79 | }; 80 | 81 | PUSHMI_CONCEPT_DEF( 82 | template(class T) 83 | concept PropertySet, 84 | detail::is_v 85 | ); 86 | 87 | // customization point for a type with properties 88 | 89 | template 90 | using __properties_t = typename T::properties; 91 | 92 | // allow specializations to use enable_if to constrain 93 | template 94 | struct property_set_traits {}; 95 | template 96 | struct property_set_traits< 97 | T, 98 | void_t<__properties_t>>> { 99 | using properties = __properties_t>; 100 | }; 101 | 102 | template 103 | using properties_t = std::enable_if_t< 104 | PropertySet<__properties_t>>, 105 | __properties_t>>; 106 | 107 | PUSHMI_CONCEPT_DEF( 108 | template(class T) 109 | concept Properties, 110 | PropertySet<__properties_t>> 111 | ); 112 | 113 | // find property in the specified set that matches the category of the property 114 | // specified. 115 | namespace detail { 116 | template 117 | POut __property_set_index_fn( 118 | property_set_element>); 119 | 120 | template 121 | property_set...> 122 | __property_set_insert_fn( 123 | property_set, 124 | property_set_element>); 125 | 126 | template 127 | property_set __property_set_insert_fn(property_set, ...); 128 | 129 | template 130 | using property_set_insert_one_t = 131 | decltype(detail::__property_set_insert_fn

(PS{}, PS{})); 132 | 133 | template 134 | struct property_set_insert { 135 | using type = PS0; 136 | }; 137 | 138 | template 139 | struct property_set_insert> 140 | : property_set_insert< 141 | property_set_insert_one_t, 142 | property_set> {}; 143 | } // namespace detail 144 | 145 | template 146 | using property_set_index_t = std::enable_if_t< 147 | PropertySet && Property

, 148 | decltype(detail::__property_set_index_fn

(PS{}))>; 149 | 150 | template 151 | using property_set_insert_t = typename std::enable_if_t< 152 | PropertySet && PropertySet, 153 | detail::property_set_insert>::type; 154 | 155 | // query for properties on types with properties. 156 | 157 | namespace detail { 158 | template 159 | std::is_base_of property_query_fn( 160 | property_set_element>*); 161 | template 162 | std::false_type property_query_fn(void*); 163 | 164 | template 165 | struct property_query_impl : bool_( 166 | (properties_t*)nullptr))::value...>> {}; 167 | } // namespace detail 168 | 169 | template 170 | struct property_query : std::conditional_t< 171 | Properties && And...>, 172 | detail::property_query_impl, 173 | std::false_type> {}; 174 | 175 | template 176 | PUSHMI_INLINE_VAR constexpr bool property_query_v = 177 | property_query::value; 178 | 179 | // query for categories on types with properties. 180 | 181 | namespace detail { 182 | template 183 | std::true_type category_query_fn(property_set_element*); 184 | template 185 | std::false_type category_query_fn(void*); 186 | 187 | template 188 | struct category_query_impl : bool_( 189 | (properties_t*)nullptr))::value...>> {}; 190 | } // namespace detail 191 | 192 | template 193 | struct category_query : std::conditional_t< 194 | Properties && !Or...>, 195 | detail::category_query_impl, 196 | std::false_type> {}; 197 | 198 | template 199 | PUSHMI_INLINE_VAR constexpr bool category_query_v = 200 | category_query::value; 201 | 202 | } // namespace pushmi 203 | -------------------------------------------------------------------------------- /include/pushmi/strand.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | namespace pushmi { 24 | 25 | template 26 | class strand_executor; 27 | 28 | template 29 | struct strand_queue_receiver; 30 | 31 | template 32 | class strand_item { 33 | public: 34 | strand_item(any_receiver> out) 35 | : what(std::move(out)) {} 36 | 37 | any_receiver> what; 38 | }; 39 | template 40 | bool operator<(const strand_item& l, const strand_item& r) { 41 | return l.when < r.when; 42 | } 43 | template 44 | bool operator>(const strand_item& l, const strand_item& r) { 45 | return l.when > r.when; 46 | } 47 | template 48 | bool operator==(const strand_item& l, const strand_item& r) { 49 | return l.when == r.when; 50 | } 51 | template 52 | bool operator!=(const strand_item& l, const strand_item& r) { 53 | return !(l == r); 54 | } 55 | template 56 | bool operator<=(const strand_item& l, const strand_item& r) { 57 | return !(l > r); 58 | } 59 | template 60 | bool operator>=(const strand_item& l, const strand_item& r) { 61 | return !(l < r); 62 | } 63 | 64 | template 65 | class strand_queue_base 66 | : public std::enable_shared_from_this> { 67 | public: 68 | std::mutex lock_; 69 | size_t remaining_ = 0; 70 | std::queue> items_; 71 | 72 | virtual ~strand_queue_base() {} 73 | 74 | strand_item& front() { 75 | // :( 76 | return const_cast&>(this->items_.front()); 77 | } 78 | 79 | virtual void dispatch() = 0; 80 | }; 81 | 82 | template 83 | class strand_queue : public strand_queue_base { 84 | public: 85 | ~strand_queue() {} 86 | strand_queue(Exec ex) : ex_(std::move(ex)) {} 87 | Exec ex_; 88 | 89 | void dispatch() override; 90 | 91 | auto shared_from_that() { 92 | return std::static_pointer_cast>( 93 | this->shared_from_this()); 94 | } 95 | 96 | template 97 | void value(SubExec&&) { 98 | // 99 | // pull ready items from the queue in order. 100 | 101 | std::unique_lock guard{this->lock_}; 102 | 103 | // only allow one at a time 104 | if (this->remaining_ > 0) { 105 | return; 106 | } 107 | // skip when empty 108 | if (this->items_.empty()) { 109 | return; 110 | } 111 | 112 | // do not allow recursive queueing to block this executor 113 | this->remaining_ = this->items_.size(); 114 | 115 | auto that = shared_from_that(); 116 | auto subEx = strand_executor{that}; 117 | 118 | while (!this->items_.empty() && --this->remaining_ >= 0) { 119 | auto item{std::move(this->front())}; 120 | this->items_.pop(); 121 | guard.unlock(); 122 | set_value(item.what, any_executor_ref{subEx}); 123 | set_done(item.what); 124 | guard.lock(); 125 | } 126 | } 127 | template 128 | void error(AE e) noexcept { 129 | std::unique_lock guard{this->lock_}; 130 | 131 | this->remaining_ = 0; 132 | 133 | while (!this->items_.empty()) { 134 | auto what{std::move(this->front().what)}; 135 | this->items_.pop(); 136 | guard.unlock(); 137 | set_error(what, detail::as_const(e)); 138 | guard.lock(); 139 | } 140 | } 141 | void done() { 142 | std::unique_lock guard{this->lock_}; 143 | 144 | // only allow one at a time 145 | if (this->remaining_ > 0) { 146 | return; 147 | } 148 | // skip when empty 149 | if (this->items_.empty()) { 150 | return; 151 | } 152 | 153 | auto that = shared_from_that(); 154 | submit(schedule(ex_), strand_queue_receiver{that}); 155 | } 156 | }; 157 | 158 | template 159 | struct strand_queue_receiver : std::shared_ptr> { 160 | ~strand_queue_receiver() {} 161 | explicit strand_queue_receiver(std::shared_ptr> that) 162 | : std::shared_ptr>(that) {} 163 | using properties = property_set>; 164 | }; 165 | 166 | template 167 | void strand_queue::dispatch() { 168 | submit(schedule(ex_), strand_queue_receiver{shared_from_that()}); 169 | } 170 | 171 | // 172 | // strand is used to build a fifo single_executor from a concurrent 173 | // single_executor. 174 | // 175 | 176 | template 177 | class strand_executor; 178 | 179 | template 180 | class strand_task { 181 | std::shared_ptr> queue_; 182 | 183 | public: 184 | using properties = property_set< 185 | is_sender<>, 186 | property_set_index_t>, is_never_blocking<>>, 187 | is_single<>>; 188 | 189 | strand_task(std::shared_ptr> queue) 190 | : queue_(std::move(queue)) {} 191 | 192 | PUSHMI_TEMPLATE(class Out) 193 | (requires ReceiveValue>&& ReceiveError) // 194 | void submit(Out out) { 195 | // queue for later 196 | std::unique_lock guard{queue_->lock_}; 197 | queue_->items_.push(any_receiver>{std::move(out)}); 198 | if (queue_->remaining_ == 0) { 199 | // noone is minding the shop, send a worker 200 | guard.unlock(); 201 | ::pushmi::submit( 202 | ::pushmi::schedule(queue_->ex_), strand_queue_receiver{queue_}); 203 | } 204 | } 205 | }; 206 | 207 | template 208 | class strand_executor { 209 | std::shared_ptr> queue_; 210 | 211 | public: 212 | using properties = property_set, is_fifo_sequence<>>; 213 | 214 | strand_executor(std::shared_ptr> queue) 215 | : queue_(std::move(queue)) {} 216 | 217 | strand_task schedule() { 218 | return {queue_}; 219 | } 220 | }; 221 | 222 | // 223 | // the strand executor factory produces a new fifo ordered queue each time that 224 | // it is called. 225 | // 226 | 227 | template 228 | class same_strand_factory_fn { 229 | Exec ex_; 230 | 231 | public: 232 | explicit same_strand_factory_fn(Exec ex) : ex_(std::move(ex)) {} 233 | auto make_strand() const { 234 | auto queue = std::make_shared>(ex_); 235 | return strand_executor{queue}; 236 | } 237 | }; 238 | 239 | PUSHMI_TEMPLATE(class E = std::exception_ptr, class Provider) 240 | (requires ExecutorProvider&& 241 | ConcurrentSequence>) // 242 | auto strands(Provider ep) { 243 | return same_strand_factory_fn>{get_executor(ep)}; 244 | } 245 | PUSHMI_TEMPLATE(class E = std::exception_ptr, class Exec) 246 | (requires Executor&& ConcurrentSequence) // 247 | auto strands(Exec ex) { 248 | return same_strand_factory_fn{std::move(ex)}; 249 | } 250 | 251 | } // namespace pushmi 252 | -------------------------------------------------------------------------------- /include/pushmi/subject.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace pushmi { 26 | 27 | template 28 | struct subject; 29 | 30 | template 31 | struct subject { 32 | using properties = property_set_insert_t< 33 | property_set, is_single<>>, 34 | property_set>>>; 35 | 36 | struct subject_shared { 37 | using receiver_t = any_receiver; 38 | bool done_ = false; 39 | ::pushmi::detail::opt...>> t_; 40 | std::exception_ptr ep_; 41 | std::vector receivers_; 42 | std::mutex lock_; 43 | PUSHMI_TEMPLATE(class Out) 44 | (requires ReceiveError)// && ReceiveValue) 45 | void submit(Out out) { 46 | std::unique_lock guard(lock_); 47 | if (ep_) { 48 | set_error(out, ep_); 49 | return; 50 | } 51 | if (!!t_ && done_) { 52 | auto args = *t_; 53 | ::pushmi::apply( 54 | ::pushmi::set_value, 55 | std::tuple_cat(std::tuple{out}, std::move(args))); 56 | return; 57 | } 58 | if (done_) { 59 | set_done(out); 60 | return; 61 | } 62 | receivers_.push_back(receiver_t{std::move(out)}); 63 | } 64 | PUSHMI_TEMPLATE(class... VN) 65 | (requires And...>) 66 | void value(VN&&... vn) { 67 | std::unique_lock guard(lock_); 68 | for (auto& out : receivers_) { 69 | ::pushmi::apply( 70 | ::pushmi::set_value, 71 | std::tuple...>{ 72 | out, detail::as_const(vn)...}); 73 | } 74 | t_ = std::make_tuple((VN &&) vn...); 75 | } 76 | PUSHMI_TEMPLATE(class E) 77 | (requires SemiMovable) 78 | void error(E e) noexcept { 79 | std::unique_lock guard(lock_); 80 | ep_ = e; 81 | for (auto& out : receivers_) { 82 | set_error(out, std::move(e)); 83 | } 84 | receivers_.clear(); 85 | } 86 | void done() { 87 | std::unique_lock guard(lock_); 88 | done_ = true; 89 | for (auto& out : receivers_) { 90 | set_done(out); 91 | } 92 | receivers_.clear(); 93 | } 94 | }; 95 | 96 | struct subject_receiver { 97 | using properties = property_set>; 98 | 99 | std::shared_ptr s; 100 | 101 | PUSHMI_TEMPLATE(class... VN) 102 | (requires And...>) 103 | void value(VN&&... vn) { 104 | s->value((VN &&) vn...); 105 | } 106 | PUSHMI_TEMPLATE(class E) 107 | (requires SemiMovable) 108 | void error(E e) noexcept { 109 | s->error(std::move(e)); 110 | } 111 | void done() { 112 | s->done(); 113 | } 114 | }; 115 | 116 | std::shared_ptr s = std::make_shared(); 117 | 118 | PUSHMI_TEMPLATE(class Out) 119 | (requires Receiver) 120 | void submit(Out out) { 121 | s->submit(std::move(out)); 122 | } 123 | 124 | auto receiver() { 125 | return detail::receiver_from_fn{}(subject_receiver{s}); 126 | } 127 | }; 128 | 129 | } // namespace pushmi 130 | -------------------------------------------------------------------------------- /include/pushmi/traits.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #define PUSHMI_NOEXCEPT_AUTO(...) \ 24 | noexcept(noexcept(static_cast(__VA_ARGS__)))\ 25 | /**/ 26 | #define PUSHMI_NOEXCEPT_RETURN(...) \ 27 | PUSHMI_NOEXCEPT_AUTO(__VA_ARGS__) {\ 28 | return (__VA_ARGS__);\ 29 | }\ 30 | /**/ 31 | 32 | namespace pushmi { 33 | #if __cpp_fold_expressions >= 201603 34 | template 35 | PUSHMI_INLINE_VAR constexpr bool and_v = (Bs && ...); 36 | 37 | template 38 | PUSHMI_INLINE_VAR constexpr bool or_v = (Bs || ...); 39 | 40 | template 41 | PUSHMI_INLINE_VAR constexpr int sum_v = (Is + ...); 42 | #else 43 | namespace detail { 44 | 45 | template 46 | struct bools; 47 | 48 | template 49 | constexpr int sum_impl(int const (&rgi)[N], int i = 0, int state = 0) noexcept { 50 | return i == N ? state : sum_impl(rgi, i + 1, state + rgi[i]); 51 | } 52 | template 53 | constexpr int sum_impl() noexcept { 54 | using RGI = int[sizeof...(Is)]; 55 | return sum_impl(RGI{Is...}); 56 | } 57 | 58 | } // namespace detail 59 | 60 | template 61 | PUSHMI_INLINE_VAR constexpr bool and_v = 62 | PUSHMI_PP_IS_SAME(detail::bools, detail::bools); 63 | 64 | template 65 | PUSHMI_INLINE_VAR constexpr bool or_v = !PUSHMI_PP_IS_SAME( 66 | detail::bools, 67 | detail::bools); 68 | 69 | template 70 | PUSHMI_INLINE_VAR constexpr int sum_v = detail::sum_impl(); 71 | #endif 72 | 73 | namespace detail { 74 | template 75 | struct void_t_ { 76 | using type = void; 77 | }; 78 | } 79 | 80 | template 81 | using void_t = typename detail::void_t_::type; 82 | 83 | template 84 | struct typelist; 85 | 86 | template 87 | using remove_cvref_t = std::remove_cv_t>; 88 | 89 | PUSHMI_CONCEPT_DEF( 90 | template(class... Args) 91 | (concept True)(Args...), 92 | true 93 | ); 94 | 95 | PUSHMI_CONCEPT_DEF( 96 | template (class T, template class Trait, class... Args) 97 | (concept Satisfies)(T, Trait, Args...), 98 | static_cast(Trait::type::value) 99 | ); 100 | 101 | PUSHMI_CONCEPT_DEF( 102 | template (class T, class U) 103 | concept Same, 104 | PUSHMI_PP_IS_SAME(T, U) && PUSHMI_PP_IS_SAME(U, T) 105 | ); 106 | 107 | PUSHMI_CONCEPT_DEF( 108 | template (bool...Bs) 109 | (concept And)(Bs...), 110 | and_v 111 | ); 112 | 113 | PUSHMI_CONCEPT_DEF( 114 | template (bool...Bs) 115 | (concept Or)(Bs...), 116 | or_v 117 | ); 118 | 119 | PUSHMI_CONCEPT_DEF( 120 | template (class T) 121 | concept Object, 122 | requires (T* p) ( 123 | *p, 124 | implicitly_convertible_to(p) 125 | ) 126 | ); 127 | 128 | PUSHMI_CONCEPT_DEF( 129 | template (class T, class... Args) 130 | (concept Constructible)(T, Args...), 131 | PUSHMI_PP_IS_CONSTRUCTIBLE(T, Args...) 132 | ); 133 | 134 | PUSHMI_CONCEPT_DEF( 135 | template (class T) 136 | concept MoveConstructible, 137 | Constructible 138 | ); 139 | 140 | PUSHMI_CONCEPT_DEF( 141 | template (class From, class To) 142 | concept ConvertibleTo, 143 | requires (From (&f)()) ( 144 | static_cast(f()) 145 | ) && std::is_convertible::value 146 | ); 147 | 148 | PUSHMI_CONCEPT_DEF( 149 | template (class A, class B) 150 | concept DerivedFrom, 151 | __is_base_of(B, A) 152 | ); 153 | 154 | PUSHMI_CONCEPT_DEF( 155 | template (class A) 156 | concept Decayed, 157 | Same> 158 | ); 159 | 160 | PUSHMI_CONCEPT_DEF( 161 | template (class T, class U) 162 | concept Assignable, 163 | requires(T t, U&& u) ( 164 | t = (U &&) u, 165 | requires_> 166 | ) && Same 167 | ); 168 | 169 | PUSHMI_CONCEPT_DEF( 170 | template (class T) 171 | concept EqualityComparable, 172 | requires(remove_cvref_t const & t) ( 173 | implicitly_convertible_to( t == t ), 174 | implicitly_convertible_to( t != t ) 175 | ) 176 | ); 177 | 178 | PUSHMI_CONCEPT_DEF( 179 | template (class T) 180 | concept SemiMovable, 181 | Object && Constructible && ConvertibleTo 182 | ); 183 | 184 | PUSHMI_CONCEPT_DEF( 185 | template (class T) 186 | concept Movable, 187 | SemiMovable && Assignable 188 | ); 189 | 190 | PUSHMI_CONCEPT_DEF( 191 | template (class T) 192 | concept Copyable, 193 | Movable && 194 | Assignable && 195 | ConvertibleTo 196 | ); 197 | 198 | PUSHMI_CONCEPT_DEF( 199 | template (class T) 200 | concept Semiregular, 201 | Copyable && Constructible 202 | ); 203 | 204 | PUSHMI_CONCEPT_DEF( 205 | template (class T) 206 | concept Regular, 207 | Semiregular && EqualityComparable 208 | ); 209 | 210 | namespace detail { 211 | // is_ taken from meta library 212 | 213 | template class> 214 | struct is_ : std::false_type {}; 215 | 216 | template class C> 217 | struct is_, C> : std::true_type {}; 218 | 219 | template class C> 220 | constexpr bool is_v = is_::value; 221 | 222 | template 223 | using requires_ = std::enable_if_t; 224 | 225 | PUSHMI_INLINE_VAR constexpr struct as_const_fn { 226 | template 227 | constexpr const T& operator()(T& t) const noexcept { 228 | return t; 229 | } 230 | } const as_const{}; 231 | 232 | } // namespace detail 233 | 234 | } // namespace pushmi 235 | -------------------------------------------------------------------------------- /install_libcxx.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright (c) 2018-present, Facebook, Inc. 4 | # 5 | # This source code is licensed under the Apache License found in the 6 | # LICENSE file in the root directory of this source tree. 7 | 8 | TRUNK_VERSION="6.0.0" 9 | 10 | set -e 11 | 12 | # The pattern of clang --version is: clang version X.Y.Z (sometimes, see below). 13 | COMPILER_VERSION_OUTPUT="$($CXX --version)" 14 | arr=(${COMPILER_VERSION_OUTPUT// / }) 15 | 16 | COMPILER="${arr[0]}" 17 | VERSION="${arr[2]}" 18 | 19 | case $COMPILER in 20 | "clang") 21 | # Some Ubuntu clang builds are advertised as "just clang", but the 22 | # Version still follows the pattern: 3.6.2-svn240577-1~exp1 23 | # echo "Compiler is clang :)" 24 | arr2=(${VERSION//-/ }) 25 | VERSION="${arr2[0]}" 26 | ;; 27 | "Ubuntu") 28 | # Ubuntu renames _some_ (not all) of its clang compilers, the pattern of 29 | # clang --version is then: 30 | # Ubuntu clang version 3.6.2-svn240577-1~exp1 31 | COMPILER="${arr[1]}" 32 | VERSION="${arr[3]}" 33 | arr2=(${VERSION//-/ }) 34 | VERSION="${arr2[0]}" 35 | ;; 36 | *) 37 | echo "case did not match: compiler: ${COMPILER}" 38 | exit 1 39 | ;; 40 | esac 41 | 42 | if [ ${COMPILER} != "clang" ]; then 43 | echo "Error: trying to install libc++ for a compiler that is not clang: ${COMPILER}" 44 | exit 1 45 | fi 46 | 47 | if [ -z ${VERSION+x} ]; then 48 | echo "libc++ version is not set. To set the libc++ version: ./install_libcxx.sh -v X.Y.Z" 49 | exit 4 50 | fi 51 | 52 | if [ ${VERSION} == $TRUNK_VERSION ]; then 53 | echo "Fetching libc++ and libc++abi tip-of-trunk..." 54 | 55 | # Checkout LLVM sources 56 | git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm-source 57 | git clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx 58 | git clone --depth=1 https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi 59 | else 60 | echo "Fetching libc++/libc++abi version: ${VERSION}..." 61 | LLVM_URL="http://releases.llvm.org/${VERSION}/llvm-${VERSION}.src.tar.xz" 62 | LIBCXX_URL="http://releases.llvm.org/${VERSION}/libcxx-${VERSION}.src.tar.xz" 63 | LIBCXXABI_URL="http://releases.llvm.org/${VERSION}/libcxxabi-${VERSION}.src.tar.xz" 64 | curl -O $LLVM_URL 65 | curl -O $LIBCXX_URL 66 | curl -O $LIBCXXABI_URL 67 | 68 | mkdir llvm-source 69 | mkdir llvm-source/projects 70 | mkdir llvm-source/projects/libcxx 71 | mkdir llvm-source/projects/libcxxabi 72 | 73 | tar -xf llvm-${VERSION}.src.tar.xz -C llvm-source --strip-components=1 74 | tar -xf libcxx-${VERSION}.src.tar.xz -C llvm-source/projects/libcxx --strip-components=1 75 | tar -xf libcxxabi-${VERSION}.src.tar.xz -C llvm-source/projects/libcxxabi --strip-components=1 76 | fi 77 | 78 | mkdir llvm-build 79 | cd llvm-build 80 | 81 | # - libc++ versions < 4.x do not have the install-cxxabi and install-cxx targets 82 | # - only ASAN is enabled for clang/libc++ versions < 4.x 83 | if [[ $VERSION == *"3."* ]]; then 84 | cmake -DCMAKE_C_COMPILER=${CC} -DCMAKE_CXX_COMPILER=${CXX} \ 85 | -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr \ 86 | ../llvm-source 87 | if [[ $SANITIZER == "Address;Undefined" ]]; then 88 | ASAN_FLAGS="-fsanitize=address" 89 | cmake -DCMAKE_CXX_FLAGS="${ASAN_FLAGS}" -DCMAKE_EXE_LINKER_FLAGS="${ASAN_FLAGS}" ../llvm-source 90 | fi 91 | make cxx -j2 VERBOSE=1 92 | sudo cp -r lib/* /usr/lib/ 93 | sudo cp -r include/c++ /usr/include/ 94 | else 95 | cmake -DCMAKE_C_COMPILER=${CC} -DCMAKE_CXX_COMPILER=${CXX} \ 96 | -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr \ 97 | -DLIBCXX_ABI_UNSTABLE=ON \ 98 | -DLLVM_USE_SANITIZER=${SANITIZER} \ 99 | ../llvm-source 100 | make cxx -j2 VERBOSE=1 101 | sudo make install-cxxabi install-cxx 102 | fi 103 | 104 | exit 0 105 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-present, Facebook, Inc. 2 | # 3 | # This source code is licensed under the Apache License found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0" AND NOT PUSHMI_ONE_TEST_BINARY) 7 | 8 | add_executable(FlowTest FlowTest.cpp) 9 | target_link_libraries(FlowTest pushmi gtest_main gmock_main Threads::Threads) 10 | add_test(NAME FlowTest COMMAND FlowTest) 11 | 12 | add_executable(FlowManyTest FlowManyTest.cpp) 13 | target_link_libraries(FlowManyTest pushmi gtest_main gmock_main Threads::Threads) 14 | add_test(NAME FlowManyTest COMMAND FlowManyTest) 15 | 16 | add_executable(CompileTest CompileTest.cpp) 17 | target_link_libraries(CompileTest pushmi gtest_main gmock_main Threads::Threads) 18 | add_test(NAME CompileTest COMMAND CompileTest) 19 | 20 | add_executable(NewThreadTest NewThreadTest.cpp) 21 | target_link_libraries(NewThreadTest pushmi gtest_main gmock_main Threads::Threads) 22 | add_test(NAME NewThreadTest COMMAND NewThreadTest) 23 | 24 | add_executable(TrampolineTest TrampolineTest.cpp) 25 | target_link_libraries(TrampolineTest pushmi gtest_main gmock_main Threads::Threads) 26 | add_test(NAME TrampolineTest COMMAND TrampolineTest) 27 | 28 | add_executable(PushmiTest PushmiTest.cpp) 29 | target_link_libraries(PushmiTest pushmi gtest_main gmock_main Threads::Threads) 30 | add_test(NAME PushmiTest COMMAND PushmiTest) 31 | 32 | else() 33 | 34 | add_executable(PushmiTest PushmiTest.cpp 35 | CompileTest.cpp 36 | TrampolineTest.cpp 37 | NewThreadTest.cpp 38 | FlowTest.cpp 39 | FlowManyTest.cpp 40 | ) 41 | 42 | target_link_libraries(PushmiTest pushmi gtest_main gmock_main Threads::Threads) 43 | add_test(NAME PushmiTest COMMAND PushmiTest) 44 | 45 | endif() 46 | -------------------------------------------------------------------------------- /test/NewThreadTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | using namespace std::literals; 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace pushmi::aliases; 37 | 38 | #include 39 | #include 40 | 41 | using namespace testing; 42 | 43 | struct countdownsingle { 44 | explicit countdownsingle(int& c) : counter(&c) {} 45 | 46 | int* counter; 47 | 48 | template 49 | void operator()(ExecutorRef exec) { 50 | if (--*counter > 0) { 51 | exec | op::schedule() | op::submit(*this); 52 | } 53 | } 54 | }; 55 | 56 | using NT = decltype(mi::new_thread()); 57 | 58 | inline auto make_time(mi::time_source<>& t, NT& ex) { 59 | auto strands = t.make(mi::systemNowF{}, ex); 60 | return mi::make_strand(strands); 61 | } 62 | 63 | class NewthreadExecutor : public Test { 64 | public: 65 | ~NewthreadExecutor() override { 66 | time_.join(); 67 | } 68 | 69 | protected: 70 | using TNT = mi::invoke_result_t&, NT&>; 71 | 72 | NT nt_{mi::new_thread()}; 73 | mi::time_source<> time_{}; 74 | TNT tnt_{make_time(time_, nt_)}; 75 | }; 76 | 77 | TEST_F(NewthreadExecutor, BlockingSubmitNow) { 78 | auto signals = 0; 79 | auto start = v::now(tnt_); 80 | auto signaled = start; 81 | tnt_ | op::schedule() | op::transform([](auto tnt) { return tnt | ep::now(); }) | 82 | op::blocking_submit( 83 | [&](auto at) { 84 | signaled = at; 85 | signals += 100; 86 | }, 87 | [&](auto) noexcept { signals += 1000; }, 88 | [&]() { signals += 10; }); 89 | 90 | EXPECT_THAT(signals, Eq(110)) 91 | << "expected that the value and done signals are recorded once and the value signal did not drift much"; 92 | auto delay = 93 | std::chrono::duration_cast((signaled - start)) 94 | .count(); 95 | EXPECT_THAT(delay, Lt(1000)) << "The delay is " << delay; 96 | } 97 | 98 | TEST_F(NewthreadExecutor, BlockingGetNow) { 99 | auto start = v::now(tnt_); 100 | auto signaled = tnt_ | op::schedule() | op::transform([](auto tnt) { return v::now(tnt); }) | 101 | op::get; 102 | 103 | auto delay = 104 | std::chrono::duration_cast((signaled - start)) 105 | .count(); 106 | 107 | EXPECT_THAT(delay, Lt(1000)) << "The delay is " << delay; 108 | } 109 | 110 | TEST_F(NewthreadExecutor, SubmissionsAreOrderedInTime) { 111 | std::vector times; 112 | std::atomic pushed{0}; 113 | auto push = [&](int time) { 114 | return v::on_value([&, time](auto) { 115 | times.push_back(std::to_string(time)); 116 | ++pushed; 117 | }); 118 | }; 119 | tnt_ | op::schedule() | op::submit(v::on_value([push](auto tnt) { 120 | auto now = tnt | ep::now(); 121 | tnt | op::schedule_after(40ms) | op::submit(push(40)); 122 | tnt | op::schedule_at(now + 10ms) | op::submit(push(10)); 123 | tnt | op::schedule_after(20ms) | op::submit(push(20)); 124 | tnt | op::schedule_at(now + 10ms) | op::submit(push(11)); 125 | })); 126 | 127 | while (pushed.load() < 4) { 128 | std::this_thread::yield(); 129 | } 130 | 131 | EXPECT_THAT(times, ElementsAre("10", "11", "20", "40")) 132 | << "expected that the items were pushed in time order not insertion order"; 133 | } 134 | 135 | TEST_F(NewthreadExecutor, NowIsCalled) { 136 | bool done = false; 137 | tnt_ | ep::now(); 138 | tnt_ | op::schedule() | op::blocking_submit([&](auto tnt) { 139 | tnt | ep::now(); 140 | done = true; 141 | }); 142 | 143 | EXPECT_THAT(done, Eq(true)) << "exptected that both calls to now() complete"; 144 | } 145 | 146 | TEST_F(NewthreadExecutor, BlockingSubmit) { 147 | auto signals = 0; 148 | nt_ | op::schedule() | op::transform([](auto) { return 42; }) | 149 | op::blocking_submit( 150 | [&](auto) { signals += 100; }, 151 | [&](auto) noexcept { signals += 1000; }, 152 | [&]() { signals += 10; }); 153 | 154 | EXPECT_THAT(signals, Eq(110)) 155 | << "the value and done signals are recorded once"; 156 | } 157 | 158 | TEST_F(NewthreadExecutor, BlockingGet) { 159 | auto v = nt_ | op::schedule() | op::transform([](auto) { return 42; }) | op::get; 160 | 161 | EXPECT_THAT(v, Eq(42)) << "expected that the result would be different"; 162 | } 163 | 164 | TEST_F(NewthreadExecutor, VirtualDerecursion) { 165 | int counter = 100'000; 166 | std::function exec)> recurse; 167 | recurse = [&](::pushmi::any_executor_ref<> nt) { 168 | if (--counter <= 0) 169 | return; 170 | nt | op::schedule() | op::submit(recurse); 171 | }; 172 | nt_ | op::schedule() | op::blocking_submit([&](auto nt) { recurse(nt); }); 173 | 174 | EXPECT_THAT(counter, Eq(0)) 175 | << "expected that all nested submissions complete"; 176 | } 177 | 178 | TEST_F(NewthreadExecutor, StaticDerecursion) { 179 | int counter = 100'000; 180 | countdownsingle single{counter}; 181 | nt_ | op::schedule() | op::blocking_submit(single); 182 | 183 | EXPECT_THAT(counter, Eq(0)) 184 | << "expected that all nested submissions complete"; 185 | } 186 | 187 | TEST_F(NewthreadExecutor, UsedWithOn) { 188 | std::vector values; 189 | auto sender = ::pushmi::make_single_sender([](auto out) { 190 | ::pushmi::set_value(out, 2.0); 191 | ::pushmi::set_done(out); 192 | // ignored 193 | ::pushmi::set_value(out, 1); 194 | ::pushmi::set_value(out, std::numeric_limits::min()); 195 | ::pushmi::set_value(out, std::numeric_limits::max()); 196 | }); 197 | sender | op::on(mi::strands(nt_)) | 198 | op::blocking_submit(v::on_value( 199 | [&](auto v) { values.push_back(std::to_string(v)); })); 200 | 201 | EXPECT_THAT(values, ElementsAre(std::to_string(2.0))) 202 | << "expected that only the first item was pushed"; 203 | } 204 | 205 | TEST_F(NewthreadExecutor, UsedWithVia) { 206 | std::vector values; 207 | auto sender = ::pushmi::make_single_sender([](auto out) { 208 | ::pushmi::set_value(out, 2.0); 209 | ::pushmi::set_done(out); 210 | // ignored 211 | ::pushmi::set_value(out, 1); 212 | ::pushmi::set_value(out, std::numeric_limits::min()); 213 | ::pushmi::set_value(out, std::numeric_limits::max()); 214 | }); 215 | sender | op::via(mi::strands(nt_)) | 216 | op::blocking_submit(v::on_value( 217 | [&](auto v) { values.push_back(std::to_string(v)); })); 218 | 219 | EXPECT_THAT(values, ElementsAre(std::to_string(2.0))) 220 | << "expected that only the first item was pushed"; 221 | } 222 | -------------------------------------------------------------------------------- /test/PushmiTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace std::literals; 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace pushmi::aliases; 33 | 34 | #include 35 | #include 36 | 37 | using namespace testing; 38 | 39 | TEST(EmptySingleSender, TapAndSubmit) { 40 | auto e = op::empty(); 41 | using E = decltype(e); 42 | 43 | EXPECT_THAT((v::SenderTo, v::is_single<>>), Eq(true)) 44 | << "expected empty to return a single sender that can take an any_receiver<>"; 45 | 46 | EXPECT_THAT( 47 | (v::SenderTo< 48 | E, 49 | v::any_receiver, 50 | v::is_single<>>), 51 | Eq(true)) 52 | << "expected empty to return a single sender that can take an any_receiver"; 53 | 54 | int signals = 0; 55 | e | 56 | op::tap( 57 | [&]() { signals += 100; }, 58 | [&](auto) noexcept { signals += 1000; }, 59 | [&]() { signals += 10; }) | 60 | op::submit( 61 | [&]() { signals += 100; }, 62 | [&](auto) noexcept { signals += 1000; }, 63 | [&]() { signals += 10; }); 64 | 65 | EXPECT_THAT(signals, Eq(20)) 66 | << "expected the done signal to be recorded twice"; 67 | 68 | EXPECT_THROW(v::future_from(e).get(), std::future_error) 69 | << "expected future_error when future_from is applied"; 70 | 71 | EXPECT_THAT( 72 | (std::is_same, decltype(v::future_from(e))>::value), 73 | Eq(true)) 74 | << "expected future_from(e) to return std::future"; 75 | } 76 | 77 | TEST(JustIntSingleSender, TransformAndSubmit) { 78 | auto j = op::just(20); 79 | using J = decltype(j); 80 | 81 | EXPECT_THAT( 82 | (v::SenderTo< 83 | J, 84 | v::any_receiver, 85 | v::is_single<>>), 86 | Eq(true)) 87 | << "expected empty to return a single sender that can take an any_receiver"; 88 | 89 | int signals = 0; 90 | int value = 0; 91 | j | 92 | op::transform( 93 | [&](int v) { 94 | signals += 10000; 95 | return v + 1; 96 | }, 97 | [&](auto v) { 98 | std::terminate(); 99 | return v; 100 | }) | 101 | op::transform([&](int v) { 102 | signals += 10000; 103 | return v * 2; 104 | }) | 105 | op::submit( 106 | [&](auto v) { 107 | value = v; 108 | signals += 100; 109 | }, 110 | [&](auto) noexcept { signals += 1000; }, 111 | [&]() { signals += 10; }); 112 | 113 | EXPECT_THAT(signals, Eq(20110)) 114 | << "expected that the transform signal is recorded twice and that the value and done signals once each"; 115 | 116 | EXPECT_THAT(value, Eq(42)) << "expected a different result"; 117 | 118 | auto twenty = v::future_from(j).get(); 119 | 120 | EXPECT_THAT(twenty, Eq(20)) 121 | << "expected a different result from future_from(e).get()"; 122 | 123 | EXPECT_THAT( 124 | (std::is_same, decltype(v::future_from(j))>::value), 125 | Eq(true)) 126 | << "expected future_from(e) to return std::future"; 127 | } 128 | 129 | TEST(FromIntManySender, TransformAndSubmit) { 130 | std::array arr{{0, 9, 99}}; 131 | auto m = op::from(arr); 132 | using M = decltype(m); 133 | 134 | EXPECT_THAT( 135 | (v::SenderTo, v::is_many<>>), 136 | Eq(true)) 137 | << "expected empty to return a many sender that can take an any_receiver"; 138 | 139 | int signals = 0; 140 | int value = 0; 141 | m | 142 | op::transform( 143 | [&](int v) { 144 | signals += 10000; 145 | return v + 1; 146 | }, 147 | [&](auto v) { 148 | std::terminate(); 149 | return v; 150 | }) | 151 | op::transform([&](int v) { 152 | signals += 10000; 153 | return v * 2; 154 | }) | 155 | op::submit( 156 | [&](auto v) { 157 | value += v; 158 | signals += 100; 159 | }, 160 | [&](auto) noexcept { signals += 1000; }, 161 | [&]() { signals += 10; }); 162 | 163 | EXPECT_THAT(signals, Eq(60310)) 164 | << "expected that the transform signal is recorded six times and that the value signal three times and done signal once"; 165 | 166 | EXPECT_THAT(value, Eq(222)) << "expected a different result"; 167 | } 168 | -------------------------------------------------------------------------------- /test/TrampolineTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace std::literals; 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | 36 | using namespace pushmi::aliases; 37 | 38 | #include 39 | #include 40 | 41 | using namespace testing; 42 | 43 | struct countdownsingle { 44 | explicit countdownsingle(int& c) : counter(&c) {} 45 | 46 | int* counter; 47 | 48 | template 49 | void operator()(ExecutorRef exec) { 50 | if (--*counter > 0) { 51 | exec | op::schedule() | op::submit(*this); 52 | } 53 | } 54 | }; 55 | 56 | using TR = decltype(mi::trampoline()); 57 | 58 | class TrampolineExecutor : public Test { 59 | protected: 60 | TR tr_{mi::trampoline()}; 61 | }; 62 | 63 | TEST_F(TrampolineExecutor, TransformAndSubmit) { 64 | auto signals = 0; 65 | tr_ | op::schedule() | op::transform([](auto) { return 42; }) | 66 | op::submit( 67 | [&](auto) { signals += 100; }, 68 | [&](auto) noexcept { signals += 1000; }, 69 | [&]() { signals += 10; }); 70 | 71 | EXPECT_THAT(signals, Eq(110)) 72 | << "expected that the value and done signals are each recorded once"; 73 | } 74 | 75 | TEST_F(TrampolineExecutor, BlockingGet) { 76 | auto v = tr_ | op::schedule() | op::transform([](auto) { return 42; }) | op::get; 77 | 78 | EXPECT_THAT(v, Eq(42)) << "expected that the result would be different"; 79 | } 80 | 81 | TEST_F(TrampolineExecutor, VirtualDerecursion) { 82 | int counter = 100'000; 83 | std::function exec)> recurse; 84 | recurse = [&](::pushmi::any_executor_ref<> tr) { 85 | if (--counter <= 0) 86 | return; 87 | tr | op::schedule() | op::submit(recurse); 88 | }; 89 | tr_ | op::schedule() | op::submit([&](auto exec) { recurse(exec); }); 90 | 91 | EXPECT_THAT(counter, Eq(0)) 92 | << "expected that all nested submissions complete"; 93 | } 94 | 95 | TEST_F(TrampolineExecutor, StaticDerecursion) { 96 | int counter = 100'000; 97 | countdownsingle single{counter}; 98 | tr_ | op::schedule() | op::submit(single); 99 | 100 | EXPECT_THAT(counter, Eq(0)) 101 | << "expected that all nested submissions complete"; 102 | } 103 | 104 | TEST_F(TrampolineExecutor, UsedWithOn) { 105 | std::vector values; 106 | auto sender = ::pushmi::make_single_sender([](auto out) { 107 | ::pushmi::set_value(out, 2.0); 108 | ::pushmi::set_done(out); 109 | // ignored 110 | ::pushmi::set_value(out, 1); 111 | ::pushmi::set_value(out, std::numeric_limits::min()); 112 | ::pushmi::set_value(out, std::numeric_limits::max()); 113 | }); 114 | auto inlineon = sender | op::on([&]() { return mi::inline_executor(); }); 115 | inlineon | op::submit(v::on_value([&](auto v) { 116 | values.push_back(std::to_string(v)); 117 | })); 118 | 119 | EXPECT_THAT(values, ElementsAre(std::to_string(2.0))) 120 | << "expected that only the first item was pushed"; 121 | } 122 | 123 | TEST_F(TrampolineExecutor, UsedWithVia) { 124 | std::vector values; 125 | auto sender = ::pushmi::make_single_sender([](auto out) { 126 | ::pushmi::set_value(out, 2.0); 127 | ::pushmi::set_done(out); 128 | // ignored 129 | ::pushmi::set_value(out, 1); 130 | ::pushmi::set_value(out, std::numeric_limits::min()); 131 | ::pushmi::set_value(out, std::numeric_limits::max()); 132 | }); 133 | auto inlinevia = sender | op::via([&]() { return mi::inline_executor(); }); 134 | inlinevia | op::submit(v::on_value([&](auto v) { 135 | values.push_back(std::to_string(v)); 136 | })); 137 | 138 | EXPECT_THAT(values, ElementsAre(std::to_string(2.0))) 139 | << "expected that only the first item was pushed"; 140 | } 141 | --------------------------------------------------------------------------------