├── conanfile.txt ├── .gitignore ├── examples ├── portable_concurrency.cmake ├── coroutine │ ├── CMakeLists.txt │ ├── main.cpp │ ├── coro_timer.cpp │ └── coro_timer.h ├── asio │ ├── CMakeLists.txt │ ├── main.cpp │ └── adapters.h ├── qt │ ├── CMakeLists.txt │ ├── PCFutureWatcher.cpp │ ├── PCFutureWatcher.h │ └── main.cpp └── CMakeLists.txt ├── portable_concurrency-config.cmake ├── portable_concurrency ├── future_fwd ├── latch ├── thread_pool ├── bits │ ├── config.h │ ├── voidify.h │ ├── closable_queue.h │ ├── alias_namespace.h │ ├── continuations_stack.h │ ├── coro.h │ ├── fwd.h │ ├── closable_queue.hpp │ ├── latch.h │ ├── make_future.h │ ├── future_state.h │ ├── future_sequence.h │ ├── small_unique_function.h │ ├── algo_adapters.h │ ├── thread_pool.h │ ├── execution.h │ ├── unique_function.hpp │ ├── utils.h │ ├── timed_waiter.h │ ├── invoke.h │ ├── once_consumable_stack.hpp │ ├── unique_function.h │ ├── concurrency_type_traits.h │ ├── async.h │ ├── once_consumable_stack.h │ ├── packaged_task.h │ ├── shared_state.h │ ├── shared_future.h │ ├── small_unique_function.hpp │ ├── either.h │ ├── future.h │ ├── portable_concurrency.cpp │ └── when_all.h ├── timed_waiter ├── functional ├── execution ├── functional_fwd ├── future └── CMakeLists.txt ├── .clang-format ├── cmake ├── ConanDeps.cmake ├── FindCoroutinesTS.cmake └── BuildTypes.cmake ├── .travis.yml ├── .travis ├── cxx-conan-gcc-5.dockerfile ├── cxx-conan-gcc-9.dockerfile ├── cxx-conan-base.dockerfile ├── cxx-conan-clang-8.dockerfile ├── cxx-conan-clang-3.9.dockerfile └── autobuild.sh ├── appveyor.yml ├── test ├── algo_adapters.cpp ├── simple_arena_allocator.h ├── CMakeLists.txt ├── thread_pool.cpp ├── async.cpp ├── small_unique_function.cpp ├── test_tools.cpp ├── timed_waiter.cpp ├── once_consumable_stack.cpp ├── notify.cpp ├── future_then_unwrap.cpp ├── future_next.cpp ├── shared_future_then_unwrap.cpp ├── test_tools.h ├── shared_future_next.cpp ├── test_helpers.h ├── packaged_task_unwrap.cpp ├── future_then.cpp └── shared_future_then.cpp ├── conanfile.py ├── CMakeLists.txt ├── .github └── workflows │ ├── cmake-msvc.yml │ └── cmake-gcc.yml ├── README.md └── LICENSE.md /conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | gtest/1.13.0 3 | asio/1.28.0 4 | 5 | [generators] 6 | CMakeDeps 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | conanbuildinfo.cmake 2 | conaninfo.txt 3 | build 4 | *.kdev4 5 | CMakeLists.txt.user 6 | -------------------------------------------------------------------------------- /examples/portable_concurrency.cmake: -------------------------------------------------------------------------------- 1 | add_library(portable_concurrency::portable_concurrency ALIAS portable_concurrency) 2 | -------------------------------------------------------------------------------- /portable_concurrency-config.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | find_dependency(Threads) 3 | 4 | include("${CMAKE_CURRENT_LIST_DIR}/portable_concurrency.cmake") 5 | -------------------------------------------------------------------------------- /portable_concurrency/future_fwd: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | #pragma once 3 | 4 | #include "bits/alias_namespace.h" 5 | #include "bits/future.h" 6 | #include "bits/fwd.h" 7 | #include "bits/shared_future.h" 8 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | AlignAfterOpenBracket: 'false' 4 | AlwaysBreakTemplateDeclarations: 'true' 5 | ColumnLimit: '120' 6 | MaxEmptyLinesToKeep: '1' 7 | PointerAlignment: Left 8 | 9 | ... 10 | -------------------------------------------------------------------------------- /cmake/ConanDeps.cmake: -------------------------------------------------------------------------------- 1 | if (EXISTS "${portable_concurrency_BINARY_DIR}/conanbuildinfo.cmake") 2 | include("${portable_concurrency_BINARY_DIR}/conanbuildinfo.cmake") 3 | set(CONAN_CMAKE_FIND_ROOT_PATH ${CONAN_CMAKE_MODULE_PATH}) 4 | conan_basic_setup(TARGETS) 5 | endif() 6 | -------------------------------------------------------------------------------- /portable_concurrency/latch: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | #pragma once 3 | 4 | /** 5 | * @defgroup latch 6 | * @headerfile portable_concurrency/latch 7 | * 8 | * Simple non reusable barrier class. 9 | */ 10 | 11 | #include "bits/alias_namespace.h" 12 | #include "bits/latch.h" 13 | -------------------------------------------------------------------------------- /portable_concurrency/thread_pool: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | #pragma once 3 | 4 | /** 5 | * @defgroup thread_pool 6 | * @headerfile portable_concurrency/thread_pool 7 | * 8 | * Statically sized thread pool implementation 9 | */ 10 | 11 | #include "bits/alias_namespace.h" 12 | #include "bits/thread_pool.h" 13 | -------------------------------------------------------------------------------- /portable_concurrency/bits/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(_MSC_VER) 4 | #define PC_NODISCARD _Check_return_ 5 | #elif defined(__has_cpp_attribute) 6 | #if __has_cpp_attribute(gnu::warn_unused_result) 7 | #define PC_NODISCARD [[gnu::warn_unused_result]] 8 | #endif 9 | #endif 10 | 11 | #if !defined(PC_NODISCARD) 12 | #define PC_NODISCARD 13 | #endif 14 | -------------------------------------------------------------------------------- /portable_concurrency/timed_waiter: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | #pragma once 3 | 4 | /** 5 | * @defgroup timed_waiter_hdr 6 | * @headerfile portable_concurrency/timed_waiter 7 | * 8 | * Class allowing to wait for futures with timeout 9 | */ 10 | 11 | #include "bits/alias_namespace.h" 12 | #include "bits/timed_waiter.h" 13 | -------------------------------------------------------------------------------- /portable_concurrency/functional: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | #pragma once 3 | 4 | /** 5 | * @defgroup functional 6 | * @headerfile portable_concurrency/functional 7 | * 8 | * Extra functional types usefull for writing concurrent code. 9 | */ 10 | 11 | #include "bits/alias_namespace.h" 12 | #include "bits/unique_function.hpp" 13 | -------------------------------------------------------------------------------- /portable_concurrency/bits/voidify.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace portable_concurrency { 4 | inline namespace cxx14_v1 { 5 | namespace detail { 6 | 7 | // TODO: replace with std::void_t after migration to C++17 8 | template struct voidify { 9 | using type = void; 10 | }; 11 | 12 | } // namespace detail 13 | } // namespace cxx14_v1 14 | } // namespace portable_concurrency 15 | -------------------------------------------------------------------------------- /portable_concurrency/execution: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | #pragma once 3 | 4 | /** 5 | * @defgroup execution 6 | * @headerfile portable_concurrency/execution 7 | * 8 | * Set of primitives to establish future API interaction with user provided 9 | * executor types. 10 | */ 11 | 12 | #include "bits/alias_namespace.h" 13 | #include "bits/execution.h" 14 | -------------------------------------------------------------------------------- /portable_concurrency/functional_fwd: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | #pragma once 3 | 4 | /** 5 | * @ingroup functional 6 | * @file portable_concurrency/functional_fwd 7 | * 8 | * Forward declarations for extra functional types usefull for writing 9 | * concurrent code 10 | */ 11 | 12 | #include "bits/alias_namespace.h" 13 | #include "bits/unique_function.h" 14 | -------------------------------------------------------------------------------- /examples/coroutine/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(coroutine) 2 | cmake_minimum_required(VERSION 3.15) 3 | 4 | find_package(portable_concurrency REQUIRED) 5 | add_executable(coroutine 6 | coro_timer.cpp 7 | coro_timer.h 8 | main.cpp 9 | ) 10 | target_link_libraries(coroutine PRIVATE 11 | portable_concurrency::portable_concurrency 12 | ) 13 | target_compile_features(coroutine PRIVATE 14 | cxx_std_20 15 | ) 16 | -------------------------------------------------------------------------------- /examples/asio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(asio-pc) 4 | 5 | find_package(asio REQUIRED) 6 | find_package(portable_concurrency REQUIRED) 7 | 8 | add_executable(asio-pc 9 | main.cpp 10 | 11 | adapters.h 12 | ) 13 | target_link_libraries(asio-pc asio::asio portable_concurrency::portable_concurrency) 14 | target_compile_definitions(asio-pc PRIVATE ASIO_NO_DEPRECATED) 15 | target_compile_features(coroutine PRIVATE 16 | cxx_std_17 17 | ) 18 | -------------------------------------------------------------------------------- /examples/qt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(qt_pc) 3 | 4 | set(CMAKE_INCLUDE_CURRENT_DIR On) 5 | set(CMAKE_AUTOMOC On) 6 | 7 | find_package(Qt6Core REQUIRED) 8 | find_package(portable_concurrency REQUIRED) 9 | 10 | add_executable(qt-pc 11 | main.cpp 12 | PCFutureWatcher.cpp 13 | PCFutureWatcher.h 14 | ) 15 | target_link_libraries(qt-pc 16 | Qt6::Core 17 | portable_concurrency::portable_concurrency 18 | ) 19 | target_compile_features(coroutine PRIVATE 20 | cxx_std_14 21 | ) 22 | -------------------------------------------------------------------------------- /portable_concurrency/bits/closable_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace portable_concurrency { 8 | inline namespace cxx14_v1 { 9 | namespace detail { 10 | 11 | template class closable_queue { 12 | public: 13 | bool pop(T &dest); 14 | void push(T &&val); 15 | void close(); 16 | 17 | private: 18 | std::mutex mutex_; 19 | std::condition_variable cv_; 20 | std::queue queue_; 21 | bool closed_ = false; 22 | }; 23 | 24 | } // namespace detail 25 | } // namespace cxx14_v1 26 | } // namespace portable_concurrency 27 | -------------------------------------------------------------------------------- /portable_concurrency/future: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | #pragma once 3 | 4 | /** 5 | * @defgroup future_hdr 6 | * @headerfile portable_concurrency/future 7 | * 8 | * `future` API imlementation with continuations and executors support 9 | */ 10 | 11 | #include "bits/algo_adapters.h" 12 | #include "bits/alias_namespace.h" 13 | #include "bits/async.h" 14 | #include "bits/future.hpp" 15 | #include "bits/make_future.h" 16 | #include "bits/packaged_task.h" 17 | #include "bits/promise.h" 18 | #include "bits/shared_future.hpp" 19 | #include "bits/when_all.h" 20 | #include "bits/when_any.h" 21 | -------------------------------------------------------------------------------- /examples/coroutine/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "coro_timer.h" 8 | 9 | using namespace std::literals; 10 | 11 | pc::future foo() { 12 | std::string_view hello = "Hello Coroutine World\n"; 13 | for (char c : hello) { 14 | co_await 300ms; 15 | std::cout << c << std::flush; 16 | } 17 | co_return hello.size(); 18 | } 19 | 20 | pc::future bar() { std::cout << co_await foo() << " symbols printed\n"; } 21 | 22 | int main() { 23 | auto res = bar(); 24 | std::this_thread::sleep_for(4s); 25 | res.get(); 26 | return EXIT_SUCCESS; 27 | } 28 | -------------------------------------------------------------------------------- /portable_concurrency/bits/alias_namespace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace portable_concurrency {} 4 | 5 | #if !defined(PORTABLE_CONCURRENCY_ALIAS_NS) 6 | #define PORTABLE_CONCURRENCY_ALIAS_NS 1 7 | #endif 8 | 9 | #if PORTABLE_CONCURRENCY_ALIAS_NS 10 | /** 11 | * @namespace pc 12 | * @brief Shortcut for the library public namespace `portable_concurrency`. 13 | */ 14 | namespace pc = portable_concurrency; 15 | #endif 16 | 17 | /** 18 | * @namespace portable_concurrency 19 | * @brief The library public namespace. 20 | * 21 | * This namespace name is long and too verbose. There is an alias namespace @ref 22 | * pc provided which simplifies this library usage. 23 | */ 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | sudo: required 4 | services: 5 | - docker 6 | 7 | env: 8 | - IMG=cxx-conan-gcc-5 LIBCXX=libstdc++11 TYPE=Release 9 | - IMG=cxx-conan-gcc:9 LIBCXX=libstdc++11 TYPE=UBsan 10 | - IMG=cxx-conan-clang-3.9 LIBCXX=libc++ TYPE=Release 11 | - IMG=cxx-conan-clang:8 LIBCXX=libc++ TYPE=Release 12 | 13 | matrix: 14 | include: 15 | - 16 | if: tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ 17 | env: 18 | - IMG=cxx-conan-gcc-8 LIBCXX=libstdc++11 TYPE=Release 19 | - IMG=cxx-conan-clang-7 LIBCXX=libstdc++11 TYPE=Release 20 | 21 | before_install: 22 | - docker pull sirvestnik/${IMG} 23 | 24 | script: 25 | - docker run -v ${PWD}:/home/builder/src sirvestnik/${IMG} ${LIBCXX} ${TYPE} 26 | -------------------------------------------------------------------------------- /.travis/cxx-conan-gcc-5.dockerfile: -------------------------------------------------------------------------------- 1 | FROM sirvestnik/cxx-conan-base:latest 2 | 3 | RUN echo "deb http://mirror.yandex.ru/debian sid main" > /etc/apt/sources.list.d/10-sid-backports.list && \ 4 | echo "APT::Default-Release "stable";" > /etc/apt/apt.conf.d/10-default-release && \ 5 | apt-get update && \ 6 | apt-get -y --no-install-recommends -t sid install gcc-5 g++-5 libstdc++-5-dev && \ 7 | apt-get -y clean && \ 8 | rm -rf /var/lib/apt/lists 9 | 10 | VOLUME /home/builder/src 11 | USER builder 12 | ENV CC gcc-5 13 | ENV CXX g++-5 14 | RUN mkdir /home/builder/build && conan profile new --detect default 15 | WORKDIR /home/builder/build 16 | 17 | CMD ["libstdc++11", "Release"] 18 | ENTRYPOINT ["/home/builder/src/.travis/autobuild.sh"] 19 | -------------------------------------------------------------------------------- /.travis/cxx-conan-gcc-9.dockerfile: -------------------------------------------------------------------------------- 1 | FROM sirvestnik/cxx-conan-base:latest 2 | 3 | RUN echo "deb http://mirror.yandex.ru/debian sid main" > /etc/apt/sources.list.d/10-sid-backports.list && \ 4 | echo "APT::Default-Release "stable";" > /etc/apt/apt.conf.d/10-default-release && \ 5 | apt-get update && \ 6 | apt-get -y --no-install-recommends -t sid install \ 7 | gcc-9 g++-9 libstdc++-9-dev libgcc-9-dev \ 8 | && \ 9 | apt-get -y clean && \ 10 | rm -rf /var/lib/apt/lists 11 | 12 | VOLUME /home/builder/src 13 | USER builder 14 | ENV CC gcc-9 15 | ENV CXX g++-9 16 | RUN mkdir /home/builder/build && conan profile new --detect default 17 | WORKDIR /home/builder/build 18 | 19 | CMD ["libstdc++11", "Release"] 20 | ENTRYPOINT ["/home/builder/src/.travis/autobuild.sh"] 21 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Ugly hack to make find_packages(portable_concurrency) work in examples 2 | configure_file( 3 | portable_concurrency.cmake 4 | ${CMAKE_CURRENT_BINARY_DIR}/portable_concurrency/portable_concurrency.cmake 5 | COPYONLY 6 | ) 7 | configure_file( 8 | ${portable_concurrency_SOURCE_DIR}/portable_concurrency-config.cmake 9 | ${CMAKE_CURRENT_BINARY_DIR}/portable_concurrency/portable_concurrency-config.cmake 10 | COPYONLY 11 | ) 12 | list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}") 13 | 14 | if (cxx_std_20 IN_LIST CMAKE_CXX_COMPILE_FEATURES) 15 | add_subdirectory(coroutine) 16 | endif() 17 | 18 | find_package(Qt6Core QUIET) 19 | if (Qt6Core_FOUND) 20 | add_subdirectory(qt) 21 | endif() 22 | 23 | find_package(asio QUIET) 24 | if (asio_FOUND) 25 | add_subdirectory(asio) 26 | endif() 27 | -------------------------------------------------------------------------------- /portable_concurrency/bits/continuations_stack.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "once_consumable_stack.h" 4 | #include "small_unique_function.hpp" 5 | 6 | namespace portable_concurrency { 7 | inline namespace cxx14_v1 { 8 | namespace detail { 9 | 10 | using continuation = small_unique_function; 11 | extern template struct forward_list_deleter; 12 | extern template class once_consumable_stack; 13 | 14 | class continuations_stack { 15 | public: 16 | void push(continuation &&cnt); 17 | template void push(continuation &&cnt, const Alloc &alloc) { 18 | if (!stack_.push(cnt, alloc)) 19 | cnt(); 20 | } 21 | 22 | void execute(); 23 | bool executed() const; 24 | 25 | private: 26 | once_consumable_stack stack_; 27 | }; 28 | 29 | } // namespace detail 30 | } // namespace cxx14_v1 31 | } // namespace portable_concurrency 32 | -------------------------------------------------------------------------------- /.travis/cxx-conan-base.dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:buster 2 | 3 | RUN echo "deb http://mirror.yandex.ru/debian buster-backports main" > /etc/apt/sources.list.d/20-backports.list && \ 4 | apt-get update && \ 5 | apt-get install -y --no-install-recommends -t buster-backports wget && \ 6 | mkdir -p /opt/cmake && \ 7 | cd /opt/cmake && \ 8 | wget --no-check-certificate https://cmake.org/files/v3.9/cmake-3.9.6-Linux-x86_64.tar.gz && \ 9 | tar -xzf cmake-3.9.6-Linux-x86_64.tar.gz && \ 10 | ln -fs /opt/cmake/cmake-3.9.6-Linux-x86_64/bin/cmake /usr/bin/cmake && \ 11 | apt-get install -y --no-install-recommends ninja-build python3 python3-pip python3-setuptools && \ 12 | pip3 install conan && \ 13 | useradd -ms /bin/bash builder && \ 14 | apt-get -y --purge remove python3-pip wget && \ 15 | apt-get -y --purge autoremove && \ 16 | apt-get -y clean && \ 17 | rm -rf /var/lib/apt/lists 18 | -------------------------------------------------------------------------------- /examples/qt/PCFutureWatcher.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "PCFutureWatcher.h" 5 | 6 | struct PCFutureWatcher::SafeRef { 7 | SafeRef(PCFutureWatcher* obj) : ref(obj) {} 8 | 9 | PCFutureWatcher* ref; 10 | QMutex mutex; 11 | }; 12 | 13 | PCFutureWatcher::PCFutureWatcher(QObject* parent) : QObject(parent) {} 14 | 15 | PCFutureWatcher::~PCFutureWatcher() { detachCurrRef(); } 16 | 17 | void PCFutureWatcher::notify(SafeRef& ref) { 18 | QMutexLocker lock{&ref.mutex}; 19 | if (!ref.ref) 20 | return; 21 | QMetaObject::invokeMethod(ref.ref, "finished", Qt::QueuedConnection); 22 | } 23 | 24 | QSharedPointer PCFutureWatcher::createNewRef() { 25 | detachCurrRef(); 26 | return cur_ref_ = QSharedPointer::create(this); 27 | } 28 | 29 | void PCFutureWatcher::detachCurrRef() { 30 | if (!cur_ref_) 31 | return; 32 | QMutexLocker{&cur_ref_->mutex}, cur_ref_->ref = nullptr; 33 | cur_ref_.reset(); 34 | } 35 | -------------------------------------------------------------------------------- /portable_concurrency/bits/coro.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__cpp_impl_coroutine) 4 | #if __has_include() 5 | #include 6 | namespace portable_concurrency { 7 | inline namespace cxx14_v1 { 8 | namespace detail { 9 | using suspend_never = std::suspend_never; 10 | template 11 | using coroutine_handle = std::coroutine_handle; 12 | } // namespace detail 13 | } // namespace cxx14_v1 14 | } // namespace portable_concurrency 15 | #define PC_HAS_COROUTINES 16 | #endif 17 | #elif defined(__cpp_coroutines) 18 | #include 19 | namespace portable_concurrency { 20 | inline namespace cxx14_v1 { 21 | namespace detail { 22 | using suspend_never = std::experimental::suspend_never; 23 | template 24 | using coroutine_handle = std::experimental::coroutine_handle; 25 | #define PC_HAS_COROUTINES 26 | } // namespace detail 27 | } // namespace cxx14_v1 28 | } // namespace portable_concurrency 29 | #endif 30 | -------------------------------------------------------------------------------- /portable_concurrency/bits/fwd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace portable_concurrency { 6 | inline namespace cxx14_v1 { 7 | 8 | struct canceler_arg_t {}; 9 | constexpr canceler_arg_t canceler_arg = {}; 10 | 11 | enum class future_status { ready, timeout }; 12 | 13 | template class future; 14 | template class packaged_task; 15 | template class promise; 16 | template class shared_future; 17 | 18 | namespace detail { 19 | template struct future_state; 20 | 21 | template std::shared_ptr> &state_of(future &); 22 | template std::shared_ptr> state_of(future &&); 23 | 24 | template 25 | std::shared_ptr> &state_of(shared_future &); 26 | template 27 | std::shared_ptr> state_of(shared_future &&); 28 | } // namespace detail 29 | 30 | } // namespace cxx14_v1 31 | } // namespace portable_concurrency 32 | -------------------------------------------------------------------------------- /portable_concurrency/bits/closable_queue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "closable_queue.h" 4 | 5 | namespace portable_concurrency { 6 | inline namespace cxx14_v1 { 7 | namespace detail { 8 | 9 | template bool closable_queue::pop(T &dest) { 10 | std::unique_lock lock(mutex_); 11 | cv_.wait(lock, [this]() { return closed_ || !queue_.empty(); }); 12 | if (closed_ && queue_.empty()) 13 | return false; 14 | std::swap(dest, queue_.front()); 15 | queue_.pop(); 16 | return true; 17 | } 18 | 19 | template void closable_queue::push(T &&val) { 20 | std::lock_guard guard(mutex_); 21 | if (closed_) 22 | return; 23 | queue_.emplace(std::move(val)); 24 | cv_.notify_one(); 25 | } 26 | 27 | template void closable_queue::close() { 28 | std::lock_guard guard(mutex_); 29 | closed_ = true; 30 | cv_.notify_all(); 31 | } 32 | 33 | } // namespace detail 34 | } // namespace cxx14_v1 35 | } // namespace portable_concurrency 36 | -------------------------------------------------------------------------------- /.travis/cxx-conan-clang-8.dockerfile: -------------------------------------------------------------------------------- 1 | FROM sirvestnik/cxx-conan-base 2 | 3 | RUN apt-get update && \ 4 | apt-get -y --no-install-recommends install wget gnupg2 && \ 5 | echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-8 main" > /etc/apt/sources.list.d/50-llvm-8.list && \ 6 | echo "APT::Default-Release "stable";" > /etc/apt/apt.conf.d/10-default-release && \ 7 | wget -O - 'https://apt.llvm.org/llvm-snapshot.gpg.key' | apt-key add - && \ 8 | apt-get update && \ 9 | apt-get -y --no-install-recommends install clang-8 libc++-8-dev libc++abi-8-dev && \ 10 | cd /home/builder && \ 11 | apt-get -y purge wget gnupg2 && \ 12 | apt-get -y autoremove && \ 13 | apt-get -y clean && \ 14 | rm -rf /var/lib/apt/lists 15 | 16 | VOLUME /home/builder/src 17 | USER builder 18 | ENV CC clang-8 19 | ENV CXX clang++-8 20 | RUN mkdir /home/builder/build && conan profile new --detect default 21 | WORKDIR /home/builder/build 22 | 23 | CMD ["libstdc++11", "Release"] 24 | ENTRYPOINT ["/home/builder/src/.travis/autobuild.sh"] 25 | -------------------------------------------------------------------------------- /examples/qt/PCFutureWatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | class PCFutureWatcher : public QObject { 9 | Q_OBJECT 10 | public: 11 | PCFutureWatcher(QObject* parent = nullptr); 12 | ~PCFutureWatcher(); 13 | 14 | template 15 | void setFuture(pc::future& future) { 16 | future = future.then([ref = createNewRef()](pc::future ready) { 17 | PCFutureWatcher::notify(*ref); 18 | return ready; 19 | }); 20 | } 21 | 22 | template 23 | void setFuture(pc::shared_future& future) { 24 | future = future.then([ref = createNewRef()](pc::future ready) { 25 | PCFutureWatcher::notify(*ref); 26 | return ready; 27 | }); 28 | } 29 | 30 | signals: 31 | void finished(); 32 | 33 | private: 34 | struct SafeRef; 35 | 36 | static void notify(SafeRef& ref); 37 | QSharedPointer createNewRef(); 38 | void detachCurrRef(); 39 | 40 | private: 41 | QSharedPointer cur_ref_; 42 | }; 43 | -------------------------------------------------------------------------------- /cmake/FindCoroutinesTS.cmake: -------------------------------------------------------------------------------- 1 | if (CoroutinesTS_FOUND) 2 | return() 3 | endif() 4 | 5 | include(CheckIncludeFileCXX) 6 | 7 | if (NOT cxx_std_17 IN_LIST CMAKE_CXX_COMPILE_FEATURES) 8 | if (CoroutinesTS_FIND_REQUIRED) 9 | message(FATAL_ERROR "C++17 support is not available") 10 | elseif(NOT CoroutinesTS_FIND_QUIETLY) 11 | message(STATUS "C++17 support is not available") 12 | endif() 13 | return() 14 | endif() 15 | 16 | check_include_file_cxx(experimental/coroutine CLANG_COROUTINES_FOUND "-fcoroutines-ts") 17 | 18 | if (CLANG_COROUTINES_FOUND) 19 | add_library(coroutines::coroutines-ts INTERFACE IMPORTED) 20 | set_target_properties(coroutines::coroutines-ts PROPERTIES INTERFACE_COMPILE_OPTIONS -fcoroutines-ts) 21 | set(CoroutinesTS_FOUND YES) 22 | message(STATUS "Clang implementation of the CoroutinesTS detected") 23 | return() 24 | endif() 25 | 26 | if (CoroutinesTS_FIND_REQUIRED) 27 | message(FATAL_ERROR "No CoroutinesTS provided by the compiler") 28 | elseif(NOT CoroutinesTS_FIND_QUIETLY) 29 | message(STATUS "No CoroutinesTS provided by the compiler") 30 | endif() 31 | -------------------------------------------------------------------------------- /.travis/cxx-conan-clang-3.9.dockerfile: -------------------------------------------------------------------------------- 1 | FROM sirvestnik/cxx-conan-base 2 | 3 | RUN echo "deb http://mirror.yandex.ru/debian sid main" > /etc/apt/sources.list.d/10-sid-backports.list && \ 4 | echo "deb http://dl.bintray.com/pdeps/libcxx-versions stretch main" > /etc/apt/sources.list.d/20-libcxx-versions.list && \ 5 | echo "APT::Default-Release "stable";" > /etc/apt/apt.conf.d/10-default-release && \ 6 | apt-get update && \ 7 | apt-get -y --no-install-recommends --allow-unauthenticated install -t sid \ 8 | clang-3.9 libc++-dev=3.9.1-1 libc++abi-dev=3.9.1-1 libc++1=3.9.1-1 libc++abi1=3.9.1-1 libc6-dev && \ 9 | cd /home/builder && \ 10 | apt-get -y autoremove && \ 11 | apt-get -y clean && \ 12 | rm -rf /var/lib/apt/lists && \ 13 | ln -s /usr/include/locale.h /usr/include/xlocale.h 14 | 15 | VOLUME /home/builder/src 16 | USER builder 17 | ENV CC clang-3.9 18 | ENV CXX clang++-3.9 19 | RUN mkdir /home/builder/build && conan profile new --detect default 20 | WORKDIR /home/builder/build 21 | 22 | CMD ["libstdc++11", "Release"] 23 | ENTRYPOINT ["/home/builder/src/.travis/autobuild.sh"] 24 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | 3 | environment: 4 | global: 5 | CTEST_OUTPUT_ON_FAILURE: True 6 | matrix: 7 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 8 | VS_GENERATOR: Visual Studio 14 2015 9 | CONAN_VC_VER: 14 10 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 11 | VS_GENERATOR: Visual Studio 15 2017 12 | CONAN_VC_VER: 15 13 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 14 | VS_GENERATOR: Visual Studio 16 2019 15 | CONAN_VC_VER: 16 16 | 17 | install: 18 | 19 | - ps: >- 20 | $Env:PATH = "C:\Python38-x64;C:\Python38-x64\Scripts;$Env:PATH" 21 | python -m pip install conan 22 | mkdir build 23 | cd build 24 | conan install -o gtest:shared=False -s arch=x86_64 -s compiler="Visual Studio" -s compiler.version=$Env:CONAN_VC_VER -s compiler.runtime=MT -s build_type=Release --build=missing .. 25 | 26 | build_script: 27 | - ps: >- 28 | cmake -G "$Env:VS_GENERATOR" -A x64 -DCONAN=On -DBUILD_SHARED_LIBS=Off .. 29 | cmake --build . --config ReleaseStatic 30 | 31 | test_script: 32 | - ps: cmake --build . --config ReleaseStatic --target RUN_TESTS 33 | 34 | -------------------------------------------------------------------------------- /.travis/autobuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | SRCDIR=$(dirname $(dirname $0)) 4 | 5 | export LIBCXX=$1 6 | export BUILD_TYPE=$2 7 | export DEPS_BUILD_TYPE=${BUILD_TYPE} 8 | export CTEST_OUTPUT_ON_FAILURE=True 9 | 10 | export LSAN_OPTIONS=verbosity=1:log_threads=1 11 | 12 | if [ "${BUILD_TYPE}" == "UBsan" ] || [ "${BUILD_TYPE}" == "Asan" ] || [ "${BUILD_TYPE}" == "Tsan" ] 13 | then 14 | export DEPS_BUILD_TYPE=Debug 15 | fi 16 | 17 | export CONAN_CMAKE_GENERATOR=Ninja 18 | 19 | cmake --version 20 | conan --version 21 | conan profile update settings.compiler.libcxx="${LIBCXX}" default 22 | conan profile update settings.build_type="${DEPS_BUILD_TYPE}" default 23 | # dump toolchain info 24 | conan profile show default 25 | 26 | conan remote add VestniK https://api.bintray.com/conan/pdeps/deps 27 | conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan 28 | conan install --build=missing ${SRCDIR} 29 | 30 | cmake -G Ninja ${SRCDIR} \ 31 | -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ 32 | -DCONAN_LIBCXX=${LIBCXX} \ 33 | -DCONAN_COMPILER=$(conan profile get settings.compiler default) \ 34 | -DCONAN_COMPILER_VERSION=$(conan profile get settings.compiler.version default) \ 35 | -DPC_DEV_BUILD=ON 36 | time ninja 37 | ninja test 38 | -------------------------------------------------------------------------------- /portable_concurrency/bits/latch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace portable_concurrency { 8 | inline namespace cxx14_v1 { 9 | 10 | /** 11 | * @headerfile portable_concurrency/latch 12 | * @ingroup latch 13 | * 14 | * The latch class is a downward counter of type ptrdiff_t which can be used to 15 | * synchronize threads. The value of the counter is initialized on creation. 16 | * Threads may block on the latch until the counter is decremented to zero. 17 | * There is no possibility to increase or reset the counter, which makes the 18 | * latch a single-use barrier. 19 | */ 20 | class latch { 21 | public: 22 | explicit latch(ptrdiff_t count) : counter_(count) {} 23 | 24 | latch(const latch &) = delete; 25 | latch &operator=(const latch &) = delete; 26 | 27 | ~latch(); 28 | 29 | void count_down_and_wait(); 30 | void count_down(ptrdiff_t n = 1); 31 | bool is_ready() const noexcept; 32 | void wait() const; 33 | 34 | private: 35 | ptrdiff_t counter_; 36 | mutable unsigned waiters_ = 0; 37 | mutable std::mutex mutex_; 38 | mutable std::condition_variable cv_; 39 | }; 40 | 41 | } // namespace cxx14_v1 42 | } // namespace portable_concurrency 43 | -------------------------------------------------------------------------------- /examples/qt/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "PCFutureWatcher.h" 13 | 14 | namespace portable_concurrency { 15 | template <> 16 | struct is_executor : std::true_type {}; 17 | } // namespace portable_concurrency 18 | 19 | void post(QThreadPool* exec, pc::unique_function func) { 20 | struct PCRunnable : QRunnable { 21 | PCRunnable(pc::unique_function&& func) noexcept : func{std::move(func)} {} 22 | 23 | void run() override { func(); } 24 | 25 | pc::unique_function func; 26 | }; 27 | exec->start(new PCRunnable{std::move(func)}); 28 | } 29 | 30 | int main(int argc, char** argv) { 31 | QCoreApplication app{argc, argv}; 32 | 33 | pc::future f = pc::async(QThreadPool::globalInstance(), []() { 34 | QThread::msleep(800); 35 | return QStringLiteral("Hello Qt Concurent"); 36 | }); 37 | PCFutureWatcher watcher; 38 | QObject::connect(&watcher, &PCFutureWatcher::finished, [&f]() { qInfo() << f.get(); }); 39 | QObject::connect(&watcher, &PCFutureWatcher::finished, &app, &QCoreApplication::quit); 40 | watcher.setFuture(f); 41 | 42 | return app.exec(); 43 | } 44 | -------------------------------------------------------------------------------- /test/algo_adapters.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace { 9 | 10 | template