├── .bazelrc ├── .gitignore ├── Test ├── Mocks │ ├── dummy.cpp │ ├── BUILD.bazel │ ├── CMakeLists.txt │ └── asyncly │ │ └── MockCancelable.h ├── CMakeLists.txt ├── Header │ └── CMakeLists.txt ├── Performance │ ├── BUILD.bazel │ ├── CMakeLists.txt │ └── Executor │ │ ├── ExecutorBenchmarksMain.cpp │ │ ├── ExecutorWrappersPerformance.cpp │ │ └── ThreadPoolPerformanceTest.cpp ├── Unit │ ├── BUILD.bazel │ ├── observable │ │ └── IObservableInterface.h │ ├── task │ │ └── AutoCancellableTest.cpp │ ├── InterfaceForExecutorTest.h │ ├── detail │ │ ├── PrometheusTestHelper.h │ │ ├── PrometheusTestHelper.cpp │ │ └── ThrowingExecutor.h │ ├── tests_main.cpp │ ├── StrandImplTestFactory.h │ ├── CMakeLists.txt │ ├── future │ │ └── LazyValueTest.cpp │ ├── ExceptionShieldTest.cpp │ ├── StrandTest.cpp │ └── PeriodicTaskTest.cpp └── Utils │ ├── BUILD.bazel │ ├── Source │ ├── TimeoutGuardEnvironment.cpp │ ├── TimeoutGuard.h │ ├── TimeoutGuard.cpp │ └── WindowsTimerResolution.cpp │ ├── CMakeLists.txt │ └── Include │ └── asyncly │ └── test │ ├── WindowsTimerResolution.h │ ├── TimeoutGuardEnvironment.h │ ├── CurrentExecutorGuard.h │ ├── SchedulerProvider.h │ ├── IFakeExecutor.h │ ├── PostAndWait.h │ ├── ConvertExecutorFutureToStdFuture.h │ ├── ExecutorTestFactories.h │ └── FutureTest.h ├── logo_transparent.png ├── Bazel ├── BUILD.bazel ├── function2.BUILD └── repositories.bzl ├── CMake ├── test_headercompile.cpp.in ├── asyncly-config.cmake.in └── AsynclyBuild.cmake ├── Docs ├── build_docs.sh ├── generate_docs.sh ├── Dockerfile └── doxyrest-config.lua ├── WORKSPACE.bazel ├── .clang-format ├── BUILD.bazel ├── Contributing.md ├── COPYRIGHT ├── Include └── asyncly │ ├── future │ ├── Futurize.h │ ├── WhenThen.h │ ├── WhenAny.h │ ├── detail │ │ ├── FutureTraits.h │ │ └── Coroutine.h │ ├── Split.h │ ├── WhenAll.h │ ├── LazyOneTimeInitializer.h │ └── LazyValue.h │ ├── Observable.h │ ├── executor │ ├── IStrand.h │ ├── ExceptionShield.h │ ├── MetricsWrapper.h │ ├── IExecutorController.h │ ├── ISteadyClock.h │ ├── ExecutorStoppedException.h │ ├── Strand.h │ ├── IExecutor.h │ ├── InlineExecutor.h │ ├── AsioExecutorController.h │ ├── detail │ │ ├── AsioExecutor.h │ │ ├── ExternalEventExecutor.h │ │ └── ExecutorMetrics.h │ ├── ThreadPoolExecutorController.h │ ├── CurrentExecutor.h │ └── ExternalEventExecutorController.h │ ├── task │ ├── RepeatableTask.h │ ├── detail │ │ ├── TaskConcept.h │ │ ├── TaskCurrentExecutorGuard.h │ │ ├── TaskCancelable.h │ │ ├── TaskWrapper.h │ │ └── PeriodicTask.h │ ├── AutoCancelable.h │ ├── Cancelable.h │ ├── CancelableTask.h │ └── Task.h │ ├── Future.h │ ├── scheduler │ ├── IRunnableScheduler.h │ ├── IScheduler.h │ ├── detail │ │ ├── AsioTimerHandler.h │ │ ├── AsioTimerTask.h │ │ └── Sleep.h │ ├── SchedulerThread.h │ ├── PriorityQueue.h │ ├── AsioScheduler.h │ └── DefaultScheduler.h │ ├── observable │ ├── Subscription.h │ └── detail │ │ └── Subscription.h │ └── ExecutorTypes.h ├── Source ├── executor │ ├── Strand.cpp │ ├── detail │ │ ├── MetricsTask.h │ │ ├── StrandImpl.h │ │ ├── MetricsTask.cpp │ │ ├── AsioExecutor.cpp │ │ └── StrandImpl.cpp │ ├── InlineExecutor.cpp │ ├── AsioExecutorController.cpp │ ├── ExternalEventExecutorController.cpp │ ├── CurrentExecutor.cpp │ └── ThreadPoolExecutorController.cpp └── task │ └── detail │ ├── TaskCurrentExecutorGuard.cpp │ └── PeriodicTask.cpp ├── Readme.md ├── CMakeLists.txt └── .github └── workflows └── continuous-integration-workflow.yml /.bazelrc: -------------------------------------------------------------------------------- 1 | build --announce_rc 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-* 2 | _docs 3 | -------------------------------------------------------------------------------- /Test/Mocks/dummy.cpp: -------------------------------------------------------------------------------- 1 | int asyncly_mocks_dummy; 2 | -------------------------------------------------------------------------------- /logo_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goto-opensource/asyncly/HEAD/logo_transparent.png -------------------------------------------------------------------------------- /Bazel/BUILD.bazel: -------------------------------------------------------------------------------- 1 | exports_files( 2 | glob([ 3 | "*.BUILD", 4 | ]), 5 | visibility = ["//visibility:public"], 6 | ) 7 | -------------------------------------------------------------------------------- /Test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Header) 2 | add_subdirectory(Mocks) 3 | add_subdirectory(Performance) 4 | add_subdirectory(Utils) 5 | add_subdirectory(Unit) 6 | -------------------------------------------------------------------------------- /Bazel/function2.BUILD: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "function2", 3 | hdrs = ["include/function2/function2.hpp"], 4 | includes = ["include"], 5 | visibility = ["//visibility:public"], 6 | ) 7 | -------------------------------------------------------------------------------- /Test/Header/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | asyncly_add_self_contained_header_test(test_asyncly_self_contained "../../Include") 2 | target_link_libraries(test_asyncly_self_contained PRIVATE asyncly GTest::gmock) 3 | 4 | -------------------------------------------------------------------------------- /CMake/test_headercompile.cpp.in: -------------------------------------------------------------------------------- 1 | #include <@HEADER@> 2 | 3 | // check for header guard presence 4 | #include <@HEADER@> 5 | 6 | // silence warnings about empty object files 7 | int @UNIQUE_PART@_self_contained_header_check; 8 | -------------------------------------------------------------------------------- /Test/Performance/BUILD.bazel: -------------------------------------------------------------------------------- 1 | cc_binary( 2 | name = "asyncly_benchmarks", 3 | srcs = glob(["**/*.cpp"]), 4 | deps = [ 5 | "//:asyncly", 6 | "@com_github_google_benchmark//:benchmark", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /Docs/build_docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -x 5 | 6 | cd /build 7 | cp Docs/conf.py ${TMP_RST_DIR} 8 | doxygen 9 | ${DOXYREST_BIN_DIR}/doxyrest -c Docs/doxyrest-config.lua 10 | sphinx-build -b html $TMP_RST_DIR $OUTPUT_HTML_DIR 11 | cp logo_transparent.png $OUTPUT_HTML_DIR 12 | -------------------------------------------------------------------------------- /Docs/generate_docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | readonly PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && /bin/pwd -P) 6 | 7 | docker build "${PROJECT_ROOT}" -t executordocs 8 | docker run -v "${PROJECT_ROOT}/..":/build --rm executordocs 9 | docker rmi executordocs 10 | -------------------------------------------------------------------------------- /Test/Mocks/BUILD.bazel: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "asyncly_mocks", 3 | hdrs = glob(["asyncly/**/*.h"]), 4 | includes = ["."], 5 | linkstatic = True, 6 | visibility = ["//visibility:public"], 7 | deps = [ 8 | "//:asyncly", 9 | "@com_google_googletest//:gtest", 10 | ], 11 | ) 12 | -------------------------------------------------------------------------------- /CMake/asyncly-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | include(CMakeFindDependencyMacro) 3 | 4 | set_and_check(asyncly_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") 5 | 6 | find_dependency(Boost 1.71.0) 7 | find_dependency(function2) 8 | find_dependency(prometheus-cpp) 9 | 10 | include("${CMAKE_CURRENT_LIST_DIR}/asyncly-targets.cmake") 11 | -------------------------------------------------------------------------------- /Test/Mocks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(asyncly_mocks EXCLUDE_FROM_ALL 3 | 4 | # Enforce creation of a library for iOS, Mac 5 | dummy.cpp 6 | 7 | asyncly/MockCancelable.h 8 | ) 9 | 10 | 11 | target_include_directories(asyncly_mocks PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 12 | 13 | target_link_libraries(asyncly_mocks PUBLIC asyncly) 14 | target_link_libraries(asyncly_mocks PUBLIC GTest::gmock) 15 | -------------------------------------------------------------------------------- /WORKSPACE.bazel: -------------------------------------------------------------------------------- 1 | workspace(name = "com_github_logmein_asyncly") 2 | 3 | load("//Bazel:repositories.bzl", "asyncly_repositories") 4 | 5 | asyncly_repositories() 6 | 7 | load("@com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps") 8 | 9 | boost_deps() 10 | 11 | load("@com_github_jupp0r_prometheus_cpp//bazel:repositories.bzl", "prometheus_cpp_repositories") 12 | 13 | prometheus_cpp_repositories() 14 | -------------------------------------------------------------------------------- /Test/Performance/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | asyncly_add_google_benchmark(asyncly_benchmarks 2 | EXCLUDE_FROM_TEST 3 | SOURCES 4 | Executor/ExecutorBenchmarksMain.cpp 5 | Executor/ExecutorWrappersPerformance.cpp 6 | Executor/ThreadPoolPerformanceTest.cpp) 7 | 8 | 9 | target_include_directories(asyncly_benchmarks PRIVATE ${PROJECT_SOURCE_DIR}/Source) 10 | 11 | target_link_libraries(asyncly_benchmarks asyncly) 12 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | NamespaceIndentation: None 4 | DerivePointerAlignment: false 5 | PointerAlignment: Left 6 | ColumnLimit: 100 7 | Standard: Cpp11 8 | AccessModifierOffset: -2 9 | AllowShortFunctionsOnASingleLine: false 10 | BinPackArguments: false 11 | BinPackParameters: false 12 | AlignAfterOpenBracket: AlwaysBreak 13 | SpaceBeforeCpp11BracedList: false 14 | AllowShortBlocksOnASingleLine: Never 15 | --- 16 | -------------------------------------------------------------------------------- /Test/Unit/BUILD.bazel: -------------------------------------------------------------------------------- 1 | cc_test( 2 | name = "test_asyncly", 3 | size = "medium", 4 | srcs = glob( 5 | [ 6 | "**/*.cpp", 7 | "**/*.h", 8 | ], 9 | ), 10 | includes = [ 11 | ".", 12 | ], 13 | deps = [ 14 | "//:asyncly", 15 | "//Test/Mocks:asyncly_mocks", 16 | "//Test/Utils:asyncly_test_utils", 17 | "@com_google_googletest//:gtest_main", 18 | ], 19 | ) 20 | -------------------------------------------------------------------------------- /Test/Utils/BUILD.bazel: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | cc_library( 4 | name = "asyncly_test_utils", 5 | srcs = glob([ 6 | "Source/**/*.cpp", 7 | "Source/**/*.h", 8 | ]), 9 | hdrs = glob([ 10 | "Include/**/*.h", 11 | ]), 12 | includes = [ 13 | "Include", 14 | ], 15 | linkstatic = True, 16 | deps = [ 17 | "//:asyncly", 18 | "@boost//:asio", 19 | "@com_google_googletest//:gtest", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | filegroup( 4 | name = "asyncly_Include", 5 | srcs = glob(["Include/**/*.h"]), 6 | ) 7 | 8 | cc_library( 9 | name = "asyncly", 10 | srcs = glob([ 11 | "Source/**/*.cpp", 12 | "Source/**/*.h", 13 | ]), 14 | hdrs = [":asyncly_Include"], 15 | defines = ["WIN32_LEAN_AND_MEAN"], 16 | includes = [ 17 | "Include", 18 | "Source", 19 | ], 20 | linkstatic = True, 21 | deps = [ 22 | "@boost//:asio", 23 | "@boost//:callable_traits", 24 | "@boost//:hana", 25 | "@boost//:mp11", 26 | "@com_github_jupp0r_prometheus_cpp//core", 27 | "@com_github_naios_function2//:function2", 28 | ], 29 | ) 30 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Asyncly on GitHub 2 | 3 | We gladly take pull requests on GitHub. Please follow these guidelines to make 4 | it easier for us to merge your pull requests: 5 | 6 | 1. make sure to clang-format your changes 7 | 2. although we lack a formal coding style guide, please make sure to follow the 8 | conventions of the code around your change. 9 | 3. after you opened a PR, it will be reviewed by asyncly maintainers. After all 10 | review feedback has been incorporated, we will merge the patch to LogMeIns 11 | internal version of asyncly and it will arrive in GitHub soon after. This is 12 | necessary because we want to run our internal integration test suite against 13 | the change to make sure it doesn't break any production services. 14 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: asyncly 3 | Source: https://github.com/LogMeIn/asyncly 4 | 5 | Files: * 6 | Copyright: 2019 LogMeIn, Inc. 7 | License: Apache-2.0 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | . 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | . 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | -------------------------------------------------------------------------------- /Test/Performance/Executor/ExecutorBenchmarksMain.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | 21 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /Docs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV DOXYREST_DIR /doxyrest 5 | ENV DOXYREST_BIN_DIR ${DOXYREST_DIR}/bin 6 | ENV DOXYGEN_XML_DIR /doxygen_out/xml 7 | ENV TMP_RST_DIR /tmp 8 | ENV OUTPUT_HTML_DIR /build/_docs 9 | 10 | ENV DOXYREST_SPHINX_DIR ${DOXYREST_DIR}/share/doxyrest/sphinx 11 | ENV DOXYREST_FRAME_DIR ${DOXYREST_DIR}/share/doxyrest/frame 12 | 13 | RUN apt-get update && apt-get install -y --no-install-recommends curl doxygen python3-minimal python3-sphinx python3-sphinx-rtd-theme tar xz-utils liblua5.2 14 | RUN mkdir ${DOXYREST_DIR} 15 | RUN curl -sOL https://github.com/vovkos/doxyrest/releases/download/doxyrest-2.1.3/doxyrest-2.1.3-linux-amd64.tar.xz 16 | RUN tar -xJf doxyrest-2.1.3-linux-amd64.tar.xz -C /doxyrest --strip-components=1 17 | 18 | ENTRYPOINT /build/Docs/build_docs.sh 19 | -------------------------------------------------------------------------------- /Include/asyncly/future/Futurize.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) GoTo 3 | * All Rights Reserved Worldwide. 4 | * 5 | * THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO GOTO 6 | * AND CONSTITUTES A VALUABLE TRADE SECRET. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "asyncly/future/Future.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace asyncly { 17 | template auto futurize(F&& f, const std::string& error) 18 | { 19 | using R = std::remove_cv_t; 20 | 21 | try { 22 | return asyncly::make_ready_future(f()); 23 | } catch (const std::exception& e) { 24 | return asyncly::make_exceptional_future(error + ": " + e.what()); 25 | } catch (...) { 26 | return asyncly::make_exceptional_future(error); 27 | } 28 | } 29 | } // namespace app_server_client 30 | -------------------------------------------------------------------------------- /Include/asyncly/Observable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/observable/Observable.h" 22 | #include "asyncly/observable/Subscriber.h" 23 | #include "asyncly/observable/Subscription.h" 24 | -------------------------------------------------------------------------------- /Include/asyncly/executor/IStrand.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/IExecutor.h" 22 | 23 | namespace asyncly { 24 | class IStrand : public IExecutor { }; 25 | } // namespace asyncly 26 | -------------------------------------------------------------------------------- /Include/asyncly/task/RepeatableTask.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace asyncly { 24 | 25 | using RepeatableTask = fu2::unique_function; 26 | 27 | } // namespace asyncly 28 | -------------------------------------------------------------------------------- /Test/Mocks/asyncly/MockCancelable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include 24 | 25 | namespace asyncly { 26 | 27 | class MockCancelable : public Cancelable { 28 | public: 29 | MOCK_METHOD0(cancel, bool()); 30 | }; 31 | } // namespace asyncly 32 | -------------------------------------------------------------------------------- /Include/asyncly/task/detail/TaskConcept.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace asyncly::detail { 24 | struct TaskConcept { 25 | virtual ~TaskConcept() = default; 26 | virtual void run() = 0; 27 | virtual explicit operator bool() const = 0; 28 | }; 29 | } // namespace asyncly::detail 30 | -------------------------------------------------------------------------------- /Docs/doxyrest-config.lua: -------------------------------------------------------------------------------- 1 | -- Specify input and output paths: 2 | 3 | OUTPUT_FILE = os.getenv("TMP_RST_DIR") .. "/index.rst" 4 | INPUT_FILE = os.getenv("DOXYGEN_XML_DIR") .. "/index.xml" 5 | FRAME_FILE = "index.rst.in" 6 | FRAME_DIR_LIST = { os.getenv("DOXYREST_FRAME_DIR") .. "/cfamily", os.getenv("DOXYREST_FRAME_DIR") .. "/common" } 7 | 8 | -- Usually, Doxygen-based documentation has a main page (created with 9 | -- the \mainpage directive). If that's the case, force-include 10 | -- the contents of 'page_index.rst' into 'index.rst': 11 | 12 | INTRO_FILE = "page_index.rst" 13 | 14 | -- If your documentation uses \verbatim directives for code snippets 15 | -- you can convert those to reStructuredText C++ code-blocks: 16 | 17 | VERBATIM_TO_CODE_BLOCK = "cpp" 18 | 19 | -- Asterisks, pipes and trailing underscores have special meaning in 20 | -- reStructuredText. If they appear in Doxy-comments anywhere except 21 | -- for code-blocks, they must be escaped: 22 | 23 | ESCAPE_ASTERISKS = true 24 | ESCAPE_PIPES = true 25 | ESCAPE_TRAILING_UNDERSCORES = true -------------------------------------------------------------------------------- /Include/asyncly/Future.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/future/AddTimeout.h" 22 | #include "asyncly/future/Future.h" 23 | #include "asyncly/future/LazyOneTimeInitializer.h" 24 | #include "asyncly/future/Split.h" 25 | #include "asyncly/future/WhenAll.h" 26 | #include "asyncly/future/WhenAny.h" 27 | #include "asyncly/future/WhenThen.h" 28 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/IRunnableScheduler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "IScheduler.h" 22 | 23 | namespace asyncly { 24 | 25 | class IRunnableScheduler : public IScheduler { 26 | public: 27 | ~IRunnableScheduler() override = default; 28 | virtual void run() = 0; 29 | virtual void stop() = 0; 30 | }; 31 | } // namespace asyncly 32 | -------------------------------------------------------------------------------- /Test/Unit/observable/IObservableInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "asyncly/observable/Observable.h" 24 | 25 | namespace asyncly { 26 | 27 | class IObservableInterface { 28 | public: 29 | virtual ~IObservableInterface() = default; 30 | virtual Observable method() = 0; 31 | }; 32 | } // namespace asyncly 33 | -------------------------------------------------------------------------------- /Include/asyncly/executor/ExceptionShield.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/IExecutor.h" 22 | 23 | #include 24 | #include 25 | 26 | namespace asyncly { 27 | 28 | IExecutorPtr create_exception_shield( 29 | const IExecutorPtr& executor, std::function exceptionHandler); 30 | 31 | } // namespace asyncly 32 | -------------------------------------------------------------------------------- /Include/asyncly/executor/MetricsWrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/IExecutor.h" 22 | #include "prometheus/registry.h" 23 | 24 | #include 25 | 26 | namespace asyncly { 27 | 28 | IExecutorPtr create_metrics_wrapper( 29 | const IExecutorPtr& executor, 30 | const std::string& executorLabel, 31 | const std::shared_ptr& registry); 32 | 33 | } // namespace asyncly 34 | -------------------------------------------------------------------------------- /Include/asyncly/executor/IExecutorController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/ExecutorTypes.h" 22 | 23 | #include 24 | 25 | namespace asyncly { 26 | 27 | class IExecutorController { 28 | public: 29 | virtual ~IExecutorController() = default; 30 | virtual void finish() = 0; 31 | virtual IExecutorPtr get_executor() const = 0; 32 | virtual ISchedulerPtr get_scheduler() const = 0; 33 | }; 34 | } // namespace asyncly 35 | -------------------------------------------------------------------------------- /Include/asyncly/executor/ISteadyClock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/ExecutorTypes.h" 22 | 23 | #include 24 | 25 | namespace asyncly { 26 | 27 | class ISteadyClock { 28 | public: 29 | virtual ~ISteadyClock() = default; 30 | 31 | virtual clock_type::time_point now() const = 0; 32 | }; 33 | 34 | using ISteadyClockPtr = std::shared_ptr; 35 | using ISteadyClockUPtr = std::unique_ptr; 36 | 37 | } // namespace asyncly 38 | -------------------------------------------------------------------------------- /Include/asyncly/executor/ExecutorStoppedException.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace asyncly { 24 | 25 | class ExecutorStoppedException : public std::runtime_error { 26 | public: 27 | ExecutorStoppedException(const std::string& message) 28 | : std::runtime_error(message) 29 | { 30 | } 31 | ExecutorStoppedException(const char* message) 32 | : std::runtime_error(message) 33 | { 34 | } 35 | }; 36 | 37 | } // namespace asyncly 38 | -------------------------------------------------------------------------------- /Include/asyncly/executor/Strand.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/ExecutorTypes.h" 22 | 23 | namespace asyncly { 24 | 25 | /** 26 | * Strand implements a serializing queue used to dispatch tasks that have to be executed 27 | * sequentially. 28 | */ 29 | IStrandPtr create_strand(const IExecutorPtr& executor); 30 | IStrandPtr create_strand(const IStrandPtr& strand); 31 | 32 | /** 33 | * \returns wheter the executor is serializing. 34 | */ 35 | bool is_serializing(const IExecutorPtr& executor); 36 | } // namespace asyncly 37 | -------------------------------------------------------------------------------- /Test/Utils/Source/TimeoutGuardEnvironment.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/test/TimeoutGuardEnvironment.h" 20 | 21 | #include "TimeoutGuard.h" 22 | 23 | namespace asyncly::test { 24 | 25 | TimeoutGuardEnviroment::TimeoutGuardEnviroment(std::time_t timeoutInSec) 26 | : m_timeoutGuard(new TimeoutGuard(timeoutInSec)) 27 | { 28 | } 29 | 30 | void TimeoutGuardEnviroment::SetUp() 31 | { 32 | m_timeoutGuard->start(); 33 | } 34 | 35 | void TimeoutGuardEnviroment::TearDown() 36 | { 37 | m_timeoutGuard->stop(); 38 | } 39 | } // namespace asyncly::test 40 | -------------------------------------------------------------------------------- /Test/Unit/task/AutoCancellableTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/MockCancelable.h" 20 | #include "asyncly/task/AutoCancelable.h" 21 | 22 | #include "gmock/gmock.h" 23 | 24 | namespace asyncly { 25 | 26 | using namespace testing; 27 | 28 | TEST(AutoCancelableTest, shouldCallCancelOnDestruction) 29 | { 30 | auto cancelable = std::make_shared(); 31 | auto autoCancelable = std::make_shared(cancelable); 32 | 33 | EXPECT_CALL(*cancelable, cancel()); 34 | autoCancelable.reset(); 35 | } 36 | 37 | } // namespace asyncly 38 | -------------------------------------------------------------------------------- /Test/Utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(asyncly_test_utils 2 | Include/asyncly/test/ConvertExecutorFutureToStdFuture.h 3 | Include/asyncly/test/ExecutorInterfaceTest.h 4 | Include/asyncly/test/ExecutorTestFactories.h 5 | Include/asyncly/test/FakeClockScheduler.h 6 | Include/asyncly/test/FakeExecutor.h 7 | Include/asyncly/test/FakeFutureTest.h 8 | Include/asyncly/test/FutureTest.h 9 | Include/asyncly/test/IFakeExecutor.h 10 | Include/asyncly/test/PostAndWait.h 11 | Include/asyncly/test/SchedulerProvider.h 12 | Include/asyncly/test/WaitForFuture.h 13 | Include/asyncly/test/CurrentExecutorGuard.h 14 | Source/TimeoutGuard.cpp 15 | Source/TimeoutGuard.h 16 | Source/TimeoutGuardEnvironment.cpp 17 | Source/WindowsTimerResolution.cpp 18 | ) 19 | 20 | target_include_directories(asyncly_test_utils PUBLIC $) 21 | target_link_libraries(asyncly_test_utils PUBLIC asyncly GTest::gmock) 22 | 23 | 24 | install(DIRECTORY Include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 25 | install(TARGETS asyncly_test_utils 26 | EXPORT ${PROJECT_NAME}-targets 27 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 28 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 29 | ) 30 | -------------------------------------------------------------------------------- /Include/asyncly/task/AutoCancelable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/task/Cancelable.h" 22 | 23 | #include 24 | 25 | namespace asyncly { 26 | 27 | class AutoCancelable { 28 | public: 29 | AutoCancelable(const std::shared_ptr& cancelable) 30 | : _cancelable(cancelable) 31 | { 32 | } 33 | virtual ~AutoCancelable() 34 | { 35 | _cancelable->cancel(); 36 | } 37 | 38 | private: 39 | const std::shared_ptr _cancelable; 40 | }; 41 | using AutoCancelablePtr = std::shared_ptr; 42 | 43 | } // namespace asyncly 44 | -------------------------------------------------------------------------------- /Include/asyncly/task/Cancelable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | namespace asyncly { 22 | 23 | class Cancelable { 24 | public: 25 | virtual ~Cancelable() = default; 26 | /// 27 | /// Cancels a task. 28 | /// Cancellation cannot be guaranteed to be successful when called 29 | /// within another strand (or thread) than the task-to-be-cancelled is executed on. 30 | /// Reason: Task might already be in execution (or preparation of execution) 31 | /// 32 | /// \return true if cancel succeeded, false otherwise (too late, already cancelled) 33 | virtual bool cancel() = 0; 34 | }; 35 | 36 | } // namespace asyncly 37 | -------------------------------------------------------------------------------- /Include/asyncly/future/WhenThen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/future/Future.h" 22 | 23 | namespace asyncly { 24 | template auto when_then(Future&& when, Promise& then) 25 | { 26 | when.then([then](T val) mutable { then.set_value(val); }) 27 | .catch_error([then](std::exception_ptr e) mutable { then.set_exception(e); }); 28 | } 29 | template <> inline auto when_then(Future&& when, Promise& then) 30 | { 31 | when.then([then]() mutable { then.set_value(); }) 32 | .catch_error([then](std::exception_ptr e) mutable { then.set_exception(e); }); 33 | } 34 | } // namespace asyncly 35 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/WindowsTimerResolution.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace asyncly::test { 24 | 25 | class WindowsTimerResolution { 26 | public: 27 | WindowsTimerResolution( 28 | const std::chrono::milliseconds& timerResolution = std::chrono::milliseconds(1)); 29 | ~WindowsTimerResolution(); 30 | 31 | // non-copyable 32 | WindowsTimerResolution& operator=(const WindowsTimerResolution&) = delete; 33 | WindowsTimerResolution(const WindowsTimerResolution&) = delete; 34 | 35 | private: 36 | const std::chrono::milliseconds timerResolution_; 37 | }; 38 | } // namespace asyncly::test 39 | -------------------------------------------------------------------------------- /Test/Unit/InterfaceForExecutorTest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "asyncly/future/Future.h" 24 | #include "asyncly/observable/Observable.h" 25 | 26 | namespace asyncly { 27 | 28 | class InterfaceForExecutorTest { 29 | public: 30 | virtual ~InterfaceForExecutorTest() = default; 31 | virtual void method() = 0; 32 | virtual void methodWithArguments(std::unique_ptr a, int c) = 0; 33 | virtual Future methodReturningFuture(bool b) = 0; 34 | virtual Future methodReturningVoidFuture(bool b) = 0; 35 | virtual Observable methodReturningObservable(bool b) = 0; 36 | }; 37 | } // namespace asyncly 38 | -------------------------------------------------------------------------------- /Source/executor/Strand.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | 21 | #include "detail/StrandImpl.h" 22 | 23 | namespace asyncly { 24 | 25 | IStrandPtr create_strand(const IExecutorPtr& executor) 26 | { 27 | auto strand = std::dynamic_pointer_cast(executor); 28 | if (strand) { 29 | return strand; 30 | } else { 31 | return std::make_shared(executor); 32 | } 33 | } 34 | 35 | IStrandPtr create_strand(const IStrandPtr& strand) 36 | { 37 | return strand; 38 | } 39 | 40 | bool is_serializing(const IExecutorPtr& executor) 41 | { 42 | return std::dynamic_pointer_cast(executor) != nullptr; 43 | } 44 | } // namespace asyncly 45 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/TimeoutGuardEnvironment.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include 27 | 28 | namespace asyncly::test { 29 | 30 | class TimeoutGuard; 31 | 32 | /// Wraps TimeoutGuard in a Google Test enviroment 33 | class TimeoutGuardEnviroment : public ::testing::Environment, boost::noncopyable { 34 | public: 35 | TimeoutGuardEnviroment(std::time_t timeoutInSec); 36 | 37 | private: 38 | std::unique_ptr m_timeoutGuard; 39 | 40 | void SetUp() override; 41 | 42 | void TearDown() override; 43 | }; 44 | } // namespace asyncly::test 45 | -------------------------------------------------------------------------------- /Test/Unit/detail/PrometheusTestHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "prometheus/client_metric.h" 25 | 26 | namespace prometheus { 27 | struct MetricFamily; 28 | } // namespace prometheus 29 | 30 | namespace asyncly::detail { 31 | 32 | struct MetricSearchResult { 33 | bool success; 34 | prometheus::ClientMetric metric; 35 | std::string errorMessage; 36 | }; 37 | 38 | MetricSearchResult grabMetric( 39 | const std::vector& families, 40 | prometheus::MetricType type, 41 | const std::string& familyName, 42 | const std::string& labelValue); 43 | } // namespace asyncly::detail 44 | -------------------------------------------------------------------------------- /Source/task/detail/TaskCurrentExecutorGuard.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/task/detail/TaskCurrentExecutorGuard.h" 20 | 21 | namespace asyncly::detail { 22 | TaskCurrentExecutorGuard::TaskCurrentExecutorGuard(const std::weak_ptr& executor) 23 | : executor_(executor) 24 | { 25 | last_executor_wrapper_rawptr_ = _get_current_executor_wrapper_rawptr(); 26 | _set_current_executor_wrapper_rawptr(this); 27 | } 28 | 29 | TaskCurrentExecutorGuard::~TaskCurrentExecutorGuard() 30 | { 31 | _set_current_executor_wrapper_rawptr(last_executor_wrapper_rawptr_); 32 | } 33 | 34 | asyncly::IExecutorPtr TaskCurrentExecutorGuard::get_current_executor() 35 | { 36 | return executor_.lock(); 37 | } 38 | } // namespace asyncly::detail 39 | -------------------------------------------------------------------------------- /Include/asyncly/task/CancelableTask.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "Task.h" 22 | #include "detail/TaskCancelable.h" 23 | 24 | #include 25 | 26 | namespace asyncly { 27 | 28 | class CancelableTask { 29 | public: 30 | CancelableTask( 31 | const std::shared_ptr& task, const std::shared_ptr& cancelable) 32 | : task_(task) 33 | , cancelable_(cancelable) 34 | { 35 | } 36 | 37 | void operator()() const 38 | { 39 | if (cancelable_->maybeMarkAsRunning()) { 40 | (*task_)(); 41 | } 42 | } 43 | 44 | private: 45 | std::shared_ptr task_; 46 | std::shared_ptr cancelable_; 47 | }; 48 | } // namespace asyncly 49 | -------------------------------------------------------------------------------- /Include/asyncly/task/detail/TaskCurrentExecutorGuard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/CurrentExecutor.h" 22 | 23 | #include 24 | 25 | namespace asyncly { 26 | 27 | class IExecutor; 28 | 29 | namespace detail { 30 | 31 | class TaskCurrentExecutorGuard : public ICurrentExecutorWrapper { 32 | public: 33 | explicit TaskCurrentExecutorGuard(const std::weak_ptr& executor); 34 | ~TaskCurrentExecutorGuard() override; 35 | std::shared_ptr get_current_executor() override; 36 | 37 | private: 38 | const std::weak_ptr& executor_; 39 | asyncly::detail::ICurrentExecutorWrapper* last_executor_wrapper_rawptr_; 40 | }; 41 | } // namespace detail 42 | } // namespace asyncly 43 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/IScheduler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "asyncly/ExecutorTypes.h" 24 | #include "asyncly/task/Cancelable.h" 25 | #include "asyncly/task/Task.h" 26 | 27 | namespace asyncly { 28 | 29 | typedef std::function ClockNowFunction; 30 | 31 | class IScheduler { 32 | public: 33 | virtual ~IScheduler() = default; 34 | virtual clock_type::time_point now() const = 0; 35 | virtual std::shared_ptr 36 | execute_at(const IExecutorWPtr& executor, const clock_type::time_point& absTime, Task&&) = 0; 37 | virtual std::shared_ptr 38 | execute_after(const IExecutorWPtr& executor, const clock_type::duration& relTime, Task&&) = 0; 39 | }; 40 | } // namespace asyncly 41 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/detail/AsioTimerHandler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "asyncly/task/Task.h" 24 | 25 | namespace asyncly::detail { 26 | 27 | class AsioTimerTask; 28 | 29 | class AsioTimerHandler { 30 | public: 31 | AsioTimerHandler(asyncly::Task&& task, const std::shared_ptr& timerTask) 32 | : m_task{ std::move(task) } 33 | , m_timerTask{ timerTask } 34 | { 35 | } 36 | 37 | void operator()(const boost::system::error_code& error) 38 | { 39 | if (error) { 40 | return; 41 | } 42 | 43 | m_task(); 44 | } 45 | 46 | private: 47 | asyncly::Task m_task; 48 | const std::shared_ptr m_timerTask; 49 | }; 50 | } // namespace asyncly::detail 51 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/CurrentExecutorGuard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "asyncly/executor/CurrentExecutor.h" 24 | 25 | namespace asyncly { 26 | 27 | class IExecutor; 28 | 29 | namespace test { 30 | 31 | class CurrentExecutorGuard { 32 | public: 33 | CurrentExecutorGuard(const std::weak_ptr<::asyncly::IExecutor>& currentExecutor) 34 | { 35 | asyncly::this_thread::set_current_executor(currentExecutor); 36 | } 37 | 38 | ~CurrentExecutorGuard() 39 | { 40 | asyncly::this_thread::set_current_executor({}); 41 | } 42 | 43 | CurrentExecutorGuard(const CurrentExecutorGuard&) = delete; 44 | CurrentExecutorGuard& operator=(const CurrentExecutorGuard&) = delete; 45 | }; 46 | } // namespace test 47 | } // namespace asyncly 48 | -------------------------------------------------------------------------------- /Include/asyncly/observable/Subscription.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace asyncly { 24 | 25 | /// Subscription represents the contract started when calling `subscribe` on an 26 | /// `Observable`. It can be used to signal that no further callbacks passed to `subscribe` 27 | /// should be invoked by canceling the subscription. 28 | class Subscription { 29 | public: 30 | class Unsubscribable { 31 | public: 32 | virtual ~Unsubscribable() = default; 33 | virtual void cancel() = 0; 34 | }; 35 | 36 | public: 37 | Subscription(const std::shared_ptr& unsubscribable) 38 | : impl{ unsubscribable } 39 | { 40 | } 41 | 42 | void cancel() 43 | { 44 | impl->cancel(); 45 | } 46 | 47 | private: 48 | const std::shared_ptr impl; 49 | }; 50 | } // namespace asyncly 51 | -------------------------------------------------------------------------------- /Test/Unit/tests_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #ifdef __clang__ 20 | #pragma clang diagnostic push 21 | #pragma clang diagnostic ignored "-Wnull-dereference" 22 | #endif // __clang__ 23 | #include "gmock/gmock.h" 24 | #ifdef __clang__ 25 | #pragma clang diagnostic pop 26 | #endif // __clang__ 27 | 28 | #include "asyncly/test/TimeoutGuardEnvironment.h" 29 | #include "asyncly/test/WindowsTimerResolution.h" 30 | 31 | int main(int argc, char* argv[]) 32 | { 33 | asyncly::test::WindowsTimerResolution winTimerResolution; 34 | 35 | const int timeoutInSeconds = 240; 36 | 37 | ::testing::InitGoogleMock(&argc, argv); 38 | ::testing::GTEST_FLAG(catch_exceptions) = false; 39 | const int repeats = testing::GTEST_FLAG(repeat); 40 | ::testing::AddGlobalTestEnvironment( 41 | new asyncly::test::TimeoutGuardEnviroment(repeats * timeoutInSeconds)); 42 | 43 | return RUN_ALL_TESTS(); 44 | } 45 | -------------------------------------------------------------------------------- /Test/Unit/StrandImplTestFactory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "executor/detail/StrandImpl.h" 20 | 21 | #include "asyncly/executor/IExecutor.h" 22 | #include "asyncly/executor/Strand.h" 23 | #include "asyncly/executor/ThreadPoolExecutorController.h" 24 | #include "asyncly/test/SchedulerProvider.h" 25 | 26 | namespace asyncly::test { 27 | 28 | template class StrandImplTestFactory { 29 | public: 30 | StrandImplTestFactory() 31 | { 32 | executorController_ 33 | = ThreadPoolExecutorController::create(1, schedulerProvider_.get_scheduler()); 34 | } 35 | 36 | std::shared_ptr create() 37 | { 38 | return std::make_shared(executorController_->get_executor()); 39 | } 40 | 41 | private: 42 | std::unique_ptr executorController_; 43 | SchedulerProvider schedulerProvider_; 44 | }; 45 | } // namespace asyncly::test 46 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/SchedulerProvider.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include "asyncly/ExecutorTypes.h" 25 | #include "asyncly/scheduler/SchedulerThread.h" 26 | 27 | namespace asyncly::test { 28 | 29 | class SchedulerProviderNone { 30 | public: 31 | ISchedulerPtr get_scheduler() const 32 | { 33 | return ISchedulerPtr(); 34 | } 35 | }; 36 | 37 | template class SchedulerProviderExternal { 38 | public: 39 | SchedulerProviderExternal() 40 | { 41 | schedulerThread_ = std::make_shared( 42 | ThreadInitFunction{}, std::make_shared()); 43 | } 44 | 45 | ISchedulerPtr get_scheduler() const 46 | { 47 | return schedulerThread_->get_scheduler(); 48 | } 49 | 50 | private: 51 | std::shared_ptr schedulerThread_; 52 | }; 53 | } // namespace asyncly::test 54 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/IFakeExecutor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/IStrand.h" 22 | 23 | #include 24 | namespace asyncly::test { 25 | 26 | class IFakeExecutor : public IStrand { 27 | public: 28 | virtual ~IFakeExecutor() = default; 29 | /** 30 | * @param maxTasksToExecute default is 0 which means unlimited, > 0 means: execute only a 31 | * certain number of tasks in this call 32 | */ 33 | virtual size_t runTasks(size_t maxTasksToExecute = 0) = 0; 34 | virtual void clear() = 0; 35 | virtual size_t queuedTasks() const = 0; 36 | virtual size_t queuedSchedulerTasks() const = 0; 37 | virtual void advanceClock(clock_type::duration advance) = 0; 38 | virtual void advanceClock(clock_type::time_point timePoint) = 0; 39 | virtual void advanceClockToCurrentLastEvent() = 0; 40 | }; 41 | using IFakeExecutorPtr = std::shared_ptr; 42 | 43 | } // namespace asyncly::test -------------------------------------------------------------------------------- /Test/Utils/Source/TimeoutGuard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace asyncly::test { 31 | 32 | /// Allows to kill an application after a certain timeout 33 | class TimeoutGuard : boost::noncopyable { 34 | public: 35 | TimeoutGuard(std::time_t timeoutInSec); 36 | 37 | ~TimeoutGuard(); 38 | 39 | /// start running the background thread which kill the application on timeout 40 | void start(); 41 | 42 | /// stop the background thread 43 | void stop(); 44 | 45 | private: 46 | void worker(); 47 | 48 | using clock = std::chrono::steady_clock; 49 | 50 | const clock::duration timeout_; 51 | std::thread thread_; 52 | bool testDone_; 53 | std::mutex mutex_; 54 | std::condition_variable conditionVariable_; 55 | }; 56 | } // namespace asyncly::test 57 | -------------------------------------------------------------------------------- /Include/asyncly/observable/detail/Subscription.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/observable/Subscription.h" 22 | 23 | #include "asyncly/observable/detail/SharedSubscriptionContext.h" 24 | 25 | namespace asyncly::detail { 26 | 27 | template class Subscription : public asyncly::Subscription::Unsubscribable { 28 | public: 29 | Subscription(const std::shared_ptr>& context); 30 | void cancel() override; 31 | 32 | private: 33 | std::shared_ptr> context; 34 | }; 35 | 36 | template void Subscription::cancel() 37 | { 38 | if (context == nullptr) { 39 | return; 40 | } 41 | 42 | context->onSubscriptionCancelled(); 43 | context.reset(); 44 | } 45 | 46 | template 47 | Subscription::Subscription(const std::shared_ptr>& ccontext) 48 | : context{ ccontext } 49 | { 50 | } 51 | } // namespace asyncly::detail 52 | -------------------------------------------------------------------------------- /Include/asyncly/executor/IExecutor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include "asyncly/ExecutorTypes.h" 25 | #include "asyncly/executor/ISteadyClock.h" 26 | #include "asyncly/task/AutoCancelable.h" 27 | #include "asyncly/task/Cancelable.h" 28 | #include "asyncly/task/RepeatableTask.h" 29 | #include "asyncly/task/Task.h" 30 | 31 | namespace asyncly { 32 | class IExecutor : public ISteadyClock { 33 | public: 34 | virtual ~IExecutor() = default; 35 | virtual void post(Task&&) = 0; 36 | virtual std::shared_ptr post_at(const clock_type::time_point& absTime, Task&&) = 0; 37 | virtual std::shared_ptr post_after(const clock_type::duration& relTime, Task&&) = 0; 38 | [[nodiscard]] virtual std::shared_ptr 39 | post_periodically(const clock_type::duration& period, RepeatableTask&&) = 0; 40 | virtual ISchedulerPtr get_scheduler() const = 0; 41 | }; 42 | using IExecutorPtr = std::shared_ptr; 43 | } // namespace asyncly 44 | -------------------------------------------------------------------------------- /Include/asyncly/executor/InlineExecutor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include "asyncly/executor/IExecutor.h" 25 | #include "asyncly/scheduler/IScheduler.h" 26 | 27 | namespace asyncly { 28 | 29 | class InlineExecutor final : public IExecutor, public std::enable_shared_from_this { 30 | private: 31 | InlineExecutor(); 32 | 33 | public: 34 | static std::shared_ptr create(); 35 | 36 | clock_type::time_point now() const override; 37 | void post(Task&& closure) override; 38 | std::shared_ptr post_at(const clock_type::time_point&, Task&&) override; 39 | std::shared_ptr post_after(const clock_type::duration&, Task&&) override; 40 | [[nodiscard]] std::shared_ptr 41 | post_periodically(const clock_type::duration&, RepeatableTask&&) override; 42 | ISchedulerPtr get_scheduler() const override; 43 | 44 | private: 45 | const ISchedulerPtr _scheduler; 46 | }; 47 | } // namespace asyncly 48 | -------------------------------------------------------------------------------- /Test/Unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | asyncly_add_google_test(test_asyncly 3 | 4 | tests_main.cpp 5 | detail/PrometheusTestHelper.cpp 6 | detail/PrometheusTestHelper.h 7 | detail/ThrowingExecutor.h 8 | future/AddTimeoutTest.cpp 9 | future/AsyncTest.cpp 10 | future/BlockingWait.cpp 11 | future/CoroutineTest.cpp 12 | future/FutureTest.cpp 13 | future/LazyOneTimeInitializerTest.cpp 14 | future/LazyValueTest.cpp 15 | future/SplitTest.cpp 16 | future/WhenAllTest.cpp 17 | future/WhenAnyTest.cpp 18 | future/WhenThenTest.cpp 19 | observable/IObservableInterface.h 20 | observable/ObservableTest.cpp 21 | task/AutoCancellableTest.cpp 22 | 23 | BaseSchedulerTest.cpp 24 | ExceptionShieldTest.cpp 25 | ExecutorCommonTest.cpp 26 | InterfaceForExecutorTest.h 27 | MetricsWrapperTest.cpp 28 | PeriodicTaskTest.cpp 29 | StrandTest.cpp 30 | ThreadPoolExecutorTest.cpp 31 | WrapTest.cpp 32 | ) 33 | 34 | 35 | if(FALSE) 36 | set(COROUTINE_AWARE_SOURCES future/CoroutineTest.cpp observable/ObservableTest.cpp) 37 | 38 | if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 5 AND NOT RTC_OS_ANDROID) 39 | set_source_files_properties(${COROUTINE_AWARE_SOURCES} PROPERTIES COMPILE_FLAGS "-fcoroutines-ts") 40 | endif() 41 | 42 | if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION VERSION_GREATER_EQUAL 1910) 43 | set_source_files_properties(${COROUTINE_AWARE_SOURCES} PROPERTIES COMPILE_FLAGS "/AWAIT") 44 | endif() 45 | endif() 46 | 47 | target_include_directories(test_asyncly PRIVATE ${PROJECT_SOURCE_DIR}/Source) 48 | target_include_directories(test_asyncly PRIVATE .) 49 | 50 | target_link_libraries(test_asyncly asyncly asyncly_test_utils asyncly_mocks) 51 | 52 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/detail/AsioTimerTask.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "AsioTimerHandler.h" 22 | 23 | #include "asyncly/ExecutorTypes.h" 24 | #include "asyncly/task/Cancelable.h" 25 | #include "asyncly/task/Task.h" 26 | 27 | #include 28 | 29 | namespace asyncly::detail { 30 | 31 | class AsioTimerTask : public asyncly::Cancelable, 32 | public std::enable_shared_from_this { 33 | public: 34 | AsioTimerTask(asyncly::clock_type::duration duration, boost::asio::io_context& io_context) 35 | : timer_{ io_context, duration } 36 | { 37 | } 38 | 39 | void schedule(Task&& task) 40 | { 41 | // we need to retain a shared_ptr in the handler because asio holds a non-owning reference 42 | // to the timer 43 | timer_.async_wait(AsioTimerHandler{ std::move(task), shared_from_this() }); 44 | } 45 | 46 | bool cancel() override 47 | { 48 | const auto cancelled_ops = timer_.cancel(); 49 | return (cancelled_ops > 0); 50 | } 51 | 52 | private: 53 | boost::asio::steady_timer timer_; 54 | }; 55 | } // namespace asyncly::detail 56 | -------------------------------------------------------------------------------- /Include/asyncly/task/detail/TaskCancelable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/task/Cancelable.h" 22 | #include "asyncly/task/Task.h" 23 | 24 | #include 25 | #include 26 | 27 | namespace asyncly { 28 | 29 | class TaskCancelable : public Cancelable { 30 | public: 31 | TaskCancelable(std::weak_ptr&& task) 32 | : isCanceled_(false) 33 | , isRunning_(false) 34 | , task_(std::move(task)) 35 | { 36 | } 37 | 38 | bool cancel() override 39 | { 40 | std::lock_guard lock(mtx_); 41 | if (isCanceled_) { 42 | return false; 43 | } 44 | isCanceled_ = true; 45 | if (isRunning_) { 46 | return false; // too late to cancel 47 | } 48 | if (auto task = task_.lock()) { 49 | task->task_.reset(); 50 | } 51 | return true; 52 | } 53 | 54 | bool maybeMarkAsRunning() 55 | { 56 | std::lock_guard lock(mtx_); 57 | isRunning_ = !isCanceled_; 58 | return isRunning_; 59 | } 60 | 61 | private: 62 | bool isCanceled_; 63 | bool isRunning_; 64 | std::weak_ptr task_; 65 | std::mutex mtx_; 66 | }; 67 | 68 | } // namespace asyncly 69 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/SchedulerThread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "IRunnableScheduler.h" 22 | #include "asyncly/ExecutorTypes.h" 23 | 24 | #include 25 | 26 | namespace asyncly { 27 | 28 | class SchedulerThread { 29 | public: 30 | SchedulerThread( 31 | ThreadInitFunction threadInit, std::shared_ptr runableScheduler); 32 | ~SchedulerThread(); 33 | asyncly::ISchedulerPtr get_scheduler() const; 34 | 35 | private: 36 | const std::shared_ptr m_runableScheduler; 37 | std::thread m_timerThread; 38 | }; 39 | 40 | inline SchedulerThread::SchedulerThread( 41 | ThreadInitFunction threadInit, std::shared_ptr runableScheduler) 42 | : m_runableScheduler(std::move(runableScheduler)) 43 | { 44 | m_timerThread = std::thread([this, init = std::move(threadInit)] { 45 | if (init) { 46 | init(); 47 | } 48 | m_runableScheduler->run(); 49 | }); 50 | } 51 | 52 | inline SchedulerThread::~SchedulerThread() 53 | { 54 | m_runableScheduler->stop(); 55 | m_timerThread.join(); 56 | } 57 | 58 | inline asyncly::ISchedulerPtr SchedulerThread::get_scheduler() const 59 | { 60 | return m_runableScheduler; 61 | } 62 | } // namespace asyncly 63 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ![Asyncly](logo_transparent.png) 2 | 3 | # Asyncly 4 | 5 | High level concurrency primitives for C++. 6 | 7 | --- 8 | 9 | **Never blocking:** asyncly purposefully takes away all APIs that would allow 10 | your event loop to be blocked when waiting for an asynchronous operation to 11 | finish. We want to make it really easy to do the right thing and never block. 12 | 13 | **Works on top of your own runtime:** asyncly works together with existing 14 | runtimes like boost::asio or libuv and makes it easy to plug in your own. This 15 | allows you to slowly migrate existing legacy code to high level concurrency 16 | primitives. 17 | 18 | **Composable:** Primitives in asyncly are designed to compose well and support 19 | your software design instead of getting in the way. 20 | 21 | ## Example 22 | 23 | ```c++ 24 | // asyncly does not contain an http client library, 25 | // http is merely used as an example of an asynchronous operation 26 | getHttpUrl("https://discovery/numberservice") 27 | .then([](auto responseFromDiscoveryService) { 28 | return getHttpUrl(responseFromDiscoveryService.body); 29 | }) 30 | .then([](auto response) { std::cout << response.body << std::endl; }) 31 | .catch_error([](auto exception) { 32 | try { 33 | std::rethrow_exception(exception); 34 | } catch (std::exception& e) { 35 | std::cout << e.what(); 36 | }); 37 | }); 38 | ``` 39 | 40 | ## Features 41 | 42 | - Futures 43 | - Observables 44 | - Thread Pools 45 | - Executors 46 | - Object lifetime helpers 47 | - Experimental C++20 coroutine support 48 | - Multi platform support (used in production on these platforms) 49 | - Windows 50 | - MacOS 51 | - Linux 52 | - iOS 53 | - Android 54 | 55 | ## Dependencies 56 | 57 | - C++20 58 | - Boost 1.81 59 | - prometheus-cpp 60 | - function2 61 | - Bazel or CMake 62 | 63 | ## Contributing 64 | 65 | See [Contributing.md](Contributing.md). 66 | 67 | ## License 68 | 69 | [Apache 2.0](LICENSE) 70 | -------------------------------------------------------------------------------- /Test/Utils/Source/TimeoutGuard.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "TimeoutGuard.h" 20 | 21 | #include 22 | #include 23 | 24 | namespace asyncly::test { 25 | 26 | TimeoutGuard::TimeoutGuard(std::time_t timeoutInSec) 27 | : timeout_{ std::chrono::seconds(timeoutInSec) } 28 | , testDone_(false) 29 | { 30 | } 31 | 32 | TimeoutGuard::~TimeoutGuard() 33 | { 34 | stop(); 35 | } 36 | 37 | /// start running the background thread which kill the application on timeout 38 | void TimeoutGuard::start() 39 | { 40 | stop(); 41 | 42 | testDone_ = false; 43 | 44 | // We spawn a background thread that calls abort() when the timeout is reached 45 | thread_ = std::thread([&]() { worker(); }); 46 | } 47 | 48 | /// stop the background thread 49 | void TimeoutGuard::stop() 50 | { 51 | testDone_ = true; 52 | conditionVariable_.notify_all(); 53 | if (thread_.joinable()) { 54 | thread_.join(); 55 | } 56 | } 57 | 58 | void TimeoutGuard::worker() 59 | { 60 | std::unique_lock lock{ mutex_ }; 61 | auto notTimedOut = conditionVariable_.wait_for(lock, timeout_, [this]() { return testDone_; }); 62 | 63 | if (!notTimedOut) { 64 | std::cerr << std::endl << "ERROR: Test Guard timed out" << std::endl; 65 | std::abort(); 66 | } 67 | } 68 | } // namespace asyncly::test 69 | -------------------------------------------------------------------------------- /Include/asyncly/ExecutorTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace asyncly { 28 | 29 | using clock_type = std::chrono::steady_clock; 30 | 31 | using AutoCancelablePtr = std::shared_ptr; 32 | using CancelablePtr = std::shared_ptr; 33 | 34 | class ISteadyClock; 35 | using ISteadyClockPtr = std::shared_ptr; 36 | 37 | class IExecutor; 38 | using IExecutorPtr = std::shared_ptr; 39 | using IExecutorWPtr = std::weak_ptr; 40 | 41 | class IScheduler; 42 | using ISchedulerPtr = std::shared_ptr; 43 | 44 | class IStrand; 45 | using IStrandPtr = std::shared_ptr; 46 | 47 | class IExecutorController; 48 | using IExecutorControllerUPtr = std::unique_ptr; 49 | 50 | class SchedulerThread; 51 | 52 | using ThreadInitFunction = std::function; 53 | 54 | struct ThreadPoolConfig { 55 | std::string name; 56 | std::vector executorInitFunctions; 57 | ThreadInitFunction schedulerInitFunction; 58 | }; 59 | 60 | struct ThreadConfig { 61 | ThreadInitFunction executorInitFunction; 62 | ThreadInitFunction schedulerInitFunction; 63 | }; 64 | 65 | } // namespace asyncly 66 | -------------------------------------------------------------------------------- /Include/asyncly/future/WhenAny.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/future/detail/WhenAny.h" 22 | 23 | namespace asyncly { 24 | 25 | template class Future; 26 | 27 | /// 28 | /// when_any can be used to combine multiple `Futures` into another 29 | /// `Future` that will be resolved when the first of the supplied 30 | /// `Future` is resolved or rejected when any `Future` is rejected 31 | /// (whichever happens first). 32 | /// 33 | /// Example usage: 34 | /// \snippet WhenAnyTest.cpp WhenAny Combination 35 | /// 36 | /// \tparam Args deduced and must never be specified manually 37 | /// \param args Futures to be combined 38 | /// 39 | /// \return a `Future` containing a `std::variant` of possible 40 | /// values. The `variant` types are ordered by a types first 41 | /// occurrance in the passed arguments. When two `Futures` of the same 42 | /// type are contained in `args`, it's no longer possible to 43 | /// distinguish which `Future` has been resolved. `void` is replaced 44 | /// by `std::monostate`, because `variants` can not hold void. Example: 45 | /// `when_any(Future, Future, Future, Future) -> 46 | /// Future` 47 | /// 48 | template Future> when_any(Args... args) 49 | { 50 | return { detail::when_any_impl(args...) }; 51 | } 52 | } // namespace asyncly 53 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/PostAndWait.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/IStrand.h" 22 | 23 | #include 24 | 25 | namespace asyncly::test { 26 | 27 | inline void postAndWait( 28 | const asyncly::IStrandPtr& strand, 29 | const asyncly::clock_type::duration& timeout, 30 | const std::function& task) 31 | { 32 | std::promise synchronized; 33 | auto synchronize = [&synchronized, task]() { 34 | task(); 35 | synchronized.set_value(); 36 | }; 37 | 38 | strand->post(synchronize); 39 | 40 | if (synchronized.get_future().wait_for(timeout) == std::future_status::timeout) { 41 | throw std::runtime_error("Timeout waiting for Task execution"); 42 | } 43 | } 44 | 45 | template 46 | T postWaitGet( 47 | const asyncly::IStrandPtr& strand, 48 | const asyncly::clock_type::duration& timeout, 49 | const std::function& task) 50 | { 51 | std::promise synchronized; 52 | auto synchronize = [&synchronized, task]() { synchronized.set_value(task()); }; 53 | 54 | strand->post(synchronize); 55 | 56 | auto future = synchronized.get_future(); 57 | if (future.wait_for(timeout) == std::future_status::timeout) { 58 | throw std::runtime_error("Timeout waiting for Task execution"); 59 | } 60 | return future.get(); 61 | } 62 | 63 | } // namespace asyncly::test 64 | -------------------------------------------------------------------------------- /Bazel/repositories.bzl: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") 3 | 4 | def asyncly_repositories(): 5 | maybe( 6 | http_archive, 7 | name = "com_github_google_benchmark", 8 | sha256 = "3e7059b6b11fb1bbe28e33e02519398ca94c1818874ebed18e504dc6f709be45", 9 | strip_prefix = "benchmark-1.8.4", 10 | urls = [ 11 | "https://github.com/google/benchmark/archive/v1.8.4.tar.gz", 12 | ], 13 | ) 14 | 15 | maybe( 16 | http_archive, 17 | name = "com_google_googletest", 18 | sha256 = "8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7", 19 | strip_prefix = "googletest-1.14.0", 20 | urls = [ 21 | "https://github.com/google/googletest/archive/v1.14.0.tar.gz", 22 | ], 23 | ) 24 | 25 | maybe( 26 | http_archive, 27 | name = "com_github_jupp0r_prometheus_cpp", 28 | sha256 = "48dbad454d314b836cc667ec4def93ec4a6e4255fc8387c20cacb3b8b6faee30", 29 | strip_prefix = "prometheus-cpp-1.2.4", 30 | urls = [ 31 | "https://github.com/jupp0r/prometheus-cpp/archive/v1.2.4.tar.gz", 32 | ], 33 | ) 34 | 35 | maybe( 36 | http_archive, 37 | name = "com_github_naios_function2", 38 | sha256 = "6081d0f7011ddb8555bd846caf1245d4bce62d83fee1403b9d247b66ed617a67", 39 | strip_prefix = "function2-4.2.4", 40 | urls = [ 41 | "https://github.com/Naios/function2/archive/4.2.4.tar.gz", 42 | ], 43 | build_file = "@com_github_logmein_asyncly//Bazel:function2.BUILD", 44 | ) 45 | 46 | maybe( 47 | http_archive, 48 | name = "com_github_nelhage_rules_boost", 49 | sha256 = "a7c42df432fae9db0587ff778d84f9dc46519d67a984eff8c79ae35e45f277c1", 50 | strip_prefix = "rules_boost-cfa585b1b5843993b70aa52707266dc23b3282d0", 51 | urls = [ 52 | "https://github.com/nelhage/rules_boost/archive/cfa585b1b5843993b70aa52707266dc23b3282d0.tar.gz", 53 | ], 54 | ) 55 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/PriorityQueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace asyncly::detail { 26 | 27 | // PriorityQueue implements a priority queue with 28 | // move-capable pop() 29 | template , typename Compare = std::less> 30 | class PriorityQueue { 31 | public: 32 | void push(T element) 33 | { 34 | container.push_back(std::move(element)); 35 | std::push_heap(container.begin(), container.end(), compare); 36 | } 37 | 38 | T pop() 39 | { 40 | std::pop_heap(container.begin(), container.end(), compare); 41 | auto res = std::move(container.back()); 42 | container.pop_back(); 43 | return res; 44 | } 45 | 46 | std::size_t size() const 47 | { 48 | return container.size(); 49 | } 50 | 51 | bool empty() const 52 | { 53 | return container.empty(); 54 | } 55 | 56 | const T& peek() const 57 | { 58 | return container.front(); 59 | } 60 | 61 | const T& peekBack() const 62 | { 63 | return *std::min_element(container.begin(), container.end(), compare); 64 | } 65 | 66 | void clear() 67 | { 68 | container.clear(); 69 | } 70 | 71 | private: 72 | Container container; 73 | Compare compare; 74 | }; 75 | } // namespace asyncly::detail 76 | -------------------------------------------------------------------------------- /Include/asyncly/task/detail/TaskWrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "TaskConcept.h" 22 | 23 | #include 24 | #include 25 | 26 | namespace asyncly::detail { 27 | 28 | template constexpr bool checkClosure(const T& closure) 29 | { 30 | if constexpr (std::is_convertible_v) { 31 | // preserve validation checking for closures that support it, ie 32 | // std::function bound to a shared_ptr 33 | return !!closure; 34 | } else { 35 | // assume valid closures if we cant check 36 | return true; 37 | } 38 | } 39 | 40 | template struct TaskWrapper : public TaskConcept { 41 | TaskWrapper(T&& closure) 42 | : closure_(std::move(closure)) 43 | { 44 | using R = typename std::invoke_result_t; 45 | static_assert(std::is_void_v, "Posted function objects can not return values!"); 46 | } 47 | 48 | TaskWrapper(const T& closure) 49 | : closure_(closure) 50 | { 51 | using R = typename std::invoke_result_t; 52 | static_assert(std::is_void_v, "Posted function objects can not return values!"); 53 | } 54 | 55 | void run() override 56 | { 57 | closure_(); 58 | } 59 | 60 | explicit operator bool() const override 61 | { 62 | return checkClosure(closure_); 63 | } 64 | 65 | T closure_; 66 | }; 67 | } // namespace asyncly::detail 68 | -------------------------------------------------------------------------------- /Include/asyncly/executor/AsioExecutorController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "IExecutorController.h" 22 | #include "asyncly/ExecutorTypes.h" 23 | #include "asyncly/scheduler/DefaultScheduler.h" 24 | #include "asyncly/scheduler/SchedulerThread.h" 25 | #include "detail/AsioExecutor.h" 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | namespace asyncly { 33 | class AsioExecutorController final : public IExecutorController { 34 | public: 35 | static std::unique_ptr 36 | create(const ThreadConfig& threadConfig = {}, const ISchedulerPtr& scheduler = {}); 37 | 38 | ~AsioExecutorController() override; 39 | AsioExecutorController(AsioExecutorController const&) = delete; 40 | AsioExecutorController& operator=(AsioExecutorController const&) = delete; 41 | 42 | // IExecutorControl 43 | void finish() override; 44 | std::shared_ptr get_executor() const override; 45 | std::shared_ptr get_scheduler() const override; 46 | 47 | boost::asio::io_context& get_io_context(); 48 | 49 | private: 50 | AsioExecutorController( 51 | const ThreadConfig& threadConfig, const ISchedulerPtr& optionalScheduler); 52 | std::shared_ptr m_executor; 53 | std::thread m_workerThread; 54 | std::shared_ptr m_schedulerThread; 55 | std::mutex m_stopMutex; 56 | }; 57 | 58 | } // namespace asyncly 59 | -------------------------------------------------------------------------------- /Source/executor/detail/MetricsTask.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "asyncly/ExecutorTypes.h" 28 | #include "asyncly/executor/detail/ExecutorMetrics.h" 29 | #include "asyncly/task/Task.h" 30 | 31 | namespace asyncly { 32 | 33 | class MetricsTask { 34 | public: 35 | enum class ExecutionType { timed, immediate }; 36 | 37 | MetricsTask( 38 | Task&& t, 39 | const IExecutorPtr& executor, 40 | const ExecutorMetricsPtr& metrics, 41 | ExecutionType executionType); 42 | 43 | MetricsTask(const MetricsTask&) = delete; 44 | MetricsTask(MetricsTask&& o) = default; 45 | 46 | MetricsTask& operator=(const MetricsTask&) = delete; 47 | MetricsTask& operator=(MetricsTask&& o) = delete; // due to reference type members 48 | 49 | void operator()(); 50 | 51 | Task task_; 52 | 53 | // reference is correct here since execution can only occur in an existing executor 54 | const IExecutorPtr& executor_; 55 | const ExecutorMetricsPtr metrics_; 56 | 57 | prometheus::Histogram& taskExecutionDuration_; 58 | prometheus::Histogram& taskQueueingDelay_; 59 | prometheus::Gauge& enqueuedTasks_; 60 | prometheus::Counter& processedTasks_; 61 | 62 | const clock_type::time_point postTimePoint_; 63 | }; 64 | } // namespace asyncly 65 | -------------------------------------------------------------------------------- /Test/Utils/Source/WindowsTimerResolution.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/test/WindowsTimerResolution.h" 20 | 21 | #ifdef _WIN32 22 | #pragma comment(lib, "winmm.lib") 23 | #include 24 | #include 25 | #endif 26 | #include 27 | #include 28 | 29 | namespace asyncly::test { 30 | 31 | WindowsTimerResolution::WindowsTimerResolution(const std::chrono::milliseconds& timerResolution) 32 | : timerResolution_(timerResolution) 33 | { 34 | #ifdef _WIN32 35 | auto resolution = static_cast(timerResolution_.count()); 36 | auto result = ::timeBeginPeriod(resolution); 37 | if (TIMERR_NOCANDO == result) { 38 | std::ostringstream ostr; 39 | ostr << "timeBeginPeriod failed: "; 40 | 41 | TIMECAPS timeCaps; 42 | auto devCapsResult = ::timeGetDevCaps(&timeCaps, sizeof(timeCaps)); 43 | if (TIMERR_NOERROR == devCapsResult) { 44 | ostr << "requested resolution (" << resolution << "ms ) is out of supported range [ " 45 | << timeCaps.wPeriodMin << "ms, " << timeCaps.wPeriodMax << "ms ]"; 46 | } 47 | throw std::runtime_error(ostr.str()); 48 | } 49 | #else 50 | (void)timerResolution_; 51 | #endif // _WIN32 52 | } 53 | 54 | WindowsTimerResolution::~WindowsTimerResolution() 55 | { 56 | #ifdef _WIN32 57 | auto resolution = static_cast(timerResolution_.count()); 58 | ::timeEndPeriod(resolution); 59 | #endif // _WIN32 60 | } 61 | } // namespace asyncly::test 62 | -------------------------------------------------------------------------------- /Include/asyncly/executor/detail/AsioExecutor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/IStrand.h" 22 | 23 | #include "asyncly/scheduler/IScheduler.h" 24 | #include "asyncly/task/Task.h" 25 | #include "asyncly/task/detail/PeriodicTask.h" 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | namespace asyncly { 33 | 34 | class AsioExecutor final : public IStrand, public std::enable_shared_from_this { 35 | public: 36 | explicit AsioExecutor(const ISchedulerPtr& scheduler); 37 | ~AsioExecutor() override; 38 | AsioExecutor(AsioExecutor const&) = delete; 39 | AsioExecutor& operator=(AsioExecutor const&) = delete; 40 | 41 | void run(); 42 | void finish(); 43 | boost::asio::io_context& get_io_context(); 44 | 45 | // IExecutor 46 | clock_type::time_point now() const override; 47 | void post(Task&&) override; 48 | std::shared_ptr post_at(const clock_type::time_point& absTime, Task&&) override; 49 | std::shared_ptr post_after(const clock_type::duration& relTime, Task&&) override; 50 | [[nodiscard]] std::shared_ptr 51 | post_periodically(const clock_type::duration& relTime, RepeatableTask&&) override; 52 | ISchedulerPtr get_scheduler() const override; 53 | 54 | private: 55 | boost::asio::io_context ioContext_; 56 | boost::asio::executor_work_guard work_; 57 | const ISchedulerPtr m_scheduler; 58 | }; 59 | } // namespace asyncly 60 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/detail/Sleep.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #if defined(_WIN32) 25 | #include 26 | #endif 27 | 28 | namespace asyncly::this_thread { 29 | 30 | /*! Implements platform specific sleep_for. 31 | * 32 | * This function is intended as a replacement for std::this_thread::sleep_for() 33 | * which is currently (VS2017) broken on Windows because it is using the system 34 | * clock instead of a monotonic one. 35 | * 36 | * On Windows the function is implemented using Win32 primitives, on all other 37 | * platforms it just wraps std::this_thread::sleep_for(). 38 | * It only gives the guarantee that it will not return before the desired 39 | * sleep duration, sub-millisecond accuracy will not be achieved. 40 | * 41 | * \param sleep_duration The amount of time to suspend the current thread. 42 | */ 43 | template 44 | void sleep_for(const std::chrono::duration& sleep_duration) 45 | { 46 | #if defined(_WIN32) 47 | auto before = std::chrono::steady_clock::now(); 48 | auto slept = std::chrono::nanoseconds{ 0 }; 49 | while (slept < sleep_duration) { 50 | auto duration_ms = std::chrono::ceil(sleep_duration - slept); 51 | ::Sleep(static_cast(duration_ms.count())); 52 | slept = std::chrono::duration_cast( 53 | std::chrono::steady_clock::now() - before); 54 | } 55 | #else 56 | std::this_thread::sleep_for(sleep_duration); 57 | #endif 58 | } 59 | } // namespace asyncly::this_thread -------------------------------------------------------------------------------- /Include/asyncly/task/detail/PeriodicTask.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/IExecutor.h" 22 | 23 | #include 24 | 25 | namespace asyncly::detail { 26 | 27 | /** 28 | * Implementation of a periodic task, using the IExecutor framework. Until cancelled, it will 29 | * re-post itself. Construction is via create(), as we need to allocate and then schedule. 30 | */ 31 | class PeriodicTask : public Cancelable, public std::enable_shared_from_this { 32 | private: 33 | // Token to prevent construction from outside create(), but still support std::make_shared 34 | struct Token { }; 35 | 36 | public: 37 | // Due to the constructor wanting to schedule, and scheduling relying upon weak_from_this(), we 38 | // need a creator 39 | static std::shared_ptr 40 | create(const clock_type::duration& period, RepeatableTask&& task, const IExecutorPtr& executor); 41 | 42 | public: 43 | bool cancel() override; 44 | 45 | public: 46 | PeriodicTask( 47 | const clock_type::duration& period, 48 | RepeatableTask&& task, 49 | const IExecutorPtr& executor, 50 | Token token); 51 | 52 | private: 53 | void scheduleTask_(); 54 | void onTimer_(); 55 | 56 | private: 57 | std::mutex mutex_; 58 | const clock_type::duration period_; 59 | std::shared_ptr task_; 60 | const std::weak_ptr executor_; 61 | 62 | bool cancelled_; 63 | std::shared_ptr currentDelayedTask_; 64 | clock_type::time_point expiry_; 65 | }; 66 | 67 | } // namespace asyncly::detail 68 | -------------------------------------------------------------------------------- /Include/asyncly/executor/ThreadPoolExecutorController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include "detail/ThreadPoolExecutor.h" 25 | 26 | #include "IExecutor.h" 27 | #include "IExecutorController.h" 28 | 29 | #include "asyncly/ExecutorTypes.h" 30 | #include "asyncly/scheduler/SchedulerThread.h" 31 | 32 | namespace asyncly { 33 | 34 | class ThreadPoolExecutorController final : public IExecutorController { 35 | public: 36 | static std::unique_ptr 37 | create(size_t numberOfThreads, const ISchedulerPtr& scheduler = {}); 38 | static std::unique_ptr 39 | create(const ThreadPoolConfig& threadPoolConfig, const ISchedulerPtr& scheduler = {}); 40 | 41 | ~ThreadPoolExecutorController() override; 42 | 43 | ThreadPoolExecutorController(ThreadPoolExecutorController const&) = delete; 44 | ThreadPoolExecutorController& operator=(ThreadPoolExecutorController const&) = delete; 45 | 46 | void finish() override; 47 | IExecutorPtr get_executor() const override; 48 | std::shared_ptr get_scheduler() const override; 49 | 50 | private: 51 | ThreadPoolExecutorController( 52 | const ThreadPoolConfig& threadPoolConfig, const ISchedulerPtr& scheduler); 53 | 54 | private: 55 | std::shared_ptr m_threadPoolExecutor; 56 | std::shared_ptr m_executor; 57 | std::shared_ptr m_schedulerThread; 58 | std::vector m_workerThreads; 59 | std::mutex m_stopMutex; 60 | }; 61 | 62 | } // namespace asyncly 63 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/ConvertExecutorFutureToStdFuture.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/future/Future.h" 22 | 23 | #include 24 | #include 25 | 26 | namespace asyncly::test { 27 | template 28 | std::future convertExecutorFutureToStdFuture(asyncly::Future& executorFuture) 29 | { 30 | auto promise = std::make_shared>(); 31 | auto future = promise->get_future(); 32 | if constexpr (std::is_void_v) { 33 | executorFuture.then([promise]() mutable { promise->set_value(); }) 34 | .catch_error([promise](std::exception_ptr e) { promise->set_exception(e); }); 35 | } else { 36 | executorFuture.then([promise](T val) mutable { promise->set_value(std::move(val)); }) 37 | .catch_error([promise](std::exception_ptr e) { promise->set_exception(e); }); 38 | } 39 | return future; 40 | } 41 | template 42 | std::future convertExecutorFutureToStdFuture(asyncly::Future&& executorFuture) 43 | { 44 | auto promise = std::make_shared>(); 45 | auto future = promise->get_future(); 46 | if constexpr (std::is_void_v) { 47 | executorFuture.then([promise]() mutable { promise->set_value(); }) 48 | .catch_error([promise](std::exception_ptr e) { promise->set_exception(e); }); 49 | } else { 50 | executorFuture.then([promise](T val) mutable { promise->set_value(std::move(val)); }) 51 | .catch_error([promise](std::exception_ptr e) { promise->set_exception(e); }); 52 | } 53 | return future; 54 | } 55 | } // namespace asyncly::test 56 | -------------------------------------------------------------------------------- /Source/executor/detail/StrandImpl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "asyncly/executor/IStrand.h" 26 | #include "asyncly/scheduler/IScheduler.h" 27 | 28 | namespace asyncly { 29 | 30 | /// StrandImpl implements a serializing queue used to dispatch tasks that have to be executed 31 | /// sequentially. 32 | class StrandImpl final : public IStrand, public std::enable_shared_from_this { 33 | public: 34 | /// Construct a new StrandImpl. 35 | /// @param executor The underlying executor tasks are forwarded to. 36 | StrandImpl(const IExecutorPtr& executor); 37 | 38 | public: 39 | /// get current time 40 | clock_type::time_point now() const override; 41 | 42 | /// post a task to the Strand. All tasks are guaranteed to not be executed in parallel. 43 | void post(Task&&) override; 44 | 45 | /// post a task to the underlying Strand at a given time point 46 | std::shared_ptr post_at(const clock_type::time_point& absTime, Task&&) override; 47 | 48 | /// post a task to the underlying Strand after a given time period 49 | std::shared_ptr post_after(const clock_type::duration& relTime, Task&&) override; 50 | 51 | /// post a task to the underlying Strand periodically 52 | [[nodiscard]] std::shared_ptr 53 | post_periodically(const clock_type::duration& period, RepeatableTask&&) override; 54 | 55 | ISchedulerPtr get_scheduler() const override; 56 | 57 | private: 58 | enum class State { 59 | Waiting, 60 | Executing, 61 | }; 62 | 63 | void notifyDone(); 64 | 65 | const IExecutorPtr executor_; 66 | std::deque taskQueue_; 67 | std::mutex mutex_; 68 | State state_; 69 | }; 70 | } // namespace asyncly 71 | -------------------------------------------------------------------------------- /Source/executor/detail/MetricsTask.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "MetricsTask.h" 20 | #include "asyncly/executor/IExecutor.h" 21 | 22 | namespace asyncly { 23 | 24 | MetricsTask::MetricsTask( 25 | Task&& t, 26 | const IExecutorPtr& executor, 27 | const ExecutorMetricsPtr& metrics, 28 | MetricsTask::ExecutionType executionType) 29 | : task_(std::move(t)) 30 | , executor_(executor) 31 | , metrics_(metrics) 32 | , taskExecutionDuration_{ executionType == ExecutionType::timed 33 | ? metrics_->taskExecution.timed_ 34 | : metrics_->taskExecution.immediate_ } 35 | , taskQueueingDelay_{ executionType == ExecutionType::timed ? metrics_->taskDelay.timed_ 36 | : metrics_->taskDelay.immediate_ } 37 | , enqueuedTasks_{ executionType == ExecutionType::timed ? metrics_->queuedTasks.timed_ 38 | : metrics_->queuedTasks.immediate_ } 39 | , processedTasks_{ executionType == ExecutionType::timed ? metrics_->processedTasks.timed_ 40 | : metrics_->processedTasks.immediate_ } 41 | , postTimePoint_{ executor_->now() } 42 | { 43 | } 44 | 45 | void MetricsTask::operator()() 46 | { 47 | enqueuedTasks_.Decrement(); 48 | 49 | const auto start = executor_->now(); 50 | taskQueueingDelay_.Observe( 51 | std::chrono::duration_cast>(start - postTimePoint_) 52 | .count()); 53 | 54 | task_(); 55 | processedTasks_.Increment(); 56 | 57 | taskExecutionDuration_.Observe( 58 | std::chrono::duration_cast>( 59 | executor_->now() - start) 60 | .count()); 61 | } 62 | 63 | } // namespace asyncly -------------------------------------------------------------------------------- /Include/asyncly/executor/CurrentExecutor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace asyncly { 24 | 25 | using IExecutorPtr = std::shared_ptr; 26 | using IStrandPtr = std::shared_ptr; 27 | 28 | namespace detail { 29 | 30 | class ICurrentExecutorWrapper { 31 | public: 32 | virtual ~ICurrentExecutorWrapper() = default; 33 | virtual asyncly::IExecutorPtr get_current_executor() = 0; 34 | }; 35 | 36 | // This is optimized for the default / standard executors, not the adapters. 37 | // The raw pointer variant comes with almost no overhead during task execution if the current 38 | // executor isn't required: The raw pointer refers to the stack object (TaskCurrentExecutorGuard) 39 | // and the IExecutorPtr is stored as const& to the Task's shared_ptr copy. The current 40 | // executor that is permanently set for the whole thread lifetime(required to be able to 41 | // called future.then() in non - executor context) is now stored as weak_ptr.It's not 42 | // blocking the executor from being removed, and it cannot leak. 43 | 44 | asyncly::detail::ICurrentExecutorWrapper* _get_current_executor_wrapper_rawptr(); 45 | void _set_current_executor_wrapper_rawptr(asyncly::detail::ICurrentExecutorWrapper* ptr); 46 | 47 | asyncly::IExecutorPtr _get_current_executor_sharedptr(); 48 | void _set_current_executor_weakptr(std::weak_ptr wptr); 49 | 50 | asyncly::IExecutorPtr _get_current_executor_noexcept(); 51 | } // namespace detail 52 | 53 | namespace this_thread { 54 | 55 | /// allows to set a weak_ptr as thread-local storage 56 | /// required for executor adapters for other frameworks (proxy/impl, ECWorkerThreadExecutor) 57 | void set_current_executor(std::weak_ptr executor); 58 | asyncly::IExecutorPtr get_current_executor(); 59 | asyncly::IStrandPtr get_current_strand(); 60 | } // namespace this_thread 61 | } // namespace asyncly 62 | -------------------------------------------------------------------------------- /Include/asyncly/future/detail/FutureTraits.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | namespace asyncly { 28 | template class Future; 29 | 30 | namespace detail { 31 | template class FutureImpl; 32 | 33 | template struct future_traits { 34 | using is_future = boost::mp11::mp_false; 35 | using contains_tuple = boost::mp11::mp_false; 36 | using value_type = T; 37 | }; 38 | 39 | template struct future_traits> { 40 | using is_future = boost::mp11::mp_true; 41 | using contains_tuple = boost::mp11::mp_false; 42 | using value_type = T; 43 | }; 44 | 45 | template struct future_traits> { 46 | using is_future = boost::mp11::mp_true; 47 | using contains_tuple = boost::mp11::mp_false; 48 | using value_type = T; 49 | }; 50 | 51 | template struct future_traits>> { 52 | using is_future = std::true_type; 53 | using contains_tuple = std::false_type; 54 | using value_type = T; 55 | }; 56 | 57 | template struct future_traits>> { 58 | using is_future = std::true_type; 59 | using contains_tuple = std::true_type; 60 | using value_type = std::tuple; 61 | }; 62 | 63 | template struct future_traits>> { 64 | using is_future = std::true_type; 65 | using contains_tuple = std::true_type; 66 | using value_type = std::tuple; 67 | }; 68 | 69 | template struct future_traits>>> { 70 | using is_future = std::true_type; 71 | using contains_tuple = std::true_type; 72 | using value_type = std::tuple; 73 | }; 74 | } // namespace detail 75 | } // namespace asyncly 76 | -------------------------------------------------------------------------------- /Include/asyncly/executor/detail/ExternalEventExecutor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "asyncly/ExecutorTypes.h" 26 | #include "asyncly/executor/IStrand.h" 27 | #include "asyncly/scheduler/IScheduler.h" 28 | #include "asyncly/task/detail/PeriodicTask.h" 29 | 30 | namespace asyncly { 31 | 32 | using ExternalEventFunction = std::function; 33 | 34 | class ExternalEventExecutor final : public IStrand, 35 | public std::enable_shared_from_this { 36 | public: 37 | static std::shared_ptr create( 38 | const asyncly::ISchedulerPtr& scheduler, 39 | const ExternalEventFunction& externalEventFunction); 40 | 41 | ExternalEventExecutor(ExternalEventExecutor const&) = delete; 42 | ExternalEventExecutor& operator=(ExternalEventExecutor const&) = delete; 43 | 44 | void runOnce(); 45 | 46 | // IExecutor 47 | clock_type::time_point now() const override; 48 | void post(Task&&) override; 49 | std::shared_ptr post_at(const clock_type::time_point& absTime, Task&&) override; 50 | std::shared_ptr post_after(const clock_type::duration& relTime, Task&&) override; 51 | [[nodiscard]] std::shared_ptr 52 | post_periodically(const clock_type::duration& period, RepeatableTask&&) override; 53 | ISchedulerPtr get_scheduler() const override; 54 | 55 | private: 56 | ExternalEventExecutor( 57 | const asyncly::ISchedulerPtr& scheduler, 58 | const ExternalEventFunction& externalEventFunction); 59 | 60 | private: 61 | std::mutex m_mutex; 62 | std::queue m_taskQueue; 63 | std::queue m_execQueue; 64 | ExternalEventFunction m_externalEventFunction; 65 | bool m_isStopped; 66 | const ISchedulerPtr m_scheduler; 67 | }; 68 | } // namespace asyncly 69 | -------------------------------------------------------------------------------- /Source/executor/InlineExecutor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | 21 | #include "asyncly/executor/InlineExecutor.h" 22 | 23 | namespace asyncly { 24 | 25 | class InlineScheduler : public IScheduler { 26 | clock_type::time_point now() const override 27 | { 28 | return clock_type::now(); 29 | } 30 | std::shared_ptr 31 | execute_at(const IExecutorWPtr&, const clock_type::time_point&, Task&&) override 32 | { 33 | assert(false); 34 | return std::shared_ptr(); 35 | } 36 | std::shared_ptr 37 | execute_after(const IExecutorWPtr&, const clock_type::duration&, Task&&) override 38 | { 39 | assert(false); 40 | return std::shared_ptr(); 41 | } 42 | }; 43 | 44 | InlineExecutor::InlineExecutor() 45 | : _scheduler(std::make_shared()) 46 | { 47 | } 48 | 49 | std::shared_ptr InlineExecutor::create() 50 | { 51 | return std::shared_ptr(new InlineExecutor()); 52 | } 53 | 54 | clock_type::time_point InlineExecutor::now() const 55 | { 56 | return _scheduler->now(); 57 | } 58 | 59 | void InlineExecutor::post(Task&& closure) 60 | { 61 | closure.maybe_set_executor(weak_from_this()); 62 | closure(); 63 | } 64 | 65 | std::shared_ptr InlineExecutor::post_at(const clock_type::time_point&, Task&&) 66 | { 67 | assert(false); 68 | return std::shared_ptr(); 69 | } 70 | 71 | std::shared_ptr InlineExecutor::post_after(const clock_type::duration&, Task&&) 72 | { 73 | assert(false); 74 | return std::shared_ptr(); 75 | } 76 | 77 | std::shared_ptr 78 | InlineExecutor::post_periodically(const clock_type::duration&, RepeatableTask&&) 79 | { 80 | assert(false); 81 | return std::shared_ptr(); 82 | } 83 | 84 | ISchedulerPtr InlineExecutor::get_scheduler() const 85 | { 86 | return _scheduler; 87 | } 88 | 89 | } // namespace asyncly 90 | -------------------------------------------------------------------------------- /Source/task/detail/PeriodicTask.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/task/detail/PeriodicTask.h" 20 | #include "asyncly/Wrap.h" 21 | 22 | namespace asyncly::detail { 23 | 24 | std::shared_ptr PeriodicTask::create( 25 | const clock_type::duration& period, RepeatableTask&& task, const IExecutorPtr& executor) 26 | { 27 | auto periodicTask 28 | = std::make_shared(period, std::move(task), executor, PeriodicTask::Token{}); 29 | periodicTask->scheduleTask_(); 30 | return periodicTask; 31 | } 32 | 33 | PeriodicTask::PeriodicTask( 34 | const clock_type::duration& period, 35 | RepeatableTask&& task, 36 | const IExecutorPtr& executor, 37 | PeriodicTask::Token) 38 | : period_(period) 39 | , task_(std::make_shared(std::move(task))) 40 | , executor_(executor) 41 | , cancelled_(false) 42 | , expiry_(executor->now()) 43 | { 44 | } 45 | 46 | bool PeriodicTask::cancel() 47 | { 48 | std::lock_guard lock{ mutex_ }; 49 | if (cancelled_) { 50 | return true; 51 | } 52 | cancelled_ = true; 53 | 54 | if (currentDelayedTask_) { 55 | currentDelayedTask_->cancel(); 56 | currentDelayedTask_.reset(); 57 | } 58 | 59 | task_.reset(); 60 | return true; 61 | } 62 | 63 | void PeriodicTask::scheduleTask_() 64 | { 65 | expiry_ += period_; 66 | if (auto executor = executor_.lock()) { 67 | currentDelayedTask_ = executor->post_at( 68 | expiry_, asyncly::wrap_weak_this_ignore(this, [this](auto) { onTimer_(); })); 69 | } 70 | } 71 | 72 | void PeriodicTask::onTimer_() 73 | { 74 | // use a local copy to execute the task 75 | // => ensure that it is not destroyed during execution 76 | std::shared_ptr task; 77 | { 78 | std::lock_guard lock{ mutex_ }; 79 | if (cancelled_) { 80 | return; 81 | } 82 | scheduleTask_(); 83 | task = task_; 84 | } 85 | 86 | (*task)(); 87 | } 88 | 89 | } // namespace asyncly::detail 90 | -------------------------------------------------------------------------------- /Include/asyncly/future/detail/Coroutine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #if __cpp_impl_coroutine >= 201902L && __cpp_lib_coroutine >= 201902L 24 | #define ASYNCLY_HAS_COROUTINES 1 25 | #endif 26 | 27 | // C++ Coroutine Trait implementations 28 | #ifdef ASYNCLY_HAS_COROUTINES 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | namespace asyncly { 35 | 36 | template class Future; 37 | template class Promise; 38 | 39 | namespace detail { 40 | 41 | template struct coro_awaiter { 42 | coro_awaiter(Future* future); 43 | ~coro_awaiter(); 44 | bool await_ready(); 45 | // todo: use bool-version to shortcut ready futures 46 | void await_suspend(std::coroutine_handle<> coroutine_handle); 47 | T await_resume(); 48 | 49 | private: 50 | Future* future_; 51 | std::exception_ptr error_; 52 | std::optional value_; 53 | }; 54 | 55 | template <> struct coro_awaiter { 56 | coro_awaiter(Future* future); 57 | ~coro_awaiter(); 58 | bool await_ready(); 59 | // todo: use bool-version to shortcut ready futures 60 | void await_suspend(std::coroutine_handle<> coroutine_handle); 61 | void await_resume(); 62 | 63 | private: 64 | Future* future_; 65 | std::exception_ptr error_; 66 | }; 67 | 68 | template struct coro_promise { 69 | std::unique_ptr> promise_; 70 | coro_promise(); 71 | ~coro_promise(); 72 | std::suspend_never initial_suspend() noexcept; 73 | std::suspend_never final_suspend() noexcept; 74 | void unhandled_exception(); 75 | void return_value(T value); 76 | }; 77 | 78 | template <> struct coro_promise { 79 | std::unique_ptr> promise_; 80 | coro_promise(); 81 | ~coro_promise(); 82 | Future get_return_object(); 83 | std::suspend_never initial_suspend() noexcept; 84 | std::suspend_never final_suspend() noexcept; 85 | void unhandled_exception(); 86 | void return_void(); 87 | }; 88 | } 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /Source/executor/AsioExecutorController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/executor/AsioExecutorController.h" 20 | 21 | namespace asyncly { 22 | AsioExecutorController::AsioExecutorController( 23 | const ThreadConfig& threadConfig, const ISchedulerPtr& optionalScheduler) 24 | { 25 | auto scheduler = optionalScheduler; 26 | if (!scheduler) { 27 | m_schedulerThread = std::make_shared( 28 | threadConfig.schedulerInitFunction, std::make_shared()); 29 | scheduler = m_schedulerThread->get_scheduler(); 30 | } 31 | m_executor = std::make_unique(scheduler); 32 | m_workerThread = std::thread([this, executorThreadConfig{ threadConfig.executorInitFunction }] { 33 | this_thread::set_current_executor(m_executor); 34 | if (executorThreadConfig) { 35 | executorThreadConfig(); 36 | }; 37 | m_executor->run(); 38 | }); 39 | } 40 | 41 | std::unique_ptr 42 | AsioExecutorController::create(const ThreadConfig& threadConfig, const ISchedulerPtr& scheduler) 43 | { 44 | return std::unique_ptr( 45 | new AsioExecutorController(threadConfig, scheduler)); 46 | } 47 | 48 | AsioExecutorController::~AsioExecutorController() 49 | { 50 | finish(); 51 | } 52 | 53 | void AsioExecutorController::finish() 54 | { 55 | std::lock_guard lock(m_stopMutex); 56 | 57 | if (m_schedulerThread) { 58 | m_schedulerThread.reset(); 59 | } 60 | 61 | if (m_executor) { 62 | m_executor->finish(); 63 | } 64 | if (m_workerThread.joinable()) { 65 | m_workerThread.join(); 66 | } 67 | } 68 | 69 | asyncly::IExecutorPtr AsioExecutorController::get_executor() const 70 | { 71 | return m_executor; 72 | } 73 | std::shared_ptr AsioExecutorController::get_scheduler() const 74 | { 75 | return m_executor->get_scheduler(); 76 | } 77 | 78 | boost::asio::io_context& AsioExecutorController::get_io_context() 79 | { 80 | return m_executor->get_io_context(); 81 | } 82 | 83 | } // namespace asyncly -------------------------------------------------------------------------------- /Include/asyncly/task/Task.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "detail/TaskConcept.h" 22 | #include "detail/TaskCurrentExecutorGuard.h" 23 | #include "detail/TaskWrapper.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace asyncly { 30 | 31 | struct Task { 32 | template 33 | requires(!std::same_as) 34 | Task(T&& closure) 35 | : task_{ new detail::TaskWrapper>{ std::forward(closure) } } 36 | { 37 | } 38 | 39 | template 40 | requires std::same_as 41 | Task(const T& closure) 42 | : task_{ new detail::TaskWrapper{ closure } } 43 | { 44 | } 45 | 46 | void operator()() const 47 | { 48 | detail::TaskCurrentExecutorGuard guard(executor_); 49 | task_->run(); 50 | task_.reset(); 51 | } 52 | 53 | explicit operator bool() const 54 | { 55 | return task_ && *task_; 56 | } 57 | 58 | /// maybe_set_executor should be called by each executor in the 59 | /// stack the task is posted to. It will eventually make the 60 | /// original executor available inside the task by calling the 61 | /// global current_executor() function. Only the first executor 62 | /// (the top of the executor stack) is actually saved, all others 63 | /// are ignored. Executors can choose to ignore this (and not call 64 | /// this method) when it would not make sense (decorators meant to 65 | /// be used only once would be an example). 66 | void maybe_set_executor(const std::weak_ptr& executor) 67 | { 68 | if (isExecutorSet_) { 69 | return; 70 | } 71 | isExecutorSet_ = true; 72 | 73 | executor_ = executor; 74 | } 75 | 76 | // order is important here. task_ including payload needs to be destroyed 77 | // before the executor is released 78 | std::weak_ptr executor_; 79 | bool isExecutorSet_ = false; 80 | mutable std::unique_ptr task_; 81 | }; 82 | 83 | } // namespace asyncly 84 | -------------------------------------------------------------------------------- /Source/executor/ExternalEventExecutorController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/executor/ExternalEventExecutorController.h" 20 | 21 | #include "asyncly/scheduler/DefaultScheduler.h" 22 | #include "asyncly/scheduler/SchedulerThread.h" 23 | 24 | namespace asyncly { 25 | 26 | std::unique_ptr ExternalEventExecutorController::create( 27 | const ExternalEventFunction& externalEventFunction, 28 | const ThreadInitFunction& schedulerInitFunction, 29 | const ISchedulerPtr& scheduler) 30 | { 31 | auto executor 32 | = std::unique_ptr(new ExternalEventExecutorController( 33 | externalEventFunction, schedulerInitFunction, scheduler)); 34 | return executor; 35 | } 36 | 37 | ExternalEventExecutorController::ExternalEventExecutorController( 38 | const ExternalEventFunction& externalEventFunction, 39 | const ThreadInitFunction& schedulerInitFunction, 40 | const ISchedulerPtr& optionalScheduler) 41 | { 42 | auto scheduler = optionalScheduler; 43 | if (!scheduler) { 44 | m_schedulerThread = std::make_shared( 45 | schedulerInitFunction, std::make_shared()); 46 | scheduler = m_schedulerThread->get_scheduler(); 47 | } 48 | 49 | m_executor = ExternalEventExecutor::create(scheduler, externalEventFunction); 50 | } 51 | 52 | ExternalEventExecutorController::~ExternalEventExecutorController() 53 | { 54 | finish(); 55 | } 56 | 57 | void ExternalEventExecutorController::runOnce() 58 | { 59 | m_executor->runOnce(); 60 | } 61 | 62 | void ExternalEventExecutorController::finish() 63 | { 64 | std::lock_guard lock(m_stopMutex); 65 | 66 | if (m_schedulerThread) { 67 | m_schedulerThread.reset(); 68 | } 69 | } 70 | 71 | asyncly::IExecutorPtr ExternalEventExecutorController::get_executor() const 72 | { 73 | return m_executor; 74 | } 75 | 76 | std::shared_ptr ExternalEventExecutorController::get_scheduler() const 77 | { 78 | return m_executor->get_scheduler(); 79 | } 80 | 81 | } // namespace asyncly 82 | -------------------------------------------------------------------------------- /Include/asyncly/future/Split.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/future/Future.h" 22 | 23 | namespace asyncly { 24 | 25 | /// split produces two Future from a single one. This can be used to represent forks in 26 | /// asynchronous computation graphs. This only works if T is copyable, as we have to deliver it two 27 | /// destinations. This function consumes the future supplied to it. 28 | template std::tuple, Future> split(Future&& future) 29 | { 30 | static_assert( 31 | std::is_copy_constructible_v, "You can only split futures for copy-constructible types"); 32 | auto lazy1 = make_lazy_future(); 33 | auto future1 = std::get<0>(lazy1); 34 | auto promise1 = std::get<1>(lazy1); 35 | 36 | auto lazy2 = make_lazy_future(); 37 | auto future2 = std::get<0>(lazy2); 38 | auto promise2 = std::get<1>(lazy2); 39 | 40 | std::move(future) 41 | .then([promise1, promise2](T value) mutable { 42 | promise1.set_value(value); 43 | promise2.set_value(value); 44 | }) 45 | .catch_error([promise1, promise2](auto error) mutable { 46 | promise1.set_exception(error); 47 | promise2.set_exception(error); 48 | }); 49 | 50 | return std::make_tuple(future1, future2); 51 | } 52 | 53 | template <> inline std::tuple, Future> split(Future&& future) 54 | { 55 | auto lazy1 = make_lazy_future(); 56 | auto future1 = std::get<0>(lazy1); 57 | auto promise1 = std::get<1>(lazy1); 58 | 59 | auto lazy2 = make_lazy_future(); 60 | auto future2 = std::get<0>(lazy2); 61 | auto promise2 = std::get<1>(lazy2); 62 | 63 | std::move(future) 64 | .then([promise1, promise2]() mutable { 65 | promise1.set_value(); 66 | promise2.set_value(); 67 | }) 68 | .catch_error([promise1, promise2](auto error) mutable { 69 | promise1.set_exception(error); 70 | promise2.set_exception(error); 71 | }); 72 | 73 | return std::make_tuple(future1, future2); 74 | } 75 | } // namespace asyncly 76 | -------------------------------------------------------------------------------- /Include/asyncly/executor/ExternalEventExecutorController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "asyncly/executor/detail/ExternalEventExecutor.h" 24 | 25 | #include "asyncly/executor/IExecutorController.h" 26 | 27 | #include "asyncly/ExecutorTypes.h" 28 | 29 | namespace asyncly { 30 | 31 | /** 32 | * ExternalEventExecutor can be used to integrate with an existing thread and event loop. 33 | * Example: There is a network event handling based on epoll which requires a thread waiting 34 | * synchronously on epoll_wait. If you want to have an executor in the same thread context (to 35 | * expose proxies), you have to integrate with the epoll event loop. ExternalEventExecutor allows to 36 | * pass an externalEventFunction parameter which is called by the executor when tasks are posted. 37 | * This function should "wake up" the external event loop and call then 38 | * ExternalEventExecutorController::runOnce() in the context of the event loop thread. 39 | */ 40 | class ExternalEventExecutorController final : public IExecutorController { 41 | public: 42 | static std::unique_ptr create( 43 | const ExternalEventFunction& externalEventFunction, 44 | const ThreadInitFunction& schedulerInitFunction, 45 | const ISchedulerPtr& scheduler = {}); 46 | 47 | ~ExternalEventExecutorController() override; 48 | 49 | ExternalEventExecutorController(ExternalEventExecutorController const&) = delete; 50 | ExternalEventExecutorController& operator=(ExternalEventExecutorController const&) = delete; 51 | 52 | void runOnce(); 53 | 54 | void finish() override; 55 | IExecutorPtr get_executor() const override; 56 | std::shared_ptr get_scheduler() const override; 57 | 58 | private: 59 | ExternalEventExecutorController( 60 | const ExternalEventFunction& externalEventFunction, 61 | const ThreadInitFunction& schedulerInitFunction, 62 | const ISchedulerPtr& scheduler); 63 | 64 | private: 65 | std::shared_ptr m_executor; 66 | std::shared_ptr m_schedulerThread; 67 | std::mutex m_stopMutex; 68 | }; 69 | 70 | } // namespace asyncly 71 | -------------------------------------------------------------------------------- /Include/asyncly/future/WhenAll.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include "asyncly/future/Future.h" 25 | #include "asyncly/future/detail/WhenAll.h" 26 | 27 | namespace asyncly { 28 | 29 | template class Future; 30 | 31 | /// 32 | /// when_all can be used to combine multiple futures. It returns a 33 | /// `Future` that will be resolved when all `Futures` that are 34 | /// supplied to it are resolved. If any of those `Futures` are 35 | /// rejected, the `Future` returned by `when_all` will also be 36 | /// rejected with the first error that occured (in case of multiple 37 | /// errors, any errors occuring after the first are discarded). 38 | /// when_all accepts only rvalues as input arguments, i.e. the 39 | /// input futures must be temporaries or passed using std::move(). 40 | /// 41 | /// Example usage: 42 | /// \snippet WhenAllTest.cpp WhenAll Combination 43 | /// 44 | /// \tparam Args deduced and must never be specified manually 45 | /// \param args Futures to be combined 46 | /// 47 | /// \return a `Future` containing a 48 | /// std::tuple of the same type as the supplied promises, with void 49 | /// promises filtered out. For example `when_all(Future, 50 | /// Future, Future, Future) -> 51 | /// Future>` 52 | /// 53 | template 54 | Future> when_all(Future&&... args) 55 | { 56 | return { detail::when_all_impl(detail::get_future_impl(args)...) }; 57 | } 58 | 59 | /// This overload of when all takes a range of Futures and returns a Future of a vector that will 60 | /// contain all elements once all Futures are resolved or an error once the first Future is 61 | /// rejected. 62 | template // I models InputIterator 63 | Future::value_type::value_type>::result_type> 65 | when_all(I begin, I end) 66 | { 67 | return { detail::when_all_iterator_impl( 68 | begin, 69 | end, 70 | detail::when_all_iterator_tag< 71 | typename std::iterator_traits::value_type::value_type>{}) }; 72 | } 73 | 74 | } // namespace asyncly 75 | -------------------------------------------------------------------------------- /Include/asyncly/executor/detail/ExecutorMetrics.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace asyncly { 32 | 33 | struct ProcessedTasksMetrics { 34 | ProcessedTasksMetrics(prometheus::Registry& registry, const std::string& executorLabel = ""); 35 | 36 | private: 37 | prometheus::Family& family_; 38 | 39 | public: 40 | prometheus::Counter& immediate_; 41 | prometheus::Counter& timed_; 42 | }; 43 | 44 | struct EnqueuedTasksMetrics { 45 | EnqueuedTasksMetrics(prometheus::Registry& registry, const std::string& executorLabel = ""); 46 | 47 | private: 48 | prometheus::Family& family_; 49 | 50 | public: 51 | prometheus::Gauge& immediate_; 52 | prometheus::Gauge& timed_; 53 | }; 54 | 55 | struct TaskExecutionDurationMetrics { 56 | TaskExecutionDurationMetrics( 57 | prometheus::Registry& registry, const std::string& executorLabel = ""); 58 | 59 | private: 60 | prometheus::Family& family_; 61 | 62 | public: 63 | prometheus::Histogram& immediate_; 64 | prometheus::Histogram& timed_; 65 | }; 66 | 67 | struct TaskQueueingDelayMetrics { 68 | TaskQueueingDelayMetrics(prometheus::Registry& registry, const std::string& executorLabel = ""); 69 | 70 | private: 71 | prometheus::Family& family_; 72 | 73 | public: 74 | prometheus::Histogram& immediate_; 75 | prometheus::Histogram& timed_; 76 | }; 77 | 78 | struct ExecutorMetrics { 79 | ExecutorMetrics( 80 | const std::shared_ptr& registry, 81 | const std::string& executorLabel = ""); 82 | 83 | std::shared_ptr registry_; 84 | ProcessedTasksMetrics processedTasks; 85 | EnqueuedTasksMetrics queuedTasks; 86 | TaskExecutionDurationMetrics taskExecution; 87 | TaskQueueingDelayMetrics taskDelay; 88 | }; 89 | 90 | using ExecutorMetricsPtr = std::shared_ptr; 91 | } // namespace asyncly 92 | -------------------------------------------------------------------------------- /Source/executor/CurrentExecutor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/executor/CurrentExecutor.h" 20 | #include "asyncly/executor/IStrand.h" 21 | 22 | #include 23 | 24 | namespace asyncly { 25 | namespace detail { 26 | 27 | namespace { 28 | thread_local asyncly::detail::ICurrentExecutorWrapper* current_executor_wrapper_rawptr_ = nullptr; 29 | thread_local std::weak_ptr current_executor_weakptr_; 30 | } // namespace 31 | 32 | asyncly::detail::ICurrentExecutorWrapper* _get_current_executor_wrapper_rawptr() 33 | { 34 | return current_executor_wrapper_rawptr_; 35 | } 36 | 37 | void _set_current_executor_wrapper_rawptr(asyncly::detail::ICurrentExecutorWrapper* ptr) 38 | { 39 | current_executor_wrapper_rawptr_ = ptr; 40 | } 41 | 42 | asyncly::IExecutorPtr _get_current_executor_sharedptr() 43 | { 44 | return current_executor_weakptr_.lock(); 45 | } 46 | 47 | void _set_current_executor_weakptr(std::weak_ptr wptr) 48 | { 49 | current_executor_weakptr_ = wptr; 50 | } 51 | 52 | asyncly::IExecutorPtr _get_current_executor_noexcept() 53 | { 54 | if (auto currentWrapper = detail::_get_current_executor_wrapper_rawptr()) { 55 | if (auto executor = currentWrapper->get_current_executor()) { 56 | return executor; 57 | } 58 | } else if (auto currentSharedPtr = detail::_get_current_executor_sharedptr()) { 59 | return currentSharedPtr; 60 | } 61 | return nullptr; 62 | } 63 | 64 | } // namespace detail 65 | 66 | namespace this_thread { 67 | void set_current_executor(std::weak_ptr executor) 68 | { 69 | detail::_set_current_executor_weakptr(executor); 70 | } 71 | 72 | asyncly::IExecutorPtr get_current_executor() 73 | { 74 | if (auto executor = detail::_get_current_executor_noexcept()) { 75 | return executor; 76 | } 77 | throw std::runtime_error("current executor stale"); 78 | } 79 | 80 | asyncly::IStrandPtr get_current_strand() 81 | { 82 | auto executor = get_current_executor(); 83 | auto strand = std::dynamic_pointer_cast(executor); 84 | if (!strand) { 85 | throw std::runtime_error("current executor is not a strand"); 86 | } 87 | return strand; 88 | } 89 | 90 | } // namespace this_thread 91 | } // namespace asyncly 92 | -------------------------------------------------------------------------------- /Test/Unit/detail/PrometheusTestHelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "prometheus/metric_family.h" 24 | #include "prometheus/metric_type.h" 25 | 26 | #include "PrometheusTestHelper.h" 27 | 28 | namespace asyncly::detail { 29 | 30 | MetricSearchResult grabMetric( 31 | const std::vector& families, 32 | prometheus::MetricType type, 33 | const std::string& familyName, 34 | const std::string& labelValue) 35 | { 36 | const auto processedTaskFamily = std::find_if( 37 | families.begin(), families.end(), [familyName](const prometheus::MetricFamily& family) { 38 | return family.name == familyName; 39 | }); 40 | 41 | if (processedTaskFamily == families.end()) { 42 | return { false, 43 | prometheus::ClientMetric{}, 44 | "==> No family named " + familyName + " found" }; 45 | } 46 | if (type != processedTaskFamily->type) { 47 | std::stringstream reason; 48 | reason << "==> Wrong metric family type! Expected " << static_cast(type) << " got " 49 | << static_cast(processedTaskFamily->type); 50 | return { false, prometheus::ClientMetric{}, reason.str() }; 51 | } 52 | 53 | if (labelValue.empty() && !processedTaskFamily->metric.empty()) { 54 | const auto metric = processedTaskFamily->metric.begin(); 55 | return { true, *metric, "" }; 56 | } 57 | const auto metric = std::find_if( 58 | processedTaskFamily->metric.begin(), 59 | processedTaskFamily->metric.end(), 60 | [labelValue](const prometheus::ClientMetric& metric) { 61 | for (const auto& label : metric.label) { 62 | if (label.name == "type" && label.value == labelValue) 63 | return true; 64 | } 65 | return false; 66 | }); 67 | if (metric == processedTaskFamily->metric.end()) { 68 | return { false, 69 | prometheus::ClientMetric{}, 70 | "==> No metric with label=" + labelValue 71 | + " found (for metric-family=" + familyName + ")" }; 72 | } 73 | return { true, *metric, "" }; 74 | } 75 | } // namespace asyncly::detail 76 | -------------------------------------------------------------------------------- /Test/Unit/future/LazyValueTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 GoTo 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/future/LazyValue.h" 20 | #include "asyncly/test/FakeFutureTest.h" 21 | 22 | #include "gmock/gmock.h" 23 | 24 | namespace asyncly { 25 | 26 | using namespace testing; 27 | 28 | class LazyValueTest : public test::FakeFutureTest { 29 | public: 30 | LazyValueTest() 31 | : lazyValue_(std::make_shared>()) 32 | { 33 | } 34 | 35 | protected: 36 | std::shared_ptr> lazyValue_; 37 | }; 38 | 39 | TEST_F(LazyValueTest, resolvesFutureWhenValueIsSet) 40 | { 41 | auto expectedValue = std::string{ "expected" }; 42 | lazyValue_->set_value(expectedValue); 43 | auto actualValue = wait_for_future(lazyValue_->get_future()); 44 | EXPECT_EQ(expectedValue, actualValue); 45 | } 46 | 47 | TEST_F(LazyValueTest, resolvesFutureAfterValueIsSet) 48 | { 49 | auto expectedValue = std::string{ "expected" }; 50 | auto future = lazyValue_->get_future(); 51 | lazyValue_->set_value(expectedValue); 52 | auto actualValue = wait_for_future(std::move(future)); 53 | EXPECT_EQ(expectedValue, actualValue); 54 | } 55 | 56 | TEST_F(LazyValueTest, resolvesMultipleFutures) 57 | { 58 | auto expectedValue = std::string{ "expected" }; 59 | lazyValue_->set_value(expectedValue); 60 | auto actualValue1 = wait_for_future(lazyValue_->get_future()); 61 | EXPECT_EQ(expectedValue, actualValue1); 62 | auto actualValue2 = wait_for_future(lazyValue_->get_future()); 63 | EXPECT_EQ(expectedValue, actualValue2); 64 | } 65 | 66 | TEST_F(LazyValueTest, rejectsWhenLazyValueIsResetWithNoValueSet) 67 | { 68 | auto future = lazyValue_->get_future(); 69 | lazyValue_.reset(); 70 | wait_for_future_failure(std::move(future)); 71 | } 72 | 73 | TEST_F(LazyValueTest, resolvesWhenLazyValueIsResetWithValueSet) 74 | { 75 | auto expectedValue = std::string{ "expected" }; 76 | lazyValue_->set_value(expectedValue); 77 | auto future = lazyValue_->get_future(); 78 | lazyValue_.reset(); 79 | auto actualValue = wait_for_future(std::move(future)); 80 | EXPECT_EQ(expectedValue, actualValue); 81 | } 82 | 83 | TEST_F(LazyValueTest, hasValue) 84 | { 85 | EXPECT_FALSE(lazyValue_->has_value()); 86 | lazyValue_->set_value("someValue"); 87 | EXPECT_TRUE(lazyValue_->has_value()); 88 | } 89 | 90 | } // namespace asyncly -------------------------------------------------------------------------------- /Source/executor/detail/AsioExecutor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/executor/detail/AsioExecutor.h" 20 | 21 | namespace asyncly { 22 | 23 | AsioExecutor::AsioExecutor(const ISchedulerPtr& scheduler) 24 | : work_(boost::asio::make_work_guard(ioContext_.get_executor())) 25 | , m_scheduler(scheduler) 26 | { 27 | } 28 | 29 | AsioExecutor::~AsioExecutor() 30 | { 31 | finish(); 32 | } 33 | 34 | void AsioExecutor::run() 35 | { 36 | ioContext_.run(); 37 | } 38 | 39 | void AsioExecutor::finish() 40 | { 41 | work_.reset(); 42 | } 43 | 44 | boost::asio::io_context& AsioExecutor::get_io_context() 45 | { 46 | return ioContext_; 47 | } 48 | 49 | clock_type::time_point AsioExecutor::now() const 50 | { 51 | return m_scheduler->now(); 52 | } 53 | 54 | void AsioExecutor::post(Task&& closure) 55 | { 56 | if (!closure) { 57 | throw std::runtime_error("invalid closure"); 58 | } 59 | 60 | closure.maybe_set_executor(weak_from_this()); 61 | boost::asio::post(ioContext_.get_executor(), std::move(closure)); 62 | } 63 | 64 | std::shared_ptr 65 | AsioExecutor::post_at(const clock_type::time_point& absTime, Task&& task) 66 | { 67 | if (!task) { 68 | throw std::runtime_error("invalid closure"); 69 | } 70 | task.maybe_set_executor(weak_from_this()); 71 | return m_scheduler->execute_at(weak_from_this(), absTime, std::move(task)); 72 | } 73 | 74 | std::shared_ptr 75 | AsioExecutor::post_after(const clock_type::duration& period, Task&& task) 76 | { 77 | if (!task) { 78 | throw std::runtime_error("invalid closure"); 79 | } 80 | task.maybe_set_executor(weak_from_this()); 81 | return m_scheduler->execute_after(weak_from_this(), period, std::move(task)); 82 | } 83 | 84 | std::shared_ptr 85 | AsioExecutor::post_periodically(const clock_type::duration& relTime, RepeatableTask&& task) 86 | { 87 | if (!task) { 88 | throw std::runtime_error("invalid closure"); 89 | } 90 | return std::make_shared( 91 | detail::PeriodicTask::create(relTime, std::move(task), shared_from_this())); 92 | } 93 | 94 | std::shared_ptr AsioExecutor::get_scheduler() const 95 | { 96 | return m_scheduler; 97 | } 98 | 99 | } // namespace asyncly 100 | -------------------------------------------------------------------------------- /Test/Performance/Executor/ExecutorWrappersPerformance.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | 21 | #include "executor/detail/StrandImpl.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace asyncly { 28 | static const size_t kBatchSize = 10000; 29 | 30 | static void testExecutor(benchmark::State& state, const IExecutorPtr& executor) 31 | { 32 | for (const auto _ : state) { 33 | std::promise done; 34 | for (size_t i = 1; i <= kBatchSize; i++) { 35 | executor->post([i, &done]() { 36 | if (i == kBatchSize) { 37 | done.set_value(); 38 | } 39 | }); 40 | } 41 | done.get_future().get(); 42 | } 43 | state.SetItemsProcessed(state.iterations() * kBatchSize); 44 | } 45 | } // namespace asyncly 46 | 47 | static void executorThreadPoolTest(benchmark::State& state) 48 | { 49 | auto executorController = asyncly::ThreadPoolExecutorController::create(1); 50 | auto executor = executorController->get_executor(); 51 | 52 | asyncly::testExecutor(state, executor); 53 | } 54 | 55 | static void metricsWrapperTest(benchmark::State& state) 56 | { 57 | auto registry = std::make_shared(); 58 | auto executorController = asyncly::ThreadPoolExecutorController::create(1); 59 | auto executor 60 | = asyncly::create_metrics_wrapper(executorController->get_executor(), "", registry); 61 | 62 | asyncly::testExecutor(state, executor); 63 | } 64 | 65 | static void exceptionShieldTest(benchmark::State& state) 66 | { 67 | auto executorController = asyncly::ThreadPoolExecutorController::create(1); 68 | auto executor = asyncly::create_exception_shield( 69 | executorController->get_executor(), [](std::exception_ptr) {}); 70 | 71 | asyncly::testExecutor(state, executor); 72 | } 73 | 74 | static void strandTest(benchmark::State& state) 75 | { 76 | auto executorController = asyncly::ThreadPoolExecutorController::create(1); 77 | auto executor = std::make_shared(executorController->get_executor()); 78 | 79 | asyncly::testExecutor(state, executor); 80 | } 81 | 82 | BENCHMARK(executorThreadPoolTest); 83 | BENCHMARK(metricsWrapperTest); 84 | BENCHMARK(exceptionShieldTest); 85 | BENCHMARK(strandTest); 86 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------------- 2 | # Module setup 3 | # ---------------------------------------------------------------------------------------- 4 | 5 | 6 | cmake_minimum_required(VERSION 3.12 FATAL_ERROR) 7 | 8 | project(asyncly CXX) 9 | 10 | include(CMakePackageConfigHelpers) 11 | include(GNUInstallDirs) 12 | include(CMake/AsynclyBuild.cmake) 13 | 14 | option(BUILD_SHARED_LIBS "Build libraries as shared ones" OFF) 15 | option(ENABLE_TESTING "Build tests" ON) 16 | 17 | if(WIN32) 18 | add_definitions("-DNOMINMAX") 19 | add_definitions("-DWIN32_LEAN_AND_MEAN") 20 | add_definitions("-D_WIN32_WINNT=0x0601") 21 | add_compile_options("/bigobj") 22 | add_compile_options("/Zc:__cplusplus") 23 | endif() 24 | 25 | find_package(function2 CONFIG REQUIRED) 26 | find_package(prometheus-cpp CONFIG REQUIRED) 27 | find_package(Boost REQUIRED) 28 | 29 | set(CMAKE_THREAD_PREFER_PTHREAD TRUE) 30 | find_package(Threads) 31 | 32 | if(ENABLE_TESTING) 33 | find_package(benchmark CONFIG REQUIRED) 34 | find_package(GTest CONFIG REQUIRED) 35 | enable_testing() 36 | endif() 37 | 38 | 39 | # ---------------------------------------------------------------------------------------- 40 | # Targets 41 | # ---------------------------------------------------------------------------------------- 42 | 43 | add_library(asyncly 44 | 45 | Source/executor/CurrentExecutor.cpp 46 | 47 | Source/executor/AsioExecutorController.cpp 48 | Source/executor/ExceptionShield.cpp 49 | Source/executor/ExternalEventExecutorController.cpp 50 | Source/executor/InlineExecutor.cpp 51 | Source/executor/MetricsWrapper.cpp 52 | Source/executor/Strand.cpp 53 | Source/executor/ThreadPoolExecutorController.cpp 54 | 55 | Source/executor/detail/AsioExecutor.cpp 56 | Source/executor/detail/ExecutorMetrics.cpp 57 | Source/executor/detail/ExternalEventExecutor.cpp 58 | Source/executor/detail/MetricsTask.cpp 59 | Source/executor/detail/MetricsTask.h 60 | Source/executor/detail/StrandImpl.cpp 61 | Source/executor/detail/StrandImpl.h 62 | 63 | Source/task/detail/PeriodicTask.cpp 64 | Source/task/detail/TaskCurrentExecutorGuard.cpp 65 | ) 66 | 67 | 68 | target_compile_features(asyncly PUBLIC cxx_std_20) 69 | target_include_directories(asyncly PUBLIC $) 70 | target_link_libraries(asyncly PUBLIC Boost::boost prometheus-cpp::core function2::function2) 71 | 72 | if(ENABLE_TESTING) 73 | add_subdirectory(Test) 74 | endif() 75 | 76 | # ---------------------------------------------------------------------------------------- 77 | # Installation 78 | # ---------------------------------------------------------------------------------------- 79 | 80 | 81 | install(DIRECTORY Include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 82 | 83 | install(TARGETS asyncly 84 | EXPORT ${PROJECT_NAME}-targets 85 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 86 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 87 | ) 88 | 89 | asyncly_install_export( 90 | EXPORT ${PROJECT_NAME}-targets 91 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 92 | NAMESPACE ${PROJECT_NAME}:: 93 | ) 94 | 95 | 96 | -------------------------------------------------------------------------------- /Test/Unit/ExceptionShieldTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "gmock/gmock.h" 23 | 24 | #include "asyncly/executor/ExceptionShield.h" 25 | #include "asyncly/executor/ThreadPoolExecutorController.h" 26 | 27 | using namespace testing; 28 | using namespace asyncly; 29 | 30 | class ExceptionShieldTest : public Test { 31 | public: 32 | void SetUp() override 33 | { 34 | executorController_ = ThreadPoolExecutorController::create(1); 35 | executor_ = executorController_->get_executor(); 36 | } 37 | 38 | std::shared_ptr executorController_; 39 | std::shared_ptr executor_; 40 | }; 41 | 42 | TEST_F(ExceptionShieldTest, shouldRunPostedTask) 43 | { 44 | std::promise taskIsRun; 45 | auto exceptionShield = create_exception_shield(executor_, [](std::exception_ptr) {}); 46 | exceptionShield->post([&taskIsRun]() { taskIsRun.set_value(); }); 47 | 48 | taskIsRun.get_future().wait(); 49 | } 50 | 51 | TEST_F(ExceptionShieldTest, shouldCallExceptionHandler) 52 | { 53 | std::promise exceptionIsThrown; 54 | auto exceptionHandler = [&exceptionIsThrown](auto) { exceptionIsThrown.set_value(); }; 55 | auto exceptionShield = create_exception_shield(executor_, exceptionHandler); 56 | exceptionShield->post([]() { throw std::runtime_error(""); }); 57 | exceptionIsThrown.get_future().wait(); 58 | } 59 | 60 | TEST_F(ExceptionShieldTest, shouldCaptureThrownIntegers) 61 | { 62 | std::promise thrownInteger; 63 | int integerToBeThrown = 42; 64 | auto exceptionHandler = [&thrownInteger](std::exception_ptr e) { 65 | try { 66 | std::rethrow_exception(e); 67 | } catch (int i) { 68 | thrownInteger.set_value(i); 69 | } 70 | }; 71 | auto exceptionShield = create_exception_shield(executor_, exceptionHandler); 72 | exceptionShield->post([integerToBeThrown]() { throw int{ integerToBeThrown }; }); 73 | EXPECT_EQ(integerToBeThrown, thrownInteger.get_future().get()); 74 | } 75 | 76 | TEST_F(ExceptionShieldTest, shouldNotAcceptInvalidExecutor) 77 | { 78 | auto createShield = []() { auto e = create_exception_shield({}, [](std::exception_ptr) {}); }; 79 | EXPECT_THROW(createShield(), std::exception); 80 | } 81 | 82 | TEST_F(ExceptionShieldTest, shouldNotAcceptNullExceptionHandlers) 83 | { 84 | auto createShield = [this]() { auto e = create_exception_shield(this->executor_, {}); }; 85 | EXPECT_THROW(createShield(), std::exception); 86 | } 87 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/AsioScheduler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include 24 | 25 | #include "IRunnableScheduler.h" 26 | #include "PriorityQueue.h" 27 | #include "detail/AsioTimerTask.h" 28 | 29 | #include "asyncly/executor/ExecutorStoppedException.h" 30 | #include "asyncly/executor/IExecutor.h" 31 | #include "asyncly/task/Task.h" 32 | 33 | namespace asyncly { 34 | 35 | class AsioScheduler : public IRunnableScheduler { 36 | public: 37 | AsioScheduler(); 38 | 39 | // IScheduler 40 | clock_type::time_point now() const override; 41 | 42 | std::shared_ptr execute_at( 43 | const IExecutorWPtr& executor, const clock_type::time_point& absTime, Task&&) override; 44 | 45 | std::shared_ptr execute_after( 46 | const IExecutorWPtr& executor, const clock_type::duration& relTime, Task&&) override; 47 | 48 | // IRunnableScheduler 49 | void run() override; 50 | void stop() override; 51 | 52 | private: 53 | boost::asio::io_context m_ioContext; 54 | std::unique_ptr m_work; 55 | }; 56 | 57 | inline AsioScheduler::AsioScheduler() 58 | : m_work{ std::make_unique(m_ioContext) } 59 | { 60 | } 61 | 62 | inline asyncly::clock_type::time_point AsioScheduler::now() const 63 | { 64 | return clock_type::now(); 65 | } 66 | 67 | inline void AsioScheduler::stop() 68 | { 69 | m_ioContext.stop(); 70 | m_work.reset(); 71 | } 72 | 73 | inline std::shared_ptr AsioScheduler::execute_at( 74 | const IExecutorWPtr& executor, const clock_type::time_point& absTime, Task&& task) 75 | { 76 | return execute_after(executor, absTime - clock_type::now(), std::move(task)); 77 | } 78 | 79 | inline std::shared_ptr AsioScheduler::execute_after( 80 | const IExecutorWPtr& executor, const clock_type::duration& relTime, Task&& task) 81 | { 82 | auto timerTask = std::make_shared(relTime, m_ioContext); 83 | timerTask->schedule([executor, task{ std::move(task) }]() mutable { 84 | if (auto p = executor.lock()) { 85 | try { 86 | p->post(std::move(task)); 87 | } catch (const ExecutorStoppedException&) { 88 | // ignore 89 | } 90 | } 91 | }); 92 | return timerTask; 93 | } 94 | 95 | inline void AsioScheduler::run() 96 | { 97 | m_ioContext.run(); 98 | } 99 | } // namespace asyncly 100 | -------------------------------------------------------------------------------- /Test/Unit/StrandTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | 21 | #include "gmock/gmock.h" 22 | 23 | #include "asyncly/executor/ExceptionShield.h" 24 | #include "asyncly/executor/Strand.h" 25 | #include "asyncly/executor/ThreadPoolExecutorController.h" 26 | #include "asyncly/test/FakeExecutor.h" 27 | #include "executor/detail/StrandImpl.h" 28 | 29 | using namespace asyncly; 30 | using namespace testing; 31 | 32 | class StrandTest : public Test { 33 | public: 34 | StrandTest() 35 | : fakeExecutor_{ asyncly::test::FakeExecutor::create() } 36 | , strand_{ std::make_shared(fakeExecutor_) } 37 | { 38 | } 39 | 40 | std::shared_ptr fakeExecutor_; 41 | std::shared_ptr strand_; 42 | }; 43 | 44 | TEST_F(StrandTest, shouldPostImmediately) 45 | { 46 | auto run = false; 47 | strand_->post([&run]() { run = true; }); 48 | fakeExecutor_->runTasks(); 49 | EXPECT_TRUE(run); 50 | } 51 | 52 | TEST_F(StrandTest, shouldSerializeExecution) 53 | { 54 | auto run1 = false; 55 | auto run2 = false; 56 | strand_->post([&run1]() { run1 = true; }); 57 | strand_->post([&run2]() { run2 = true; }); 58 | 59 | auto secondTaskHasNotBeenPostedToExecutor = fakeExecutor_->queuedTasks() == 1; 60 | EXPECT_TRUE(secondTaskHasNotBeenPostedToExecutor); 61 | 62 | fakeExecutor_->runTasks(1U); 63 | auto secondTaskHasBeenPostedToExecutor = fakeExecutor_->queuedTasks() == 1; 64 | EXPECT_TRUE(secondTaskHasBeenPostedToExecutor); 65 | 66 | EXPECT_TRUE(run1); 67 | EXPECT_FALSE(run2); 68 | 69 | fakeExecutor_->runTasks(1U); 70 | 71 | EXPECT_TRUE(run2); 72 | } 73 | 74 | class CreateStrandTest : public Test { }; 75 | 76 | TEST_F(CreateStrandTest, shouldCreateStrandIfNonSerializedExecutor) 77 | { 78 | auto controller = ThreadPoolExecutorController::create(2); 79 | auto executor = controller->get_executor(); 80 | auto strand = asyncly::create_strand(executor); 81 | ASSERT_NE(strand, executor); 82 | ASSERT_TRUE(std::dynamic_pointer_cast(strand) != nullptr); 83 | } 84 | 85 | TEST_F(CreateStrandTest, shouldPassThroughWhenStaticTypeIsAlreadyStrand) 86 | { 87 | auto controller = ThreadPoolExecutorController::create(2); 88 | auto executor = controller->get_executor(); 89 | auto strand = asyncly::create_strand(executor); 90 | auto strand2 = asyncly::create_strand(strand); 91 | ASSERT_EQ(strand, strand2); 92 | } 93 | 94 | TEST_F(CreateStrandTest, shouldPassThroughWhenDynamicTypeIsAlreadyStrand) 95 | { 96 | auto controller = ThreadPoolExecutorController::create(1); 97 | auto executor = controller->get_executor(); 98 | asyncly::IExecutorPtr strand = asyncly::create_strand(executor); 99 | ASSERT_EQ(strand, executor); 100 | } 101 | -------------------------------------------------------------------------------- /Include/asyncly/future/LazyOneTimeInitializer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/future/Split.h" 22 | #include "asyncly/future/detail/FutureTraits.h" 23 | 24 | #include 25 | #include 26 | 27 | namespace asyncly { 28 | 29 | /// LazyOneTimeInitializer takes a function that returns a future. It allows getting multiple 30 | /// futures for this return value while guaranteeing that the function will be invoked lazily and 31 | /// only once when the first future is requested. 32 | /// 33 | /// Example usage within a class: 34 | /// \snippet LazyOneTimeInitializerTest.cpp LazyOneTimeInitializer WithinClass 35 | template class LazyOneTimeInitializer { 36 | public: 37 | template 38 | LazyOneTimeInitializer(F&& fn) 39 | : _fn(std::forward(fn)) 40 | { 41 | assert(_fn && "The initialization function has to be valid!"); 42 | 43 | using R = typename std::invoke_result_t; 44 | static_assert( 45 | detail::future_traits::is_future::value, 46 | "The function has to return an asyncly::Future!"); 47 | using T2 = typename R::value_type; 48 | static_assert(std::is_same_v, "The return type Future type has to match T!"); 49 | } 50 | 51 | /// \return A future holding the value returned from calling the provided function. 52 | /// The function is only ever called once irrespective of how often get() is called. 53 | /// This function is not thread-safe. 54 | asyncly::Future get() 55 | { 56 | if (!_future) { 57 | _future.emplace(_fn()); 58 | _fn = {}; 59 | } 60 | auto pair = asyncly::split(std::move(*_future)); 61 | _future.emplace(std::move(std::get<0>(pair))); 62 | return std::get<1>(pair); 63 | } 64 | 65 | bool hasFuture() const 66 | { 67 | assert( 68 | (!_fn != !_future) 69 | && "Invariant violated! There is always ever either the initializing function or the " 70 | "future it returned."); 71 | return _future.has_value(); 72 | } 73 | 74 | private: 75 | std::function()> _fn; 76 | 77 | std::optional> _future; 78 | }; 79 | 80 | /// Creates a LazyOneTimeInitializer given a function returning an asyncly::Future. 81 | /// 82 | /// Example usage within a class: 83 | /// \snippet LazyOneTimeInitializerTest.cpp LazyOneTimeInitializer WithinClass 84 | template auto createLazyOneTimeInitializer(F&& fn) 85 | { 86 | using R = typename std::invoke_result_t; 87 | static_assert( 88 | detail::future_traits::is_future::value, 89 | "The function has to return an asyncly::Future!"); 90 | using T = typename R::value_type; 91 | 92 | return LazyOneTimeInitializer(std::forward(fn)); 93 | } 94 | } // namespace asyncly 95 | -------------------------------------------------------------------------------- /Source/executor/detail/StrandImpl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "StrandImpl.h" 24 | #include "asyncly/scheduler/IScheduler.h" 25 | #include "asyncly/task/detail/PeriodicTask.h" 26 | 27 | namespace asyncly { 28 | 29 | StrandImpl::StrandImpl(const IExecutorPtr& executor) 30 | : executor_{ executor } 31 | , state_{ StrandImpl::State::Waiting } 32 | { 33 | } 34 | 35 | clock_type::time_point StrandImpl::now() const 36 | { 37 | return executor_->now(); 38 | } 39 | 40 | void StrandImpl::post(Task&& task) 41 | { 42 | if (!task) { 43 | throw std::runtime_error("invalid closure"); 44 | } 45 | 46 | task.maybe_set_executor(weak_from_this()); 47 | 48 | std::unique_lock lock(mutex_); 49 | switch (state_) { 50 | case StrandImpl::State::Waiting: 51 | state_ = StrandImpl::State::Executing; 52 | lock.unlock(); 53 | executor_->post([ctask = std::move(task), self = shared_from_this()]() { 54 | ctask(); 55 | self->notifyDone(); 56 | }); 57 | break; 58 | 59 | case StrandImpl::State::Executing: 60 | taskQueue_.push_back(std::move(task)); 61 | break; 62 | } 63 | } 64 | 65 | std::shared_ptr 66 | StrandImpl::post_at(const clock_type::time_point& absTime, Task&& task) 67 | { 68 | task.maybe_set_executor(weak_from_this()); 69 | return get_scheduler()->execute_at(weak_from_this(), absTime, std::move(task)); 70 | } 71 | 72 | std::shared_ptr 73 | StrandImpl::post_after(const clock_type::duration& relTime, Task&& task) 74 | { 75 | task.maybe_set_executor(weak_from_this()); 76 | return get_scheduler()->execute_after(weak_from_this(), relTime, std::move(task)); 77 | } 78 | 79 | std::shared_ptr 80 | StrandImpl::post_periodically(const clock_type::duration& period, RepeatableTask&& task) 81 | { 82 | return std::make_shared( 83 | detail::PeriodicTask::create(period, std::move(task), shared_from_this())); 84 | } 85 | 86 | ISchedulerPtr StrandImpl::get_scheduler() const 87 | { 88 | return executor_->get_scheduler(); 89 | } 90 | 91 | void StrandImpl::notifyDone() 92 | { 93 | std::unique_lock lock(mutex_); 94 | 95 | assert(state_ == StrandImpl::State::Executing); 96 | 97 | if (taskQueue_.empty()) { 98 | state_ = StrandImpl::State::Waiting; 99 | return; 100 | } 101 | 102 | auto task = std::move(taskQueue_.front()); 103 | taskQueue_.pop_front(); 104 | lock.unlock(); 105 | 106 | executor_->post([ctask = std::move(task), self = shared_from_this()]() { 107 | ctask(); 108 | self->notifyDone(); 109 | }); 110 | } 111 | } // namespace asyncly 112 | -------------------------------------------------------------------------------- /Include/asyncly/scheduler/DefaultScheduler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "IRunnableScheduler.h" 22 | #include "detail/BaseScheduler.h" 23 | #include "detail/Sleep.h" 24 | #include 25 | #include 26 | 27 | #include "asyncly/executor/IExecutor.h" 28 | #include "asyncly/task/Task.h" 29 | 30 | namespace asyncly { 31 | 32 | class DefaultScheduler : public IRunnableScheduler { 33 | public: 34 | DefaultScheduler(const clock_type::duration& timerGranularity = std::chrono::milliseconds(5)); 35 | 36 | // IScheduler 37 | clock_type::time_point now() const override; 38 | 39 | std::shared_ptr execute_at( 40 | const IExecutorWPtr& executor, const clock_type::time_point& absTime, Task&&) override; 41 | 42 | std::shared_ptr execute_after( 43 | const IExecutorWPtr& executor, const clock_type::duration& relTime, Task&&) override; 44 | 45 | // IRunnableScheduler 46 | void run() override; 47 | void stop() override; 48 | 49 | private: 50 | BaseScheduler m_baseScheduler; 51 | const clock_type::duration m_timerGranularity; 52 | std::atomic m_running; 53 | std::mutex m_mutex; 54 | }; 55 | 56 | inline DefaultScheduler::DefaultScheduler(const clock_type::duration& timerGranularity) 57 | : m_baseScheduler([]() { return clock_type::now(); }) 58 | , m_timerGranularity(timerGranularity) 59 | , m_running(true) 60 | { 61 | } 62 | 63 | inline void DefaultScheduler::stop() 64 | { 65 | m_running = false; 66 | } 67 | 68 | inline asyncly::clock_type::time_point DefaultScheduler::now() const 69 | { 70 | return m_baseScheduler.now(); 71 | } 72 | 73 | inline std::shared_ptr DefaultScheduler::execute_at( 74 | const IExecutorWPtr& executor, const clock_type::time_point& absTime, Task&& task) 75 | { 76 | std::unique_lock lock(m_mutex); 77 | return m_baseScheduler.execute_at(executor, absTime, std::move(task)); 78 | } 79 | 80 | inline std::shared_ptr DefaultScheduler::execute_after( 81 | const IExecutorWPtr& executor, const clock_type::duration& relTime, Task&& task) 82 | { 83 | std::unique_lock lock(m_mutex); 84 | return m_baseScheduler.execute_after(executor, relTime, std::move(task)); 85 | } 86 | 87 | inline void DefaultScheduler::run() 88 | { 89 | // NOTE: There is no concurrent entrance in this function. 90 | // The mutex is required to protect the timer queue 91 | // against data races with the public interface. 92 | while (m_running) { 93 | { 94 | std::unique_lock lock(m_mutex); 95 | m_baseScheduler.prepareElapse(); 96 | } 97 | m_baseScheduler.elapse(); 98 | asyncly::this_thread::sleep_for(m_timerGranularity); 99 | } 100 | } 101 | } // namespace asyncly 102 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | cmake: 6 | name: CMake ${{ matrix.os }} 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [macOS-12, ubuntu-22.04, windows-2019] 12 | include: 13 | - os: macOS-12 14 | triplet: x64-osx 15 | - os: ubuntu-22.04 16 | triplet: x64-linux 17 | - os: windows-2019 18 | triplet: x64-windows 19 | 20 | steps: 21 | - uses: actions/checkout@main 22 | 23 | - name: Mount vcpkg cache 24 | uses: actions/cache@v2 25 | with: 26 | path: "~/.cache/vcpkg/archives" 27 | key: vcpkg-${{ matrix.os }}-${{ matrix.triplet }} 28 | 29 | - name: Install vcpkg dependencies 30 | run: vcpkg install --triplet=${{ matrix.triplet }} benchmark function2 gtest prometheus-cpp boost-asio boost-callable-traits boost-hana boost-mp11 31 | 32 | - name: Install ninja on Ubuntu 33 | if: runner.os == 'Linux' 34 | run: | 35 | sudo apt-get install -y ninja-build 36 | 37 | - name: Install ninja on macOS 38 | if: runner.os == 'macOS' 39 | run: brew install ninja 40 | 41 | - name: "Configure for Unix with vcpkg dependencies" 42 | if: runner.os != 'Windows' 43 | run: cmake -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install "-DCMAKE_TOOLCHAIN_FILE=${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DCMAKE_DEBUG_POSTFIX=_d -DCMAKE_CONFIGURATION_TYPES='Release;Debug' -G"Ninja Multi-Config" -S ${{ github.workspace }} -B ${{ github.workspace }}/_build 44 | 45 | - name: "Configure for Windows with vcpkg dependencies" 46 | if: runner.os == 'Windows' 47 | run: cmake -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install "-DCMAKE_TOOLCHAIN_FILE=${Env:VCPKG_INSTALLATION_ROOT}\scripts\buildsystems\vcpkg.cmake" -DCMAKE_DEBUG_POSTFIX=_d -S ${{ github.workspace }} -B ${{ github.workspace }}/_build 48 | 49 | - name: "Build Debug" 50 | run: cmake --build ${{ github.workspace }}/_build --config Debug 51 | 52 | - name: "Build Release" 53 | run: cmake --build ${{ github.workspace }}/_build --config Release 54 | 55 | - name: "Test Debug" 56 | run: ctest -C Debug -V 57 | working-directory: "${{ github.workspace }}/_build" 58 | 59 | - name: "Test Release" 60 | run: ctest -C Release -V 61 | working-directory: "${{ github.workspace }}/_build" 62 | 63 | - name: "Install Debug" 64 | run: cmake --install ${{ github.workspace }}/_build --config Debug 65 | 66 | - name: "Install Release" 67 | run: cmake --install ${{ github.workspace }}/_build --config Release 68 | 69 | bazel: 70 | name: Bazel ${{ matrix.os }} 71 | runs-on: ${{ matrix.os }} 72 | strategy: 73 | fail-fast: false 74 | matrix: 75 | os: [macOS-latest, ubuntu-latest, windows-latest] 76 | include: 77 | - os: macOS-latest 78 | bazel_args: "--cxxopt=-std=c++20" 79 | - os: ubuntu-latest 80 | bazel_args: "--cxxopt=-std=c++20" 81 | - os: windows-latest 82 | bazel_args: "--cxxopt=/std:c++20 --cxxopt=/Zc:__cplusplus" 83 | 84 | steps: 85 | - uses: actions/checkout@main 86 | 87 | - name: "Build and test asyncly" 88 | run: bazel test ${{ matrix.bazel_args }} --test_output=all //... 89 | 90 | - name: "Run asyncly benchmark" 91 | run: bazel run ${{ matrix.bazel_args }} -c opt //Test/Performance/... 92 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/ExecutorTestFactories.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "boost/asio/io_context.hpp" 24 | 25 | #include "asyncly/executor/AsioExecutorController.h" 26 | #include "asyncly/executor/ExternalEventExecutorController.h" 27 | #include "asyncly/executor/IExecutor.h" 28 | #include "asyncly/executor/ThreadPoolExecutorController.h" 29 | #include "asyncly/scheduler/AsioScheduler.h" 30 | #include "asyncly/test/SchedulerProvider.h" 31 | 32 | namespace asyncly::test { 33 | 34 | using SchedulerProviderDefault = SchedulerProviderExternal; 35 | using SchedulerProviderAsio = SchedulerProviderExternal; 36 | 37 | template class AsioExecutorFactory { 38 | public: 39 | AsioExecutorFactory() 40 | { 41 | executorController_ 42 | = asyncly::AsioExecutorController::create({}, schedulerProvider_.get_scheduler()); 43 | } 44 | 45 | std::shared_ptr create() 46 | { 47 | return executorController_->get_executor(); 48 | } 49 | 50 | private: 51 | std::unique_ptr executorController_; 52 | SchedulerProvider schedulerProvider_; 53 | }; 54 | 55 | template 56 | class DefaultExecutorFactory { 57 | public: 58 | DefaultExecutorFactory() 59 | { 60 | executorController_ 61 | = ThreadPoolExecutorController::create(threads, schedulerProvider_.get_scheduler()); 62 | } 63 | 64 | IExecutorPtr create() 65 | { 66 | return executorController_->get_executor(); 67 | } 68 | 69 | private: 70 | std::unique_ptr executorController_; 71 | SchedulerProvider schedulerProvider_; 72 | }; 73 | 74 | template class ExternalEventExecutorFactory { 75 | public: 76 | ExternalEventExecutorFactory() 77 | { 78 | executorControllerExternal_ 79 | = ThreadPoolExecutorController::create(1, schedulerProvider_.get_scheduler()); 80 | 81 | auto externalEventFunction = [this] { 82 | executorControllerExternal_->get_executor()->post( 83 | [this] { executorController_->runOnce(); }); 84 | }; 85 | 86 | executorController_ = asyncly::ExternalEventExecutorController::create( 87 | externalEventFunction, [] {}, schedulerProvider_.get_scheduler()); 88 | } 89 | ~ExternalEventExecutorFactory() 90 | { 91 | executorControllerExternal_->finish(); 92 | } 93 | 94 | std::shared_ptr create() 95 | { 96 | return executorController_->get_executor(); 97 | } 98 | 99 | private: 100 | std::unique_ptr executorControllerExternal_; 101 | std::unique_ptr executorController_; 102 | SchedulerProvider schedulerProvider_; 103 | }; 104 | 105 | } // namespace asyncly::test 106 | -------------------------------------------------------------------------------- /Test/Utils/Include/asyncly/test/FutureTest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "asyncly/executor/ThreadPoolExecutorController.h" 24 | 25 | #include "WaitForFuture.h" 26 | 27 | #include 28 | 29 | namespace asyncly::test { 30 | 31 | // see FutureTest for documentation 32 | 33 | /// FutureTest provides helper functions for using Futures in 34 | /// GoogleTests. This is intended to be used for tests only, please 35 | /// don't copy this code somewhere and use it in 36 | /// production. Blockingly waiting for things defeats the whole point 37 | /// of having futures in the first place. 38 | 39 | class FutureTest : public ::testing::Test { 40 | public: 41 | FutureTest(); 42 | 43 | /// wait_for_future blockingly waits for a Future given to it and 44 | /// returns the value the future resolves to. In case the future 45 | /// is rejected, the exception it is rejected with is thrown. 46 | template T wait_for_future(asyncly::Future&& future); 47 | 48 | /// wait_for_future_failure blockingly waits for a Future to 49 | /// fail. In case the future is unexpectedly resolved to a value, 50 | /// a std::runtime_error is thrown. If you are interested in the 51 | /// actual exception that's thrown, it's possible to use 52 | /// wait_for_future instead and surround it with a try/catch 53 | /// block. 54 | template void wait_for_future_failure(asyncly::Future&& future); 55 | 56 | /// collect_observable blockingly waits for an observable to complete and 57 | /// returns all gathered values in a vector. If the observable never completes, this 58 | /// function will block indefinitely. The parameter passed is a function because it 59 | /// internally needs to be executed in an executor context. 60 | /// Example usage: 61 | /// \snippet ExecutorProyTest.cpp Collect Observable 62 | template 63 | std::vector::value_type> 64 | collect_observable(F functionReturningObservable); 65 | 66 | private: 67 | const std::unique_ptr waitExecutorController_; 68 | const std::shared_ptr waitExecutor_; 69 | }; 70 | 71 | inline FutureTest::FutureTest() 72 | : waitExecutorController_{ ThreadPoolExecutorController::create(1) } 73 | , waitExecutor_(waitExecutorController_->get_executor()) 74 | { 75 | } 76 | 77 | template T FutureTest::wait_for_future(asyncly::Future&& future) 78 | { 79 | return asyncly::test::wait_for_future(waitExecutor_, std::move(future)); 80 | } 81 | 82 | template void FutureTest::wait_for_future_failure(asyncly::Future&& future) 83 | { 84 | return asyncly::test::wait_for_future_failure(waitExecutor_, std::move(future)); 85 | } 86 | 87 | template 88 | std::vector::value_type> 89 | FutureTest::collect_observable(F functionReturningObservable) 90 | { 91 | return asyncly::test::collect_observable(waitExecutor_, std::move(functionReturningObservable)); 92 | } 93 | } // namespace asyncly::test 94 | -------------------------------------------------------------------------------- /Include/asyncly/future/LazyValue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 GoTo 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/Future.h" 22 | #include "asyncly/future/Future.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | namespace asyncly { 29 | 30 | template class LazyValue { 31 | public: 32 | LazyValue() 33 | : _future(_promise.get_future()) 34 | , _hasValue(false) 35 | { 36 | } 37 | 38 | LazyValue(const LazyValue&) = delete; 39 | LazyValue(LazyValue&&) = delete; 40 | LazyValue& operator=(const LazyValue&) = delete; 41 | LazyValue& operator=(LazyValue&&) = delete; 42 | 43 | ~LazyValue() 44 | { 45 | if (!_hasValue) { 46 | _promise.set_exception(std::runtime_error("Could not be resolved. No value was set.")); 47 | } 48 | } 49 | 50 | asyncly::Future get_future() 51 | { 52 | auto [futureToReturn, futureToStore] = asyncly::split(std::move(*_future)); 53 | _future.emplace(futureToStore); 54 | return futureToReturn; 55 | } 56 | 57 | void set_value(const ValueType& value) 58 | { 59 | _hasValue = true; 60 | _promise.set_value(value); 61 | } 62 | 63 | void set_value(ValueType&& value) 64 | { 65 | _hasValue = true; 66 | _promise.set_value(std::move(value)); 67 | } 68 | 69 | bool has_value() const 70 | { 71 | return _hasValue; 72 | } 73 | 74 | template void set_exception(E e) 75 | { 76 | _hasValue = true; 77 | _promise.set_exception(e); 78 | } 79 | 80 | private: 81 | asyncly::Promise _promise; 82 | std::optional> _future; 83 | 84 | bool _hasValue; 85 | }; 86 | 87 | template <> class LazyValue { 88 | public: 89 | LazyValue() 90 | : _future(_promise.get_future()) 91 | , _hasValue(false) 92 | { 93 | } 94 | 95 | LazyValue(const LazyValue&) = delete; 96 | LazyValue(LazyValue&&) = delete; 97 | LazyValue& operator=(const LazyValue&) = delete; 98 | LazyValue& operator=(LazyValue&&) = delete; 99 | 100 | ~LazyValue() 101 | { 102 | if (!_hasValue) { 103 | _promise.set_exception(std::runtime_error("Could not be resolved. No value was set.")); 104 | } 105 | } 106 | 107 | asyncly::Future get_future() 108 | { 109 | auto [futureToReturn, futureToStore] = asyncly::split(std::move(*_future)); 110 | _future.emplace(futureToStore); 111 | return futureToReturn; 112 | } 113 | 114 | void set_value() 115 | { 116 | _hasValue = true; 117 | _promise.set_value(); 118 | } 119 | 120 | bool has_value() const 121 | { 122 | return _hasValue; 123 | } 124 | 125 | template void set_exception(E e) 126 | { 127 | _hasValue = true; 128 | _promise.set_exception(e); 129 | } 130 | 131 | private: 132 | asyncly::Promise _promise; 133 | std::optional> _future; 134 | 135 | bool _hasValue; 136 | }; 137 | 138 | } // namespace asyncly -------------------------------------------------------------------------------- /Test/Performance/Executor/ThreadPoolPerformanceTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include 27 | 28 | namespace asyncly { 29 | namespace { 30 | const uint64_t counter_increment = 10000; 31 | std::atomic keepRunning{ true }; 32 | std::atomic result{ 0 }; 33 | thread_local uint64_t counter{ 0 }; 34 | 35 | static void 36 | send(std::shared_ptr executorToCallIn, std::shared_ptr executorToCallBack) 37 | { 38 | if (!keepRunning.load()) { 39 | return; 40 | } 41 | 42 | auto task 43 | = [executorToCallIn, executorToCallBack]() { send(executorToCallBack, executorToCallIn); }; 44 | executorToCallIn->post(task); 45 | 46 | counter++; 47 | if (counter % counter_increment == 0) { 48 | result.fetch_add(counter_increment, std::memory_order_relaxed); 49 | } 50 | } 51 | } // namespace 52 | } // namespace asyncly 53 | 54 | using namespace asyncly; 55 | 56 | static void threadPoolPerformanceTest(benchmark::State& state) 57 | { 58 | keepRunning = true; 59 | 60 | auto range = static_cast(state.range(0)); 61 | 62 | auto executorControlA = ThreadPoolExecutorController::create(range); 63 | auto executorA = executorControlA->get_executor(); 64 | 65 | auto executorControlB = ThreadPoolExecutorController::create(range); 66 | auto executorB = executorControlB->get_executor(); 67 | 68 | auto lastTs = std::chrono::steady_clock::now(); 69 | 70 | for (unsigned i = 0; i < 10 * range; i++) { 71 | send(executorB, executorA); 72 | } 73 | 74 | bool allowedToRun = true; 75 | uint64_t lastResult = 0; 76 | while (allowedToRun) { 77 | uint64_t currentResult = result; 78 | while (lastResult == currentResult) { 79 | std::this_thread::sleep_for(std::chrono::microseconds(5)); 80 | currentResult = result; 81 | } 82 | const auto afterTs = std::chrono::steady_clock::now(); 83 | 84 | auto incrementCount = (currentResult - lastResult); 85 | const auto iterationTime 86 | = std::chrono::duration_cast>(afterTs - lastTs).count() 87 | / incrementCount; 88 | 89 | for (size_t i = 0; allowedToRun && i < incrementCount; ++i) { 90 | allowedToRun = state.KeepRunning(); 91 | state.SetIterationTime(iterationTime); 92 | } 93 | 94 | lastTs = afterTs; 95 | lastResult = currentResult; 96 | } 97 | keepRunning = false; 98 | state.SetItemsProcessed(result.exchange(0)); 99 | 100 | std::promise doneA; 101 | executorA->post([&doneA]() { doneA.set_value(); }); 102 | std::promise doneB; 103 | executorB->post([&doneB]() { doneB.set_value(); }); 104 | doneA.get_future().wait(); 105 | doneB.get_future().wait(); 106 | } 107 | 108 | BENCHMARK(threadPoolPerformanceTest) 109 | ->Arg(1) 110 | ->Arg(2) 111 | ->Arg(std::thread::hardware_concurrency() / 2) 112 | ->UseManualTime(); -------------------------------------------------------------------------------- /Test/Unit/detail/ThrowingExecutor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "asyncly/executor/IExecutor.h" 22 | #include "asyncly/scheduler/IScheduler.h" 23 | 24 | #include 25 | #include 26 | 27 | namespace asyncly::detail { 28 | 29 | template 30 | class ThrowingExecutor : public IExecutor, 31 | public std::enable_shared_from_this> { 32 | private: 33 | ThrowingExecutor(); 34 | 35 | public: 36 | static std::shared_ptr create(); 37 | 38 | clock_type::time_point now() const override; 39 | void post(Task&& closure) override; 40 | std::shared_ptr post_at(const clock_type::time_point&, Task&&) override; 41 | std::shared_ptr post_after(const clock_type::duration&, Task&&) override; 42 | [[nodiscard]] std::shared_ptr 43 | post_periodically(const clock_type::duration&, RepeatableTask&&) override; 44 | ISchedulerPtr get_scheduler() const override; 45 | 46 | private: 47 | const ISchedulerPtr _scheduler; 48 | }; 49 | template class ThrowingScheduler : public IScheduler { 50 | clock_type::time_point now() const override 51 | { 52 | return clock_type::now(); 53 | } 54 | std::shared_ptr 55 | execute_at(const IExecutorWPtr&, const clock_type::time_point&, Task&&) override 56 | { 57 | throw E("throwing executor always throws"); 58 | } 59 | std::shared_ptr 60 | execute_after(const IExecutorWPtr&, const clock_type::duration&, Task&&) override 61 | { 62 | throw E("throwing executor always throws"); 63 | } 64 | }; 65 | 66 | template 67 | inline ThrowingExecutor::ThrowingExecutor() 68 | : _scheduler(std::make_shared>()) 69 | { 70 | } 71 | 72 | template inline std::shared_ptr> ThrowingExecutor::create() 73 | { 74 | return std::shared_ptr(new ThrowingExecutor()); 75 | } 76 | 77 | template inline clock_type::time_point ThrowingExecutor::now() const 78 | { 79 | return _scheduler->now(); 80 | } 81 | 82 | template inline void ThrowingExecutor::post(Task&&) 83 | { 84 | throw E("throwing executor always throws"); 85 | } 86 | 87 | template 88 | inline std::shared_ptr 89 | ThrowingExecutor::post_at(const clock_type::time_point&, Task&&) 90 | { 91 | throw E("throwing executor always throws"); 92 | } 93 | 94 | template 95 | inline std::shared_ptr 96 | ThrowingExecutor::post_after(const clock_type::duration&, Task&&) 97 | { 98 | throw E("throwing executor always throws"); 99 | } 100 | 101 | template 102 | inline std::shared_ptr 103 | ThrowingExecutor::post_periodically(const clock_type::duration&, RepeatableTask&&) 104 | { 105 | throw E("throwing executor always throws"); 106 | } 107 | 108 | template inline ISchedulerPtr ThrowingExecutor::get_scheduler() const 109 | { 110 | return _scheduler; 111 | } 112 | } // namespace asyncly::detail 113 | -------------------------------------------------------------------------------- /CMake/AsynclyBuild.cmake: -------------------------------------------------------------------------------- 1 | # These functions add as a replacement for the LMI internal ones 2 | 3 | set(_THIS_DIR "${CMAKE_CURRENT_LIST_DIR}") 4 | 5 | function(asyncly_add_google_test target) 6 | add_executable(${target} ${ARGN}) 7 | set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES "GTest::gmock") 8 | add_test(NAME ${target} COMMAND ${target}) 9 | set_property(TEST ${target} APPEND PROPERTY LABELS Benchmark) 10 | endfunction() 11 | 12 | function(asyncly_add_google_benchmark target) 13 | set(_options EXCLUDE_FROM_TEST) 14 | set(_oneValueArgs) 15 | set(_multiValueArgs SOURCES) 16 | 17 | cmake_parse_arguments(_ARGS "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) 18 | 19 | add_executable(${target} ${_ARGS_SOURCES}) 20 | set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES "benchmark::benchmark") 21 | if(NOT _ARGS_EXCLUDE_FROM_TEST) 22 | add_test(NAME ${target} COMMAND ${target}) 23 | endif() 24 | endfunction() 25 | 26 | function(asyncly_add_self_contained_header_test target_name include_root) 27 | asyncly_add_self_contained_header_check( 28 | TARGET ${target_name} 29 | ROOT ${include_root} 30 | GLOB_RECURSE "*") 31 | endfunction() 32 | 33 | function(asyncly_add_self_contained_header_check) 34 | set(_options) 35 | set(_oneValueArgs TARGET ROOT) 36 | set(_multiValueArgs GLOB_RECURSE) 37 | 38 | cmake_parse_arguments(_ARG "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) 39 | 40 | if(NOT _ARG_TARGET OR NOT _ARG_ROOT OR NOT _ARG_GLOB_RECURSE) 41 | message(FATAL_ERROR "Missing arguments ${ARGN}") 42 | endif() 43 | 44 | get_filename_component(_absolute_root "${_ARG_ROOT}" ABSOLUTE) 45 | 46 | # Glob all requested files 47 | set(_all_headers) 48 | foreach(_glob IN LISTS _ARG_GLOB_RECURSE) 49 | file(GLOB_RECURSE _glob_headers LIST_DIRECTORIES FALSE RELATIVE "${_absolute_root}" "${_absolute_root}/${_glob}") 50 | list(APPEND _all_headers ${_glob_headers}) 51 | endforeach() 52 | 53 | # add check for all headers 54 | 55 | unset(_sources) 56 | 57 | foreach(HEADER IN LISTS _all_headers) 58 | string(MAKE_C_IDENTIFIER "${HEADER}" _short_name) 59 | set(_source "${CMAKE_CURRENT_BINARY_DIR}/${_short_name}.cpp") 60 | set(UNIQUE_PART "${_short_name}") 61 | configure_file("${_THIS_DIR}/test_headercompile.cpp.in" "${_source}" @ONLY) 62 | list(APPEND _sources "${_source}") 63 | endforeach() 64 | 65 | add_library(${_ARG_TARGET} STATIC ${_sources}) 66 | target_include_directories(${_ARG_TARGET} PRIVATE "${_absolute_root}") 67 | endfunction() 68 | 69 | function(asyncly_install_export) 70 | set(options ) 71 | set(oneValueArgs DESTINATION EXPORT NAMESPACE) 72 | set(multiValueArgs ) 73 | 74 | cmake_parse_arguments(_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 75 | 76 | if(NOT _ARGS_DESTINATION) 77 | message(FATAL_ERROR "Required argument DESTINATION missing in asyncly_install_export() call") 78 | endif() 79 | 80 | if(NOT _ARGS_EXPORT) 81 | message(FATAL_ERROR "Required argument EXPORT missing in asyncly_install_export() call") 82 | endif() 83 | 84 | if(NOT _ARGS_NAMESPACE) 85 | message(FATAL_ERROR "Required argument NAMESPACE missing in asyncly_install_export() call") 86 | endif() 87 | 88 | install( 89 | EXPORT ${_ARGS_EXPORT} 90 | DESTINATION ${_ARGS_DESTINATION} 91 | NAMESPACE ${_ARGS_NAMESPACE} 92 | ) 93 | 94 | configure_package_config_file( 95 | "${CMAKE_CURRENT_SOURCE_DIR}/CMake/${PROJECT_NAME}-config.cmake.in" 96 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 97 | INSTALL_DESTINATION ${_ARGS_DESTINATION} 98 | NO_CHECK_REQUIRED_COMPONENTS_MACRO 99 | PATH_VARS CMAKE_INSTALL_INCLUDEDIR 100 | ) 101 | 102 | install( 103 | FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" 104 | DESTINATION ${_ARGS_DESTINATION} 105 | ) 106 | endfunction() 107 | -------------------------------------------------------------------------------- /Source/executor/ThreadPoolExecutorController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include "asyncly/executor/ThreadPoolExecutorController.h" 20 | 21 | #include "asyncly/executor/IStrand.h" 22 | #include "asyncly/scheduler/DefaultScheduler.h" 23 | 24 | namespace asyncly { 25 | 26 | ThreadPoolExecutorController::ThreadPoolExecutorController( 27 | const ThreadPoolConfig& threadPoolConfig, const ISchedulerPtr& optionalScheduler) 28 | { 29 | auto scheduler = optionalScheduler; 30 | if (!scheduler) { 31 | m_schedulerThread = std::make_shared( 32 | threadPoolConfig.schedulerInitFunction, std::make_shared()); 33 | scheduler = m_schedulerThread->get_scheduler(); 34 | } 35 | 36 | const bool isSerializingExecutor = (threadPoolConfig.executorInitFunctions.size() == 1); 37 | if (isSerializingExecutor) { 38 | const auto executor = ThreadPoolExecutor::create(threadPoolConfig.name, scheduler); 39 | m_executor = executor; 40 | m_threadPoolExecutor = executor; 41 | } else { 42 | const auto executor 43 | = ThreadPoolExecutor::create(threadPoolConfig.name, scheduler); 44 | m_executor = executor; 45 | m_threadPoolExecutor = executor; 46 | } 47 | 48 | for (const auto& threadInitFunction : threadPoolConfig.executorInitFunctions) { 49 | m_workerThreads.emplace_back([this, threadInitFunction]() { 50 | if (threadInitFunction) { 51 | threadInitFunction(); 52 | } 53 | m_threadPoolExecutor->run(); 54 | }); 55 | } 56 | } 57 | 58 | ThreadPoolExecutorController::~ThreadPoolExecutorController() 59 | { 60 | finish(); 61 | } 62 | 63 | void ThreadPoolExecutorController::finish() 64 | { 65 | std::lock_guard lock(m_stopMutex); 66 | 67 | if (m_schedulerThread) { 68 | m_schedulerThread.reset(); 69 | } 70 | 71 | m_threadPoolExecutor->finish(); 72 | for (auto& thread : m_workerThreads) { 73 | thread.join(); 74 | } 75 | m_workerThreads.clear(); 76 | } 77 | 78 | asyncly::IExecutorPtr ThreadPoolExecutorController::get_executor() const 79 | { 80 | return m_executor; 81 | } 82 | 83 | std::shared_ptr ThreadPoolExecutorController::get_scheduler() const 84 | { 85 | return m_executor->get_scheduler(); 86 | } 87 | 88 | std::unique_ptr 89 | ThreadPoolExecutorController::create(size_t numberOfThreads, const ISchedulerPtr& scheduler) 90 | { 91 | ThreadPoolConfig threadPoolConfig; 92 | threadPoolConfig.name = ""; 93 | for (auto i = 0u; i < numberOfThreads; ++i) { 94 | threadPoolConfig.executorInitFunctions.emplace_back([]() {}); 95 | } 96 | return create(threadPoolConfig, scheduler); 97 | } 98 | 99 | std::unique_ptr ThreadPoolExecutorController::create( 100 | const ThreadPoolConfig& threadPoolConfig, const ISchedulerPtr& scheduler) 101 | { 102 | auto executor = std::unique_ptr( 103 | new ThreadPoolExecutorController(threadPoolConfig, scheduler)); 104 | return executor; 105 | } 106 | 107 | } // namespace asyncly -------------------------------------------------------------------------------- /Test/Unit/PeriodicTaskTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 LogMeIn 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 | * SPDX-License-Identifier: Apache-2.0 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "gmock/gmock.h" 25 | 26 | #include "asyncly/executor/ThreadPoolExecutorController.h" 27 | #include "asyncly/test/FakeClockScheduler.h" 28 | 29 | using namespace std::chrono_literals; 30 | 31 | namespace asyncly { 32 | 33 | namespace { 34 | const auto startTimePoint = std::chrono::steady_clock::time_point(0ms); 35 | const auto period = 10ms; 36 | 37 | } // namespace 38 | 39 | struct DestructionSentinel { 40 | public: 41 | DestructionSentinel(std::promise& destroyed) 42 | : _destroyed(destroyed) 43 | { 44 | } 45 | ~DestructionSentinel() 46 | { 47 | destroy(); 48 | _destroyed.set_value(); 49 | } 50 | MOCK_CONST_METHOD0(destroy, void()); 51 | 52 | std::promise& _destroyed; 53 | }; 54 | 55 | TEST( 56 | PeriodicTaskTest, 57 | cancel_userSuppliedTaskIsExecutedConcurrently_userSuppliedTaskIsDeletedAfterExecutionHasFinished) 58 | { 59 | auto fakeScheduler = std::make_shared(); 60 | auto controller = ThreadPoolExecutorController::create(2, fakeScheduler); 61 | auto executor = controller->get_executor(); 62 | 63 | auto taskStarted = std::promise(); 64 | auto taskUnblocked = std::promise(); 65 | auto taskDestroyed = std::promise(); 66 | 67 | auto periodicTask = executor->post_periodically( 68 | period, 69 | [&taskStarted, 70 | &taskUnblocked, 71 | destructionSentinel = std::make_unique(taskDestroyed)]() mutable { 72 | EXPECT_CALL(*destructionSentinel, destroy).Times(0); 73 | taskStarted.set_value(); 74 | taskUnblocked.get_future().get(); 75 | EXPECT_CALL(*destructionSentinel, destroy).Times(1); 76 | }); 77 | 78 | fakeScheduler->advanceClockToNextEvent(startTimePoint + period); 79 | 80 | // wait for the async task to start, cancel it and then unblock it 81 | taskStarted.get_future().get(); 82 | periodicTask.reset(); 83 | taskUnblocked.set_value(); 84 | taskDestroyed.get_future().get(); 85 | } 86 | 87 | TEST( 88 | PeriodicTaskTest, 89 | cancel_callFromWithinPeriodicTask_userSuppliedTaskIsDeletedAfterExecutionHasFinished) 90 | { 91 | auto fakeScheduler = std::make_shared(); 92 | auto controller = ThreadPoolExecutorController::create(2, fakeScheduler); 93 | auto executor = controller->get_executor(); 94 | 95 | auto taskDestroyed = std::promise(); 96 | 97 | std::shared_ptr periodicTask; 98 | periodicTask = executor->post_periodically( 99 | period, 100 | [&periodicTask, 101 | destructionSentinel = std::make_unique(taskDestroyed)]() mutable { 102 | EXPECT_CALL(*destructionSentinel, destroy).Times(0); 103 | periodicTask.reset(); 104 | EXPECT_CALL(*destructionSentinel, destroy).Times(1); 105 | }); 106 | 107 | fakeScheduler->advanceClockToNextEvent(startTimePoint + period); 108 | 109 | taskDestroyed.get_future().get(); 110 | } 111 | 112 | } // namespace asyncly 113 | --------------------------------------------------------------------------------