diff --git a/.github/workflows/test_matrix.json b/.github/workflows/test_matrix.json index 0fa8a8ae..9714fc0b 100644 --- a/.github/workflows/test_matrix.json +++ b/.github/workflows/test_matrix.json @@ -1,51 +1,51 @@ [ { - "os": "ubuntu-20.04", + "os": "ubuntu-24.04", "clang-version": 11, "stdlib": "libc++", "tsan": false, "shared": false, - "name" : "ubuntu-20.04, clang-11, libc++, shared=false, tsan=false" + "name" : "ubuntu-24.04, clang-11, libc++, shared=false, tsan=false" }, { - "os": "ubuntu-20.04", + "os": "ubuntu-24.04", "clang-version": 11, "stdlib": "libc++", "tsan": false, "shared": true, - "name" : "ubuntu-20.04, clang-11, libc++, shared=true, tsan=false" + "name" : "ubuntu-24.04, clang-11, libc++, shared=true, tsan=false" }, { - "os": "ubuntu-20.04", + "os": "ubuntu-24.04", "clang-version": 12, "stdlib": "libc++", "tsan": false, "shared": false, - "name" : "ubuntu-20.04, clang-12, libc++, shared=false, tsan=false" + "name" : "ubuntu-24.04, clang-12, libc++, shared=false, tsan=false" }, { - "os": "ubuntu-20.04", + "os": "ubuntu-24.04", "clang-version": 12, "stdlib": "libc++", "tsan": false, "shared": true, - "name" : "ubuntu-20.04, clang-12, libc++, shared=true, tsan=false" + "name" : "ubuntu-24.04, clang-12, libc++, shared=true, tsan=false" }, { - "os": "ubuntu-20.04", + "os": "ubuntu-24.04", "clang-version": 13, "stdlib": "libc++", "tsan": false, "shared": false, - "name" : "ubuntu-20.04, clang-13, libc++, shared=false, tsan=false" + "name" : "ubuntu-24.04, clang-13, libc++, shared=false, tsan=false" }, { - "os": "ubuntu-20.04", + "os": "ubuntu-24.04", "clang-version": 13, "stdlib": "libc++", "tsan": false, "shared": true, - "name" : "ubuntu-20.04, clang-13, libc++, shared=true, tsan=false" + "name" : "ubuntu-24.04, clang-13, libc++, shared=true, tsan=false" }, { "os": "ubuntu-22.04", @@ -250,40 +250,40 @@ "name" : "windows-2022, clang-16, msvc-stl, shared=true, tsan=false" }, { - "os": "macos-12", + "os": "macos-15", "stdlib": "libc++", "tsan": false, "shared": false, - "name" : "macos-12, clang, libc++, shared=false, tsan=false" + "name" : "macos-15, clang, libc++, shared=false, tsan=false" }, { - "os": "macos-12", + "os": "macos-15", "stdlib": "libc++", "tsan": false, "shared": true, - "name" : "macos-12, clang, libc++, shared=true, tsan=false" + "name" : "macos-15, clang, libc++, shared=true, tsan=false" }, { - "os": "macos-12", + "os": "macos-15", "stdlib": "libc++", "tsan": true, "shared": false, - "name" : "macos-12, clang, libc++, shared=false, tsan=true" + "name" : "macos-15, clang, libc++, shared=false, tsan=true" }, { - "os": "macos-12", + "os": "macos-15", "gcc-version": 13, "stdlib": "libstdc++", "tsan": false, "shared": false, - "name" : "macos-12, gcc-13, libstdc++-13, shared=false, tsan=false" + "name" : "macos-15, gcc-13, libstdc++-13, shared=false, tsan=false" }, { - "os": "macos-12", + "os": "macos-15", "gcc-version": 13, "stdlib": "libstdc++", "tsan": false, "shared": true, - "name" : "macos-12, gcc-13, libstdc++-13, shared=true, tsan=false" + "name" : "macos-15, gcc-13, libstdc++-13, shared=true, tsan=false" } ] \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 201d386c..7373010c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ set(concurrencpp_sources source/threads/thread.cpp source/timers/timer.cpp source/timers/timer_queue.cpp + source/utils/throw_helper.cpp source/utils/math_helper.cpp) set(concurrencpp_headers @@ -78,10 +79,10 @@ set(concurrencpp_headers include/concurrencpp/threads/async_condition_variable.h include/concurrencpp/threads/thread.h include/concurrencpp/threads/cache_line.h - include/concurrencpp/timers/constants.h include/concurrencpp/timers/timer.h include/concurrencpp/timers/timer_queue.h include/concurrencpp/utils/bind.h + include/concurrencpp/utils/throw_helper.h include/concurrencpp/utils/math_helper.h include/concurrencpp/utils/slist.h include/concurrencpp/utils/dlist.h) diff --git a/include/concurrencpp/errors.h b/include/concurrencpp/errors.h index 3965eb47..b90ade1f 100644 --- a/include/concurrencpp/errors.h +++ b/include/concurrencpp/errors.h @@ -1,6 +1,8 @@ #ifndef CONCURRENCPP_ERRORS_H #define CONCURRENCPP_ERRORS_H +#include "concurrencpp/platform_defs.h" + #include namespace concurrencpp::errors { diff --git a/include/concurrencpp/executors/constants.h b/include/concurrencpp/executors/constants.h index e8f20782..6b9bbb45 100644 --- a/include/concurrencpp/executors/constants.h +++ b/include/concurrencpp/executors/constants.h @@ -5,22 +5,22 @@ #include namespace concurrencpp::details::consts { - inline const char* k_inline_executor_name = "concurrencpp::inline_executor"; + inline const char* k_inline_executor_name = "inline_executor"; constexpr int k_inline_executor_max_concurrency_level = 0; - inline const char* k_thread_executor_name = "concurrencpp::thread_executor"; + inline const char* k_thread_executor_name = "thread_executor"; constexpr int k_thread_executor_max_concurrency_level = std::numeric_limits::max(); - inline const char* k_thread_pool_executor_name = "concurrencpp::thread_pool_executor"; - inline const char* k_background_executor_name = "concurrencpp::background_executor"; + inline const char* k_thread_pool_executor_name = "thread_pool_executor"; + inline const char* k_background_executor_name = "background_executor"; constexpr int k_worker_thread_max_concurrency_level = 1; - inline const char* k_worker_thread_executor_name = "concurrencpp::worker_thread_executor"; + inline const char* k_worker_thread_executor_name = "worker_thread_executor"; - inline const char* k_manual_executor_name = "concurrencpp::manual_executor"; + inline const char* k_manual_executor_name = "manual_executor"; constexpr int k_manual_executor_max_concurrency_level = std::numeric_limits::max(); - inline const char* k_timer_queue_name = "concurrencpp::timer_queue"; + inline const char* k_timer_queue_name = "timer_queue"; inline const char* k_executor_shutdown_err_msg = " - shutdown has been called on this executor."; } // namespace concurrencpp::details::consts diff --git a/include/concurrencpp/executors/executor.h b/include/concurrencpp/executors/executor.h index 87475af0..b4b3aafb 100644 --- a/include/concurrencpp/executors/executor.h +++ b/include/concurrencpp/executors/executor.h @@ -10,7 +10,6 @@ #include namespace concurrencpp::details { - [[noreturn]] CRCPP_API void throw_runtime_shutdown_exception(std::string_view executor_name); CRCPP_API std::string make_executor_worker_name(std::string_view executor_name); } // namespace concurrencpp::details @@ -111,12 +110,12 @@ namespace concurrencpp { } public: + const std::string name; + executor(std::string_view name) : name(name) {} virtual ~executor() noexcept = default; - const std::string name; - virtual void enqueue(concurrencpp::task task) = 0; virtual void enqueue(std::span tasks) = 0; diff --git a/include/concurrencpp/executors/inline_executor.h b/include/concurrencpp/executors/inline_executor.h index 295c5809..5f4edf15 100644 --- a/include/concurrencpp/executors/inline_executor.h +++ b/include/concurrencpp/executors/inline_executor.h @@ -12,7 +12,7 @@ namespace concurrencpp { void throw_if_aborted() const { if (m_abort.load(std::memory_order_relaxed)) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } } diff --git a/include/concurrencpp/executors/manual_executor.h b/include/concurrencpp/executors/manual_executor.h index 5b535eb1..9a547ef6 100644 --- a/include/concurrencpp/executors/manual_executor.h +++ b/include/concurrencpp/executors/manual_executor.h @@ -31,11 +31,15 @@ namespace concurrencpp { return std::chrono::system_clock::now() + ms; } - size_t loop_impl(size_t max_count); - size_t loop_until_impl(size_t max_count, std::chrono::time_point deadline); + size_t loop_impl(size_t max_count, const char* calling_method); + size_t loop_until_impl(size_t max_count, + std::chrono::time_point deadline, + const char* calling_method); - void wait_for_tasks_impl(size_t count); - size_t wait_for_tasks_impl(size_t count, std::chrono::time_point deadline); + void wait_for_tasks_impl(size_t count, const char* calling_method); + size_t wait_for_tasks_impl(size_t count, + std::chrono::time_point deadline, + const char* calling_method); public: manual_executor(); @@ -58,7 +62,7 @@ namespace concurrencpp { template bool loop_once_until(std::chrono::time_point timeout_time) { - return loop_until_impl(1, to_system_time_point(timeout_time)); + return loop_until_impl(1, to_system_time_point(timeout_time), "loop_once_until"); } size_t loop(size_t max_count); @@ -66,7 +70,7 @@ namespace concurrencpp { template size_t loop_until(size_t max_count, std::chrono::time_point timeout_time) { - return loop_until_impl(max_count, to_system_time_point(timeout_time)); + return loop_until_impl(max_count, to_system_time_point(timeout_time), "loop_until"); } void wait_for_task(); @@ -74,7 +78,7 @@ namespace concurrencpp { template bool wait_for_task_until(std::chrono::time_point timeout_time) { - return wait_for_tasks_impl(1, to_system_time_point(timeout_time)) == 1; + return wait_for_tasks_impl(1, to_system_time_point(timeout_time), "wait_for_task_until") == 1; } void wait_for_tasks(size_t count); @@ -82,7 +86,7 @@ namespace concurrencpp { template size_t wait_for_tasks_until(size_t count, std::chrono::time_point timeout_time) { - return wait_for_tasks_impl(count, to_system_time_point(timeout_time)); + return wait_for_tasks_impl(count, to_system_time_point(timeout_time), "wait_for_tasks_until"); } }; } // namespace concurrencpp diff --git a/include/concurrencpp/results/constants.h b/include/concurrencpp/results/constants.h index b96e321d..a9cb19c5 100644 --- a/include/concurrencpp/results/constants.h +++ b/include/concurrencpp/results/constants.h @@ -6,113 +6,21 @@ namespace concurrencpp::details::consts { * result_promise */ - inline const char* k_result_promise_set_result_error_msg = "concurrencpp::result_promise::set_result() - empty result_promise."; - - inline const char* k_result_promise_set_exception_error_msg = - "concurrencpp::result_promise::set_exception() - empty result_promise."; - - inline const char* k_result_promise_set_exception_null_exception_error_msg = - "concurrencpp::result_promise::set_exception() - exception pointer is null."; - - inline const char* k_result_promise_set_from_function_error_msg = - "concurrencpp::result_promise::set_from_function() - empty result_promise."; - - inline const char* k_result_promise_get_result_error_msg = "concurrencpp::result_promise::get_result() - empty result_promise."; - inline const char* k_result_promise_get_result_already_retrieved_error_msg = "concurrencpp::result_promise::get_result() - result was already retrieved."; /* * result */ - - inline const char* k_result_status_error_msg = "concurrencpp::result::status() - result is empty."; - - inline const char* k_result_get_error_msg = "concurrencpp::result::get() - result is empty."; - - inline const char* k_result_wait_error_msg = "concurrencpp::result::wait() - result is empty."; - - inline const char* k_result_wait_for_error_msg = "concurrencpp::result::wait_for() - result is empty."; - - inline const char* k_result_wait_until_error_msg = "concurrencpp::result::wait_until() - result is empty."; - - inline const char* k_result_operator_co_await_error_msg = "concurrencpp::result::operator co_await() - result is empty."; - - inline const char* k_result_resolve_error_msg = "concurrencpp::result::resolve() - result is empty."; - - inline const char* k_executor_exception_error_msg = - "concurrencpp::concurrencpp::result - an exception was thrown while trying to enqueue result continuation."; - + inline const char* k_broken_task_exception_error_msg = "concurrencpp::result - associated task was interrupted abnormally"; /* * when_xxx */ - inline const char* k_make_exceptional_result_exception_null_error_msg = - "concurrencpp::make_exceptional_result() - given exception_ptr is null."; - - inline const char* k_make_exceptional_lazy_result_exception_null_error_msg = - "concurrencpp::make_exceptional_lazy_result() - given exception_ptr is null."; - - inline const char* k_when_all_empty_result_error_msg = "concurrencpp::when_all() - one of the result objects is empty."; - - inline const char* k_when_all_null_resume_executor_error_msg = "concurrencpp::when_all() - given resume_executor is null."; - - inline const char* k_when_any_empty_result_error_msg = "concurrencpp::when_any() - one of the result objects is empty."; - inline const char* k_when_any_empty_range_error_msg = "concurrencpp::when_any() - given range contains no elements."; - inline const char* k_when_any_null_resume_executor_error_msg = "concurrencpp::when_any() - given resume_executor is null."; - - /* - * shared_result - */ - - inline const char* k_shared_result_status_error_msg = "concurrencpp::shared_result::status() - result is empty."; - - inline const char* k_shared_result_get_error_msg = "concurrencpp::shared_result::get() - result is empty."; - - inline const char* k_shared_result_wait_error_msg = "concurrencpp::shared_result::wait() - result is empty."; - - inline const char* k_shared_result_wait_for_error_msg = "concurrencpp::shared_result::wait_for() - result is empty."; - - inline const char* k_shared_result_wait_until_error_msg = "concurrencpp::shared_result::wait_until() - result is empty."; - - inline const char* k_shared_result_operator_co_await_error_msg = - "concurrencpp::shared_result::operator co_await() - result is empty."; - - inline const char* k_shared_result_resolve_error_msg = "concurrencpp::shared_result::resolve() - result is empty."; - - /* - * lazy_result - */ - - inline const char* k_empty_lazy_result_status_err_msg = "concurrencpp::lazy_result::status - result is empty."; - - inline const char* k_empty_lazy_result_operator_co_await_err_msg = - "concurrencpp::lazy_result::operator co_await - result is empty."; - - inline const char* k_empty_lazy_result_resolve_err_msg = "concurrencpp::lazy_result::resolve - result is empty."; - - inline const char* k_empty_lazy_result_run_err_msg = "concurrencpp::lazy_result::run - result is empty."; - - /* - * resume_on - */ - - inline const char* k_resume_on_null_exception_err_msg = "concurrencpp::resume_on - given executor is null."; - - /* - * generator - */ - inline const char* k_empty_generator_begin_err_msg = "concurrencpp::generator::begin - generator is empty."; - - /* - * parallel-coroutine - */ - inline const char* k_parallel_coroutine_null_exception_err_msg = "concurrencpp::parallel-coroutine - given executor is null."; - } // namespace concurrencpp::details::consts #endif diff --git a/include/concurrencpp/results/generator.h b/include/concurrencpp/results/generator.h index b6de9957..fc5a05a9 100644 --- a/include/concurrencpp/results/generator.h +++ b/include/concurrencpp/results/generator.h @@ -2,6 +2,7 @@ #define CONCURRENCPP_GENERATOR_H #include "concurrencpp/results/constants.h" +#include "concurrencpp/utils/throw_helper.h" #include "concurrencpp/results/impl/generator_state.h" namespace concurrencpp { @@ -18,6 +19,8 @@ namespace concurrencpp { details::coroutine_handle m_coro_handle; public: + static constexpr std::string_view k_class_name = "generator"; + generator(details::coroutine_handle handle) noexcept : m_coro_handle(handle) {} generator(generator&& rhs) noexcept : m_coro_handle(std::exchange(rhs.m_coro_handle, {})) {} @@ -38,9 +41,7 @@ namespace concurrencpp { } iterator begin() { - if (!static_cast(m_coro_handle)) { - throw errors::empty_generator(details::consts::k_empty_generator_begin_err_msg); - } + details::throw_helper::throw_if_empty_object(m_coro_handle, k_class_name, "begin"); assert(!m_coro_handle.done()); m_coro_handle.resume(); diff --git a/include/concurrencpp/results/impl/shared_result_state.h b/include/concurrencpp/results/impl/shared_result_state.h index 8d4b1a1e..669ee744 100644 --- a/include/concurrencpp/results/impl/shared_result_state.h +++ b/include/concurrencpp/results/impl/shared_result_state.h @@ -5,6 +5,7 @@ #include "concurrencpp/results/impl/result_state.h" #include +#include #include diff --git a/include/concurrencpp/results/lazy_result.h b/include/concurrencpp/results/lazy_result.h index 78ba3d72..2afce1e1 100644 --- a/include/concurrencpp/results/lazy_result.h +++ b/include/concurrencpp/results/lazy_result.h @@ -13,18 +13,14 @@ namespace concurrencpp { private: details::coroutine_handle> m_state; - void throw_if_empty(const char* err_msg) const { - if (!static_cast(m_state)) { - throw errors::empty_result(err_msg); - } - } - result run_impl() { lazy_result self(std::move(*this)); co_return co_await self; } public: + static constexpr std::string_view k_class_name = "lazy_result"; + lazy_result() noexcept = default; lazy_result(lazy_result&& rhs) noexcept : m_state(std::exchange(rhs.m_state, {})) {} @@ -55,22 +51,22 @@ namespace concurrencpp { } result_status status() const { - throw_if_empty(details::consts::k_empty_lazy_result_status_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "status"); return m_state.promise().status(); } auto operator co_await() { - throw_if_empty(details::consts::k_empty_lazy_result_operator_co_await_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "operator co_await"); return lazy_awaitable {std::exchange(m_state, {})}; } auto resolve() { - throw_if_empty(details::consts::k_empty_lazy_result_resolve_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "resolve"); return lazy_resolve_awaitable {std::exchange(m_state, {})}; } result run() { - throw_if_empty(details::consts::k_empty_lazy_result_run_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "run"); return run_impl(); } }; diff --git a/include/concurrencpp/results/make_result.h b/include/concurrencpp/results/make_result.h index eeeaee6b..e6495c0f 100644 --- a/include/concurrencpp/results/make_result.h +++ b/include/concurrencpp/results/make_result.h @@ -12,7 +12,7 @@ namespace concurrencpp::details { template lazy_result make_exceptional_lazy_result_impl(std::exception_ptr exception_ptr) { - assert(static_cast(exception_ptr)); + assert(static_cast(exception_ptr)); std::rethrow_exception(exception_ptr); co_await suspend_never {}; } @@ -38,9 +38,7 @@ namespace concurrencpp { template result make_exceptional_result(std::exception_ptr exception_ptr) { - if (!static_cast(exception_ptr)) { - throw std::invalid_argument(details::consts::k_make_exceptional_result_exception_null_error_msg); - } + details::throw_helper::throw_if_null_argument(exception_ptr, "", "make_exceptional_result", "exception_ptr"); details::producer_result_state_ptr promise(new details::result_state()); details::consumer_result_state_ptr state_ptr(promise.get()); @@ -76,7 +74,7 @@ namespace concurrencpp { lazy_result make_ready_lazy_result(type& t) { co_return std::ref(t); } - + template lazy_result make_exceptional_lazy_result(exception_type exception) { throw exception; @@ -85,10 +83,7 @@ namespace concurrencpp { template lazy_result make_exceptional_lazy_result(std::exception_ptr exception_ptr) { - if (!static_cast(exception_ptr)) { - throw std::invalid_argument(details::consts::k_make_exceptional_lazy_result_exception_null_error_msg); - } - + details::throw_helper::throw_if_null_argument(exception_ptr, "", "make_exceptional_lazy_result", "exception_ptr"); return details::make_exceptional_lazy_result_impl(exception_ptr); } } // namespace concurrencpp diff --git a/include/concurrencpp/results/promises.h b/include/concurrencpp/results/promises.h index 9ae94f81..11eb2a36 100644 --- a/include/concurrencpp/results/promises.h +++ b/include/concurrencpp/results/promises.h @@ -24,9 +24,7 @@ namespace concurrencpp::details { "concurrencpp::initialy_rescheduled_promise<> - <> isn't driven from concurrencpp::executor."); static executor_type& to_ref(executor_type* executor_ptr) { - if (executor_ptr == nullptr) { - throw std::invalid_argument(consts::k_parallel_coroutine_null_exception_err_msg); - } + throw_helper::throw_if_null_argument(executor_ptr, "", "parallel-coroutine", "executor"); return *executor_ptr; } diff --git a/include/concurrencpp/results/result.h b/include/concurrencpp/results/result.h index 72cc25fd..eb0de442 100644 --- a/include/concurrencpp/results/result.h +++ b/include/concurrencpp/results/result.h @@ -4,6 +4,7 @@ #include "concurrencpp/errors.h" #include "concurrencpp/utils/bind.h" #include "concurrencpp/results/constants.h" +#include "concurrencpp/utils/throw_helper.h" #include "concurrencpp/results/result_awaitable.h" #include "concurrencpp/results/impl/result_state.h" @@ -23,13 +24,9 @@ namespace concurrencpp { private: details::consumer_result_state_ptr m_state; - void throw_if_empty(const char* message) const { - if (static_cast(!m_state)) { - throw errors::empty_result(message); - } - } - public: + static constexpr std::string_view k_class_name = "result"; + result() noexcept = default; result(result&& rhs) noexcept = default; @@ -52,29 +49,29 @@ namespace concurrencpp { } result_status status() const { - throw_if_empty(details::consts::k_result_status_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "status"); return m_state->status(); } void wait() const { - throw_if_empty(details::consts::k_result_wait_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "wait"); m_state->wait(); } template result_status wait_for(std::chrono::duration duration) const { - throw_if_empty(details::consts::k_result_wait_for_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "wait_for"); return m_state->wait_for(duration); } template result_status wait_until(std::chrono::time_point timeout_time) const { - throw_if_empty(details::consts::k_result_wait_until_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "wait_until"); return m_state->wait_until(timeout_time); } type get() { - throw_if_empty(details::consts::k_result_get_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "get"); m_state->wait(); details::joined_consumer_result_state_ptr state(m_state.release()); @@ -82,12 +79,12 @@ namespace concurrencpp { } auto operator co_await() { - throw_if_empty(details::consts::k_result_operator_co_await_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "operator co_await"); return awaitable {std::move(m_state)}; } auto resolve() { - throw_if_empty(details::consts::k_result_resolve_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "resolve"); return resolve_awaitable {std::move(m_state)}; } }; @@ -106,12 +103,6 @@ namespace concurrencpp { details::producer_result_state_ptr m_producer_state; details::consumer_result_state_ptr m_consumer_state; - void throw_if_empty(const char* message) const { - if (!static_cast(m_producer_state)) { - throw errors::empty_result_promise(message); - } - } - void break_task_if_needed() noexcept { if (!static_cast(m_producer_state)) { return; @@ -127,6 +118,8 @@ namespace concurrencpp { } public: + static constexpr const char* k_class_name = "result_promise"; + result_promise() { m_producer_state.reset(new details::result_state()); m_consumer_state.reset(m_producer_state.get()); @@ -161,39 +154,41 @@ namespace concurrencpp { constexpr auto is_constructable = std::is_constructible_v || std::is_same_v; static_assert(is_constructable, "result_promise::set_result() - <> is not constructable from <>"); - throw_if_empty(details::consts::k_result_promise_set_result_error_msg); + details::throw_helper::throw_if_empty_object(m_producer_state, k_class_name, "set_result"); m_producer_state->set_result(std::forward(arguments)...); m_producer_state.reset(); // publishes the result } void set_exception(std::exception_ptr exception_ptr) { - throw_if_empty(details::consts::k_result_promise_set_exception_error_msg); + details::throw_helper::throw_if_empty_object(m_producer_state, + k_class_name, + "set_exception"); - if (!static_cast(exception_ptr)) { - throw std::invalid_argument(details::consts::k_result_promise_set_exception_null_exception_error_msg); - } + details::throw_helper::throw_if_null_argument(exception_ptr, k_class_name, "set_exception", "exception_ptr"); m_producer_state->set_exception(exception_ptr); m_producer_state.reset(); // publishes the result } template - void set_from_function(callable_type&& callable, argument_types&&... args) noexcept { + void set_from_function(callable_type&& callable, argument_types&&... args) { constexpr auto is_invokable = std::is_invocable_r_v; static_assert( is_invokable, "result_promise::set_from_function() - function(args...) is not invokable or its return type can't be used to construct <>"); - throw_if_empty(details::consts::k_result_promise_set_from_function_error_msg); + details::throw_helper::throw_if_empty_object(m_producer_state, + k_class_name, + "set_from_function"); m_producer_state->from_callable( details::bind(std::forward(callable), std::forward(args)...)); m_producer_state.reset(); // publishes the result } result get_result() { - throw_if_empty(details::consts::k_result_get_error_msg); + details::throw_helper::throw_if_empty_object(m_producer_state, k_class_name, "get_result"); if (!static_cast(m_consumer_state)) { throw errors::result_already_retrieved(details::consts::k_result_promise_get_result_already_retrieved_error_msg); diff --git a/include/concurrencpp/results/resume_on.h b/include/concurrencpp/results/resume_on.h index a1e2c3f2..93e20cd4 100644 --- a/include/concurrencpp/results/resume_on.h +++ b/include/concurrencpp/results/resume_on.h @@ -44,10 +44,7 @@ namespace concurrencpp { auto resume_on(std::shared_ptr executor) { static_assert(std::is_base_of_v, "concurrencpp::resume_on() - given executor does not derive from concurrencpp::executor"); - - if (!static_cast(executor)) { - throw std::invalid_argument(details::consts::k_resume_on_null_exception_err_msg); - } + details::throw_helper::throw_if_null_argument(executor, "", "resume_on", "executor"); return details::resume_on_awaitable(*executor); } diff --git a/include/concurrencpp/results/shared_result.h b/include/concurrencpp/results/shared_result.h index 16940549..547a4486 100644 --- a/include/concurrencpp/results/shared_result.h +++ b/include/concurrencpp/results/shared_result.h @@ -12,13 +12,9 @@ namespace concurrencpp { private: std::shared_ptr> m_state; - void throw_if_empty(const char* message) const { - if (!static_cast(m_state)) { - throw errors::empty_result(message); - } - } - public: + static constexpr std::string_view k_class_name = "shared_result"; + shared_result() noexcept = default; ~shared_result() noexcept = default; @@ -58,40 +54,40 @@ namespace concurrencpp { } result_status status() const { - throw_if_empty(details::consts::k_shared_result_status_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "status"); return m_state->status(); } void wait() { - throw_if_empty(details::consts::k_shared_result_wait_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "wait"); m_state->wait(); } template result_status wait_for(std::chrono::duration duration) { - throw_if_empty(details::consts::k_shared_result_wait_for_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "wait_for"); return m_state->wait_for(duration); } template result_status wait_until(std::chrono::time_point timeout_time) { - throw_if_empty(details::consts::k_shared_result_wait_until_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "wait_until"); return m_state->wait_until(timeout_time); } std::add_lvalue_reference_t get() { - throw_if_empty(details::consts::k_shared_result_get_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "get"); m_state->wait(); return m_state->get(); } auto operator co_await() { - throw_if_empty(details::consts::k_shared_result_operator_co_await_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "operator co_await"); return shared_awaitable {m_state}; } auto resolve() { - throw_if_empty(details::consts::k_shared_result_resolve_error_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "resolve"); return shared_resolve_awaitable {m_state}; } }; diff --git a/include/concurrencpp/results/when_result.h b/include/concurrencpp/results/when_result.h index f92b314a..034648cc 100644 --- a/include/concurrencpp/results/when_result.h +++ b/include/concurrencpp/results/when_result.h @@ -1,8 +1,8 @@ #ifndef CONCURRENCPP_WHEN_RESULT_H #define CONCURRENCPP_WHEN_RESULT_H -#include "concurrencpp/errors.h" #include "concurrencpp/results/resume_on.h" +#include "concurrencpp/utils/throw_helper.h" #include "concurrencpp/results/lazy_result.h" #include @@ -13,17 +13,14 @@ namespace concurrencpp::details { class when_result_helper { private: - static void throw_if_empty_impl(const char* error_message) noexcept { - (void)error_message; + static void throw_if_empty_impl(const char* method_name) noexcept { + (void)method_name; } template - static void throw_if_empty_impl(const char* error_message, const result& result, result_types&&... results) { - if (!static_cast(result)) { - throw errors::empty_result(error_message); - } - - throw_if_empty_impl(error_message, std::forward(results)...); + static void throw_if_empty_impl(const char* method_name, const result& result, result_types&&... results) { + throw_helper::throw_if_null_argument(result, "", method_name, "result"); + throw_if_empty_impl(method_name, std::forward(results)...); } template @@ -63,16 +60,14 @@ namespace concurrencpp::details { } template - static void throw_if_empty_tuple(const char* error_message, result_types&&... results) { - throw_if_empty_impl(error_message, std::forward(results)...); + static void throw_if_empty_tuple(const char* method_name, result_types&&... results) { + throw_if_empty_impl(method_name, std::forward(results)...); } template - static void throw_if_empty_range(const char* error_message, iterator_type begin, iterator_type end) { + static void throw_if_empty_range(const char* method_name, iterator_type begin, iterator_type end) { for (; begin != end; ++begin) { - if (!static_cast((*begin))) { - throw errors::empty_result(error_message); - } + throw_helper::throw_if_null_argument(*begin, "", method_name, "result"); } } @@ -182,9 +177,7 @@ namespace concurrencpp::details { namespace concurrencpp { template lazy_result> when_all(std::shared_ptr resume_executor) { - if (!static_cast(resume_executor)) { - throw std::invalid_argument(details::consts::k_when_all_null_resume_executor_error_msg); - } + details::throw_helper::throw_if_null_argument(resume_executor, "", "when_all", "resume_executor"); auto make_lazy_result = []() -> lazy_result> { co_return std::tuple<> {}; @@ -196,12 +189,8 @@ namespace concurrencpp { template lazy_result::type...>> when_all(std::shared_ptr resume_executor, result_types&&... results) { - details::when_result_helper::throw_if_empty_tuple(details::consts::k_when_all_empty_result_error_msg, - std::forward(results)...); - - if (!static_cast(resume_executor)) { - throw std::invalid_argument(details::consts::k_when_all_null_resume_executor_error_msg); - } + details::when_result_helper::throw_if_empty_tuple("when_all", std::forward(results)...); + details::throw_helper::throw_if_null_argument(resume_executor, "", "when_all", "resume_executor"); return details::when_all_impl(resume_executor, std::make_tuple(std::forward(results)...)); } @@ -209,11 +198,8 @@ namespace concurrencpp { template lazy_result::value_type>> when_all(std::shared_ptr resume_executor, iterator_type begin, iterator_type end) { - details::when_result_helper::throw_if_empty_range(details::consts::k_when_all_empty_result_error_msg, begin, end); - - if (!static_cast(resume_executor)) { - throw std::invalid_argument(details::consts::k_when_all_null_resume_executor_error_msg); - } + details::when_result_helper::throw_if_empty_range("when_all", begin, end); + details::throw_helper::throw_if_null_argument(resume_executor, "", "when_all", "resume_executor"); using type = typename std::iterator_traits::value_type; @@ -244,12 +230,8 @@ namespace concurrencpp { lazy_result>> when_any(std::shared_ptr resume_executor, result_types&&... results) { static_assert(sizeof...(result_types) != 0, "concurrencpp::when_any() - the function must accept at least one result object."); - details::when_result_helper::throw_if_empty_tuple(details::consts::k_when_any_empty_result_error_msg, - std::forward(results)...); - - if (!static_cast(resume_executor)) { - throw std::invalid_argument(details::consts::k_when_any_null_resume_executor_error_msg); - } + details::when_result_helper::throw_if_empty_tuple("when_any", std::forward(results)...); + details::throw_helper::throw_if_null_argument(resume_executor, "", "when_any", "resume_executor"); return details::when_any_impl(resume_executor, std::make_tuple(std::forward(results)...)); } @@ -257,15 +239,12 @@ namespace concurrencpp { template lazy_result::value_type>>> when_any(std::shared_ptr resume_executor, iterator_type begin, iterator_type end) { - details::when_result_helper::throw_if_empty_range(details::consts::k_when_any_empty_result_error_msg, begin, end); - if (begin == end) { throw std::invalid_argument(details::consts::k_when_any_empty_range_error_msg); } - if (!static_cast(resume_executor)) { - throw std::invalid_argument(details::consts::k_when_any_null_resume_executor_error_msg); - } + details::when_result_helper::throw_if_empty_range("when_any", begin, end); + details::throw_helper::throw_if_null_argument(resume_executor, "", "when_any", "resume_executor"); using type = typename std::iterator_traits::value_type; diff --git a/include/concurrencpp/threads/async_condition_variable.h b/include/concurrencpp/threads/async_condition_variable.h index 4cfa28a7..6ca82901 100644 --- a/include/concurrencpp/threads/async_condition_variable.h +++ b/include/concurrencpp/threads/async_condition_variable.h @@ -56,6 +56,8 @@ namespace concurrencpp { lazy_result await_impl(std::shared_ptr resume_executor, scoped_async_lock& lock); public: + static constexpr std::string_view k_class_name = "async_condition_variable"; + async_condition_variable() noexcept = default; ~async_condition_variable() noexcept; diff --git a/include/concurrencpp/threads/async_lock.h b/include/concurrencpp/threads/async_lock.h index 6740cfe0..34989c2e 100644 --- a/include/concurrencpp/threads/async_lock.h +++ b/include/concurrencpp/threads/async_lock.h @@ -57,6 +57,8 @@ namespace concurrencpp { lazy_result lock_impl(std::shared_ptr resume_executor, bool with_raii_guard); public: + static constexpr std::string_view k_class_name = "async_lock"; + ~async_lock() noexcept; lazy_result lock(std::shared_ptr resume_executor); @@ -71,6 +73,8 @@ namespace concurrencpp { bool m_owns = false; public: + static constexpr std::string_view k_class_name = "scoped_async_lock"; + scoped_async_lock() noexcept = default; scoped_async_lock(scoped_async_lock&& rhs) noexcept; diff --git a/include/concurrencpp/threads/constants.h b/include/concurrencpp/threads/constants.h index 9baa4268..cee506cd 100644 --- a/include/concurrencpp/threads/constants.h +++ b/include/concurrencpp/threads/constants.h @@ -2,13 +2,9 @@ #define CONCURRENCPP_THREAD_CONSTS_H namespace concurrencpp::details::consts { - inline const char* k_async_lock_null_resume_executor_err_msg = "concurrencpp::async_lock::lock() - given resume executor is null."; inline const char* k_async_lock_unlock_invalid_lock_err_msg = "concurrencpp::async_lock::unlock() - trying to unlock an unowned lock."; - inline const char* k_scoped_async_lock_null_resume_executor_err_msg = - "concurrencpp::scoped_async_lock::lock() - given resume executor is null."; - inline const char* k_scoped_async_lock_lock_deadlock_err_msg = "concurrencpp::scoped_async_lock::lock() - *this is already locked."; @@ -24,9 +20,6 @@ namespace concurrencpp::details::consts { inline const char* k_scoped_async_lock_unlock_invalid_lock_err_msg = "concurrencpp::scoped_async_lock::unlock() - trying to unlock an unowned lock."; - inline const char* k_async_condition_variable_await_invalid_resume_executor_err_msg = - "concurrencpp::async_condition_variable::await() - resume_executor is null."; - inline const char* k_async_condition_variable_await_lock_unlocked_err_msg = "concurrencpp::async_condition_variable::await() - lock is unlocked."; diff --git a/include/concurrencpp/threads/thread.h b/include/concurrencpp/threads/thread.h index 3faa2b05..1ef61b44 100644 --- a/include/concurrencpp/threads/thread.h +++ b/include/concurrencpp/threads/thread.h @@ -4,6 +4,7 @@ #include "concurrencpp/platform_defs.h" #include +#include #include #include diff --git a/include/concurrencpp/timers/constants.h b/include/concurrencpp/timers/constants.h deleted file mode 100644 index b36e70b8..00000000 --- a/include/concurrencpp/timers/constants.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef CONCURRENCPP_TIMER_CONSTS_H -#define CONCURRENCPP_TIMER_CONSTS_H - -namespace concurrencpp::details::consts { - inline const char* k_timer_empty_get_due_time_err_msg = "concurrencpp::timer::get_due_time() - timer is empty."; - inline const char* k_timer_empty_get_frequency_err_msg = "concurrencpp::timer::get_frequency() - timer is empty."; - inline const char* k_timer_empty_get_executor_err_msg = "concurrencpp::timer::get_executor() - timer is empty."; - inline const char* k_timer_empty_get_timer_queue_err_msg = "concurrencpp::timer::get_timer_queue() - timer is empty."; - inline const char* k_timer_empty_set_frequency_err_msg = "concurrencpp::timer::set_frequency() - timer is empty."; - - inline const char* k_timer_queue_make_timer_executor_null_err_msg = "concurrencpp::timer_queue::make_timer() - executor is null."; - inline const char* k_timer_queue_make_oneshot_timer_executor_null_err_msg = - "concurrencpp::timer_queue::make_one_shot_timer() - executor is null."; - inline const char* k_timer_queue_make_delay_object_executor_null_err_msg = - "concurrencpp::timer_queue::make_delay_object() - executor is null."; - inline const char* k_timer_queue_shutdown_err_msg = "concurrencpp::timer_queue has been shut down."; -} // namespace concurrencpp::details::consts - -#endif diff --git a/include/concurrencpp/timers/timer.h b/include/concurrencpp/timers/timer.h index 93a28f26..0c3bb67a 100644 --- a/include/concurrencpp/timers/timer.h +++ b/include/concurrencpp/timers/timer.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace concurrencpp::details { class CRCPP_API timer_state_base : public std::enable_shared_from_this { @@ -119,6 +120,8 @@ namespace concurrencpp { void throw_if_empty(const char* error_message) const; public: + static constexpr std::string_view k_class_name = "timer"; + timer() noexcept = default; ~timer() noexcept; diff --git a/include/concurrencpp/timers/timer_queue.h b/include/concurrencpp/timers/timer_queue.h index 7d3cb30b..54d89e3b 100644 --- a/include/concurrencpp/timers/timer_queue.h +++ b/include/concurrencpp/timers/timer_queue.h @@ -2,7 +2,6 @@ #define CONCURRENCPP_TIMER_QUEUE_H #include "timer.h" -#include "constants.h" #include "concurrencpp/errors.h" #include "concurrencpp/utils/bind.h" #include "concurrencpp/threads/thread.h" @@ -16,10 +15,6 @@ #include -namespace concurrencpp::details { - enum class timer_request { add, remove }; -} - namespace concurrencpp { class CRCPP_API timer_queue : public std::enable_shared_from_this { @@ -27,14 +22,14 @@ namespace concurrencpp { using timer_ptr = std::shared_ptr; using clock_type = std::chrono::high_resolution_clock; using time_point = std::chrono::time_point; - using request_queue = std::vector>; - + friend class concurrencpp::timer; private: std::atomic_bool m_atomic_abort; std::mutex m_lock; - request_queue m_request_queue; + std::vector m_add_timer_queue; + std::vector m_remove_timer_queue; details::thread m_worker; std::condition_variable m_condition; bool m_abort; @@ -48,18 +43,20 @@ namespace concurrencpp { void add_internal_timer(std::unique_lock& lock, timer_ptr new_timer); void remove_internal_timer(timer_ptr existing_timer); - void add_timer(std::unique_lock& lock, timer_ptr new_timer); + void add_timer(std::unique_lock& lock, timer_ptr new_timer, const char* calling_method); lazy_result make_delay_object_impl(std::chrono::milliseconds due_time, std::shared_ptr self, std::shared_ptr executor); template - timer_ptr make_timer_impl(size_t due_time, + timer_ptr make_timer_impl(const char* calling_method, + size_t due_time, size_t frequency, std::shared_ptr executor, bool is_oneshot, callable_type&& callable) { + assert(calling_method != nullptr); assert(static_cast(executor)); using decayed_type = typename std::decay_t; @@ -72,7 +69,7 @@ namespace concurrencpp { std::forward(callable)); { std::unique_lock lock(m_lock); - add_timer(lock, timer_state); + add_timer(lock, timer_state, calling_method); } return timer_state; @@ -81,6 +78,8 @@ namespace concurrencpp { void work_loop(); public: + static constexpr std::string_view k_class_name = "timer_queue"; + timer_queue(std::chrono::milliseconds max_waiting_time, const std::function& thread_started_callback = {}, const std::function& thread_terminated_callback = {}); @@ -95,11 +94,10 @@ namespace concurrencpp { std::shared_ptr executor, callable_type&& callable, argumet_types&&... arguments) { - if (!static_cast(executor)) { - throw std::invalid_argument(details::consts::k_timer_queue_make_timer_executor_null_err_msg); - } + details::throw_helper::throw_if_null_argument(executor, k_class_name, "make_timer", "executor"); - return make_timer_impl(due_time.count(), + return make_timer_impl("make_timer", + due_time.count(), frequency.count(), std::move(executor), false, @@ -111,11 +109,10 @@ namespace concurrencpp { std::shared_ptr executor, callable_type&& callable, argumet_types&&... arguments) { - if (!static_cast(executor)) { - throw std::invalid_argument(details::consts::k_timer_queue_make_oneshot_timer_executor_null_err_msg); - } + details::throw_helper::throw_if_null_argument(executor, k_class_name, "make_one_shot_timer", "executor"); - return make_timer_impl(due_time.count(), + return make_timer_impl("make_one_shot_timer", + due_time.count(), 0, std::move(executor), true, diff --git a/include/concurrencpp/utils/throw_helper.h b/include/concurrencpp/utils/throw_helper.h new file mode 100644 index 00000000..5d3b9fe9 --- /dev/null +++ b/include/concurrencpp/utils/throw_helper.h @@ -0,0 +1,59 @@ +#ifndef CONCURRENCPP_THROW_HELPER_H +#define CONCURRENCPP_THROW_HELPER_H + +#include "concurrencpp/errors.h" + +#include + +#include +#include + +namespace concurrencpp::details { + struct CRCPP_API throw_helper { + + template + static void throw_if_empty_object(const state_type& state, std::string_view class_name, const char* method) { + if (!static_cast(state)) [[unlikely]] { + throw make_empty_object_exception(class_name, method); + } + } + + template + static void throw_if_null_argument(const state_type& state, + std::string_view class_name, + const char* method, + const char* arg_name) { + if (!static_cast(state)) [[unlikely]] { + throw make_empty_argument_exception(class_name, method, arg_name); + } + } + + [[noreturn]] static void throw_worker_shutdown_exception(std::string_view class_name, const char* method) { + throw make_worker_shutdown_exception(class_name, method); + } + + template + static exception_type make_empty_object_exception(std::string_view class_name, const char* method) { + assert(class_name.data() != nullptr); + assert(method != nullptr && std::strlen(method) != 0); + + char buffer[256]; + std::snprintf(buffer, + std::size(buffer), + "concurrencpp::%s::%s() - %s is empty.", + class_name.data(), + method, + class_name.data()); + + return exception_type(buffer); + } + + static std::invalid_argument make_empty_argument_exception(std::string_view class_name, + const char* method, + const char* arg_name); + + static errors::runtime_shutdown make_worker_shutdown_exception(std::string_view class_name, const char* method); + }; +} // namespace concurrencpp::details + +#endif diff --git a/source/executors/executor.cpp b/source/executors/executor.cpp index 5963eb5a..c0522d56 100644 --- a/source/executors/executor.cpp +++ b/source/executors/executor.cpp @@ -4,11 +4,6 @@ #include "concurrencpp/errors.h" #include "concurrencpp/threads/thread.h" -void concurrencpp::details::throw_runtime_shutdown_exception(std::string_view executor_name) { - const auto error_msg = std::string(executor_name) + consts::k_executor_shutdown_err_msg; - throw errors::runtime_shutdown(error_msg); -} - std::string concurrencpp::details::make_executor_worker_name(std::string_view executor_name) { return std::string(executor_name) + " worker"; } diff --git a/source/executors/manual_executor.cpp b/source/executors/manual_executor.cpp index d42fedf5..7f8522c8 100644 --- a/source/executors/manual_executor.cpp +++ b/source/executors/manual_executor.cpp @@ -1,3 +1,4 @@ +#include "concurrencpp/utils/throw_helper.h" #include "concurrencpp/executors/constants.h" #include "concurrencpp/executors/manual_executor.h" @@ -10,7 +11,7 @@ manual_executor::manual_executor() : void manual_executor::enqueue(concurrencpp::task task) { std::unique_lock lock(m_lock); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } m_tasks.emplace_back(std::move(task)); @@ -22,7 +23,7 @@ void manual_executor::enqueue(concurrencpp::task task) { void manual_executor::enqueue(std::span tasks) { std::unique_lock lock(m_lock); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } m_tasks.insert(m_tasks.end(), std::make_move_iterator(tasks.begin()), std::make_move_iterator(tasks.end())); @@ -44,7 +45,7 @@ bool manual_executor::empty() const { return size() == 0; } -size_t manual_executor::loop_impl(size_t max_count) { +size_t manual_executor::loop_impl(size_t max_count, const char* calling_method) { if (max_count == 0) { return 0; } @@ -74,13 +75,15 @@ size_t manual_executor::loop_impl(size_t max_count) { } if (shutdown_requested()) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, calling_method); } return executed; } -size_t manual_executor::loop_until_impl(size_t max_count, std::chrono::time_point deadline) { +size_t manual_executor::loop_until_impl(size_t max_count, + std::chrono::time_point deadline, + const char* calling_method) { if (max_count == 0) { return 0; } @@ -121,16 +124,16 @@ size_t manual_executor::loop_until_impl(size_t max_count, std::chrono::time_poin } if (shutdown_requested()) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, calling_method); } return executed; } -void manual_executor::wait_for_tasks_impl(size_t count) { +void manual_executor::wait_for_tasks_impl(size_t count, const char* calling_method) { if (count == 0) { if (shutdown_requested()) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, calling_method); } return; } @@ -141,13 +144,15 @@ void manual_executor::wait_for_tasks_impl(size_t count) { }); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, calling_method); } assert(m_tasks.size() >= count); } -size_t manual_executor::wait_for_tasks_impl(size_t count, std::chrono::time_point deadline) { +size_t manual_executor::wait_for_tasks_impl(size_t count, + std::chrono::time_point deadline, + const char* calling_method) { deadline += std::chrono::milliseconds(1); std::unique_lock lock(m_lock); @@ -156,26 +161,26 @@ size_t manual_executor::wait_for_tasks_impl(size_t count, std::chrono::time_poin }); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, calling_method); } return m_tasks.size(); } bool manual_executor::loop_once() { - return loop_impl(1) != 0; + return loop_impl(1, "loop_once") != 0; } bool manual_executor::loop_once_for(std::chrono::milliseconds max_waiting_time) { if (max_waiting_time == std::chrono::milliseconds(0)) { - return loop_impl(1) != 0; + return loop_impl(1, "loop_once_for") != 0; } - return loop_until_impl(1, time_point_from_now(max_waiting_time)); + return loop_until_impl(1, time_point_from_now(max_waiting_time), "loop_once_for"); } size_t manual_executor::loop(size_t max_count) { - return loop_impl(max_count); + return loop_impl(max_count, "loop"); } size_t manual_executor::loop_for(size_t max_count, std::chrono::milliseconds max_waiting_time) { @@ -184,16 +189,16 @@ size_t manual_executor::loop_for(size_t max_count, std::chrono::milliseconds max } if (max_waiting_time == std::chrono::milliseconds(0)) { - return loop_impl(max_count); + return loop_impl(max_count, "loop_for"); } - return loop_until_impl(max_count, time_point_from_now(max_waiting_time)); + return loop_until_impl(max_count, time_point_from_now(max_waiting_time), "loop_for"); } size_t manual_executor::clear() { std::unique_lock lock(m_lock); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "clear"); } const auto tasks = std::move(m_tasks); @@ -202,19 +207,19 @@ size_t manual_executor::clear() { } void manual_executor::wait_for_task() { - wait_for_tasks_impl(1); + wait_for_tasks_impl(1, "wait_for_task"); } bool manual_executor::wait_for_task_for(std::chrono::milliseconds max_waiting_time) { - return wait_for_tasks_impl(1, time_point_from_now(max_waiting_time)) == 1; + return wait_for_tasks_impl(1, time_point_from_now(max_waiting_time), "wait_for_task_for") == 1; } void manual_executor::wait_for_tasks(size_t count) { - wait_for_tasks_impl(count); + wait_for_tasks_impl(count, "wait_for_tasks"); } size_t manual_executor::wait_for_tasks_for(size_t count, std::chrono::milliseconds max_waiting_time) { - return wait_for_tasks_impl(count, time_point_from_now(max_waiting_time)); + return wait_for_tasks_impl(count, time_point_from_now(max_waiting_time), "wait_for_tasks_for"); } void manual_executor::shutdown() { diff --git a/source/executors/thread_executor.cpp b/source/executors/thread_executor.cpp index 36879d88..60227f29 100644 --- a/source/executors/thread_executor.cpp +++ b/source/executors/thread_executor.cpp @@ -1,3 +1,4 @@ +#include "concurrencpp/utils/throw_helper.h" #include "concurrencpp/executors/constants.h" #include "concurrencpp/executors/thread_executor.h" @@ -31,7 +32,7 @@ void thread_executor::enqueue_impl(std::unique_lock& lock, concurren void thread_executor::enqueue(concurrencpp::task task) { std::unique_lock lock(m_lock); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } enqueue_impl(lock, task); @@ -40,7 +41,7 @@ void thread_executor::enqueue(concurrencpp::task task) { void thread_executor::enqueue(std::span tasks) { std::unique_lock lock(m_lock); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } for (auto& task : tasks) { diff --git a/source/executors/thread_pool_executor.cpp b/source/executors/thread_pool_executor.cpp index 97412bb4..001b6c1f 100644 --- a/source/executors/thread_pool_executor.cpp +++ b/source/executors/thread_pool_executor.cpp @@ -1,3 +1,4 @@ +#include "concurrencpp/utils/throw_helper.h" #include "concurrencpp/executors/thread_pool_executor.h" #include @@ -411,7 +412,7 @@ void thread_pool_worker::ensure_worker_active(bool first_enqueuer, std::unique_l void thread_pool_worker::enqueue_foreign(concurrencpp::task& task) { std::unique_lock lock(m_lock); if (m_abort) { - throw_runtime_shutdown_exception(m_parent_pool.name); + throw_helper::throw_worker_shutdown_exception(m_parent_pool.name, "enqueue"); } m_task_found_or_abort.store(true, std::memory_order_relaxed); @@ -424,7 +425,7 @@ void thread_pool_worker::enqueue_foreign(concurrencpp::task& task) { void thread_pool_worker::enqueue_foreign(std::span tasks) { std::unique_lock lock(m_lock); if (m_abort) { - throw_runtime_shutdown_exception(m_parent_pool.name); + throw_helper::throw_worker_shutdown_exception(m_parent_pool.name, "enqueue"); } m_task_found_or_abort.store(true, std::memory_order_relaxed); @@ -437,7 +438,7 @@ void thread_pool_worker::enqueue_foreign(std::span tasks) { void thread_pool_worker::enqueue_foreign(std::deque::iterator begin, std::deque::iterator end) { std::unique_lock lock(m_lock); if (m_abort) { - throw_runtime_shutdown_exception(m_parent_pool.name); + throw_helper::throw_worker_shutdown_exception(m_parent_pool.name, "enqueue"); } m_task_found_or_abort.store(true, std::memory_order_relaxed); @@ -450,7 +451,7 @@ void thread_pool_worker::enqueue_foreign(std::deque::iterator begin, std:: void thread_pool_worker::enqueue_foreign(std::span::iterator begin, std::span::iterator end) { std::unique_lock lock(m_lock); if (m_abort) { - throw_runtime_shutdown_exception(m_parent_pool.name); + throw_helper::throw_worker_shutdown_exception(m_parent_pool.name, "enqueue"); } m_task_found_or_abort.store(true, std::memory_order_relaxed); @@ -462,7 +463,7 @@ void thread_pool_worker::enqueue_foreign(std::span::iterator void thread_pool_worker::enqueue_local(concurrencpp::task& task) { if (m_atomic_abort.load(std::memory_order_relaxed)) { - throw_runtime_shutdown_exception(m_parent_pool.name); + throw_helper::throw_worker_shutdown_exception(m_parent_pool.name, "enqueue"); } m_private_queue.emplace_back(std::move(task)); @@ -470,7 +471,7 @@ void thread_pool_worker::enqueue_local(concurrencpp::task& task) { void thread_pool_worker::enqueue_local(std::span tasks) { if (m_atomic_abort.load(std::memory_order_relaxed)) { - throw_runtime_shutdown_exception(m_parent_pool.name); + throw_helper::throw_worker_shutdown_exception(m_parent_pool.name, "enqueue"); } m_private_queue.insert(m_private_queue.end(), std::make_move_iterator(tasks.begin()), std::make_move_iterator(tasks.end())); diff --git a/source/executors/worker_thread_executor.cpp b/source/executors/worker_thread_executor.cpp index 5f6e8997..f36c16c4 100644 --- a/source/executors/worker_thread_executor.cpp +++ b/source/executors/worker_thread_executor.cpp @@ -87,7 +87,7 @@ void worker_thread_executor::work_loop() { void worker_thread_executor::enqueue_local(concurrencpp::task& task) { if (m_private_atomic_abort.load(std::memory_order_relaxed)) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } m_private_queue.emplace_back(std::move(task)); @@ -95,7 +95,7 @@ void worker_thread_executor::enqueue_local(concurrencpp::task& task) { void worker_thread_executor::enqueue_local(std::span tasks) { if (m_private_atomic_abort.load(std::memory_order_relaxed)) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } m_private_queue.insert(m_private_queue.end(), std::make_move_iterator(tasks.begin()), std::make_move_iterator(tasks.end())); @@ -104,7 +104,7 @@ void worker_thread_executor::enqueue_local(std::span tasks) void worker_thread_executor::enqueue_foreign(concurrencpp::task& task) { std::unique_lock lock(m_lock); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } const auto is_empty = m_public_queue.empty(); @@ -124,7 +124,7 @@ void worker_thread_executor::enqueue_foreign(concurrencpp::task& task) { void worker_thread_executor::enqueue_foreign(std::span tasks) { std::unique_lock lock(m_lock); if (m_abort) { - details::throw_runtime_shutdown_exception(name); + details::throw_helper::throw_worker_shutdown_exception(name, "enqueue"); } const auto is_empty = m_public_queue.empty(); diff --git a/source/threads/async_condition_variable.cpp b/source/threads/async_condition_variable.cpp index 4953965c..431120c6 100644 --- a/source/threads/async_condition_variable.cpp +++ b/source/threads/async_condition_variable.cpp @@ -1,5 +1,6 @@ #include "concurrencpp/results/resume_on.h" #include "concurrencpp/threads/constants.h" +#include "concurrencpp/utils/throw_helper.h" #include "concurrencpp/threads/async_condition_variable.h" using concurrencpp::executor; @@ -42,9 +43,7 @@ async_condition_variable::~async_condition_variable() noexcept { } void async_condition_variable::verify_await_params(const std::shared_ptr& resume_executor, const scoped_async_lock& lock) { - if (!static_cast(resume_executor)) { - throw std::invalid_argument(details::consts::k_async_condition_variable_await_invalid_resume_executor_err_msg); - } + details::throw_helper::throw_if_null_argument(resume_executor, k_class_name, "await", "resume_executor"); if (!lock.owns_lock()) { throw std::invalid_argument(details::consts::k_async_condition_variable_await_lock_unlocked_err_msg); diff --git a/source/threads/async_lock.cpp b/source/threads/async_lock.cpp index 62a6ccdf..92a76566 100644 --- a/source/threads/async_lock.cpp +++ b/source/threads/async_lock.cpp @@ -89,10 +89,7 @@ concurrencpp::lazy_result async_lock::lock_impl(std::shared_p } concurrencpp::lazy_result async_lock::lock(std::shared_ptr resume_executor) { - if (!static_cast(resume_executor)) { - throw std::invalid_argument(details::consts::k_async_lock_null_resume_executor_err_msg); - } - + details::throw_helper::throw_if_null_argument(resume_executor, k_class_name, "lock", "resume_executor"); return lock_impl(std::move(resume_executor), true); } @@ -160,9 +157,7 @@ scoped_async_lock::~scoped_async_lock() noexcept { } concurrencpp::lazy_result scoped_async_lock::lock(std::shared_ptr resume_executor) { - if (!static_cast(resume_executor)) { - throw std::invalid_argument(details::consts::k_scoped_async_lock_null_resume_executor_err_msg); - } + details::throw_helper::throw_if_null_argument(resume_executor, k_class_name, "lock", "resume_executor"); if (m_lock == nullptr) { throw std::system_error(static_cast(std::errc::operation_not_permitted), diff --git a/source/timers/timer.cpp b/source/timers/timer.cpp index fb3fb9a6..a08f02a2 100644 --- a/source/timers/timer.cpp +++ b/source/timers/timer.cpp @@ -1,6 +1,5 @@ #include "concurrencpp/timers/timer.h" #include "concurrencpp/timers/timer_queue.h" -#include "concurrencpp/timers/constants.h" #include "concurrencpp/errors.h" #include "concurrencpp/results/result.h" @@ -47,22 +46,22 @@ void timer::throw_if_empty(const char* error_message) const { } std::chrono::milliseconds timer::get_due_time() const { - throw_if_empty(details::consts::k_timer_empty_get_due_time_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "get_due_time"); return std::chrono::milliseconds(m_state->get_due_time()); } std::chrono::milliseconds timer::get_frequency() const { - throw_if_empty(details::consts::k_timer_empty_get_frequency_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "get_frequency"); return std::chrono::milliseconds(m_state->get_frequency()); } std::shared_ptr timer::get_executor() const { - throw_if_empty(details::consts::k_timer_empty_get_executor_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "get_executor"); return m_state->get_executor(); } std::weak_ptr timer::get_timer_queue() const { - throw_if_empty(details::consts::k_timer_empty_get_timer_queue_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "get_timer_queue"); return m_state->get_timer_queue(); } @@ -84,7 +83,7 @@ void timer::cancel() { } void timer::set_frequency(std::chrono::milliseconds new_frequency) { - throw_if_empty(details::consts::k_timer_empty_set_frequency_err_msg); + details::throw_helper::throw_if_empty_object(m_state, k_class_name, "set_frequency"); return m_state->set_new_frequency(new_frequency.count()); } diff --git a/source/timers/timer_queue.cpp b/source/timers/timer_queue.cpp index ce09fe03..9df06232 100644 --- a/source/timers/timer_queue.cpp +++ b/source/timers/timer_queue.cpp @@ -1,9 +1,9 @@ #include "concurrencpp/timers/timer.h" #include "concurrencpp/timers/timer_queue.h" -#include "concurrencpp/coroutines/coroutine.h" -#include "concurrencpp/executors/constants.h" #include "concurrencpp/executors/executor.h" +#include "concurrencpp/executors/constants.h" +#include "concurrencpp/coroutines/coroutine.h" #include #include @@ -14,12 +14,10 @@ using namespace std::chrono; using concurrencpp::timer; using concurrencpp::timer_queue; -using concurrencpp::details::timer_request; using concurrencpp::details::timer_state_base; using timer_ptr = timer_queue::timer_ptr; using time_point = timer_queue::time_point; -using request_queue = timer_queue::request_queue; namespace concurrencpp::details { namespace { @@ -57,19 +55,6 @@ namespace concurrencpp::details { m_iterator_mapper.erase(timer_it); } - void process_request_queue(request_queue& queue) { - for (auto& request : queue) { - auto& timer_ptr = request.first; - const auto opt = request.second; - - if (opt == timer_request::add) { - add_timer_internal(std::move(timer_ptr)); - } else { - remove_timer_internal(std::move(timer_ptr)); - } - } - } - void reset_containers_memory() noexcept { assert(empty()); timer_set timers; @@ -84,8 +69,14 @@ namespace concurrencpp::details { return m_timers.empty(); } - ::time_point process_timers(request_queue& queue) { - process_request_queue(queue); + ::time_point process_timers(std::vector add_timer_queue, std::vector remove_timer_queue) { + for (auto& timer : add_timer_queue) { + add_timer_internal(std::move(timer)); + } + + for (auto& timer : remove_timer_queue) { + remove_timer_internal(std::move(timer)); + } const auto now = high_resolution_clock::now(); @@ -148,9 +139,8 @@ namespace concurrencpp::details { timer_queue::timer_queue(milliseconds max_waiting_time, const std::function& thread_started_callback, const std::function& thread_terminated_callback) : - m_atomic_abort(false), m_abort(false), m_idle(true), - m_max_waiting_time(max_waiting_time), - m_thread_started_callback(thread_started_callback), + m_atomic_abort(false), + m_abort(false), m_idle(true), m_max_waiting_time(max_waiting_time), m_thread_started_callback(thread_started_callback), m_thread_terminated_callback(thread_terminated_callback) {} timer_queue::~timer_queue() noexcept { @@ -160,7 +150,7 @@ timer_queue::~timer_queue() noexcept { void timer_queue::add_internal_timer(std::unique_lock& lock, timer_ptr new_timer) { assert(lock.owns_lock()); - m_request_queue.emplace_back(std::move(new_timer), timer_request::add); + m_add_timer_queue.emplace_back(std::move(new_timer)); lock.unlock(); m_condition.notify_one(); @@ -169,17 +159,17 @@ void timer_queue::add_internal_timer(std::unique_lock& lock, timer_p void timer_queue::remove_internal_timer(timer_ptr existing_timer) { { std::unique_lock lock(m_lock); - m_request_queue.emplace_back(std::move(existing_timer), timer_request::remove); + m_remove_timer_queue.emplace_back(std::move(existing_timer)); } m_condition.notify_one(); } -void timer_queue::add_timer(std::unique_lock& lock, timer_ptr new_timer) { +void timer_queue::add_timer(std::unique_lock& lock, timer_ptr new_timer, const char* calling_method) { assert(lock.owns_lock()); if (m_abort) { - throw errors::runtime_shutdown(details::consts::k_timer_queue_shutdown_err_msg); + details::throw_helper::throw_worker_shutdown_exception(k_class_name, calling_method); } auto old_thread = ensure_worker_thread(lock); @@ -198,7 +188,7 @@ void timer_queue::work_loop() { std::unique_lock lock(m_lock); if (internal_state.empty()) { const auto res = m_condition.wait_for(lock, m_max_waiting_time, [this] { - return !m_request_queue.empty() || m_abort; + return !m_add_timer_queue.empty() || !m_remove_timer_queue.empty() || m_abort; }); if (!res) { @@ -209,7 +199,7 @@ void timer_queue::work_loop() { } else { m_condition.wait_until(lock, next_deadline, [this] { - return !m_request_queue.empty() || m_abort; + return !m_add_timer_queue.empty() || !m_remove_timer_queue.empty() || m_abort; }); } @@ -217,10 +207,11 @@ void timer_queue::work_loop() { return; } - auto request_queue = std::move(m_request_queue); + auto add_timer_queue = std::move(m_add_timer_queue); + auto remove_timer_queue = std::move(m_remove_timer_queue); lock.unlock(); - next_deadline = internal_state.process_timers(request_queue); + next_deadline = internal_state.process_timers(std::move(add_timer_queue), std::move(remove_timer_queue)); const auto now = clock_type::now(); if (next_deadline <= now) { continue; @@ -245,7 +236,8 @@ void timer_queue::shutdown() { return; // nothing to shut down } - m_request_queue.clear(); + auto add_timer_queue = std::move(m_add_timer_queue); + auto remove_timer_queue = std::move(m_remove_timer_queue); lock.unlock(); m_condition.notify_all(); @@ -292,7 +284,8 @@ concurrencpp::lazy_result timer_queue::make_delay_object_impl(std::chrono: void await_suspend(details::coroutine_handle coro_handle) noexcept { try { - m_parent_queue.make_timer_impl(m_due_time_ms, + m_parent_queue.make_timer_impl("make_delay_object", + m_due_time_ms, 0, std::move(m_executor), true, @@ -315,9 +308,7 @@ concurrencpp::lazy_result timer_queue::make_delay_object_impl(std::chrono: concurrencpp::lazy_result timer_queue::make_delay_object(std::chrono::milliseconds due_time, std::shared_ptr executor) { - if (!static_cast(executor)) { - throw std::invalid_argument(details::consts::k_timer_queue_make_delay_object_executor_null_err_msg); - } + details::throw_helper::throw_if_null_argument(executor, k_class_name, "make_delay_object", "executor"); return make_delay_object_impl(due_time, shared_from_this(), std::move(executor)); } diff --git a/source/utils/throw_helper.cpp b/source/utils/throw_helper.cpp new file mode 100644 index 00000000..fbb3a6dd --- /dev/null +++ b/source/utils/throw_helper.cpp @@ -0,0 +1,39 @@ +#include "concurrencpp/utils/throw_helper.h" + +using concurrencpp::details::throw_helper; + +std::invalid_argument throw_helper::make_empty_argument_exception(std::string_view class_name, + const char* method, + const char* arg_name) { + assert(method != nullptr && std::strlen(method) != 0); + assert(arg_name != nullptr && std::strlen(arg_name) != 0); + + char buffer[256]; + if (class_name.empty()) { + std::snprintf(buffer, std::size(buffer), "concurrencpp::%s() - given %s is null or empty.", method, arg_name); + } else { + std::snprintf(buffer, + std::size(buffer), + "concurrencpp::%s::%s() - given %s is null or empty.", + class_name.data(), + method, + arg_name); + } + + return std::invalid_argument(buffer); +} + +concurrencpp::errors::runtime_shutdown throw_helper::make_worker_shutdown_exception(std::string_view class_name, const char* method) { + assert(class_name.data() != nullptr); + assert(method != nullptr && std::strlen(method) != 0); + + char buffer[256]; + std::snprintf(buffer, + std::size(buffer), + "concurrencpp::%s::%s() - %s has already been shut down.", + class_name.data(), + method, + class_name.data()); + + return errors::runtime_shutdown(buffer); +} diff --git a/test/include/infra/assertions.h b/test/include/infra/assertions.h index 223266af..b0f9449f 100644 --- a/test/include/infra/assertions.h +++ b/test/include/infra/assertions.h @@ -113,6 +113,7 @@ namespace concurrencpp::tests { assert_false(true); } + //TODO: maybe remove this template void assert_throws_with_error_message(task_type&& task, std::string_view error_msg) { try { @@ -140,6 +141,20 @@ namespace concurrencpp::tests { assert_false(true); } + template + void assert_throws(task_type&& task, const exception_type& ex) { + try { + task(); + } catch (const exception_type& e) { + assert_equal(std::string_view(ex.what()), std::string_view(e.what())); + return; + } catch (...) { + } + + assert_false(true); + } + + } // namespace concurrencpp::tests #endif // CONCURRENCPP_ASSERTIONS_H diff --git a/test/source/tests/coroutine_tests/coroutine_promise_tests.cpp b/test/source/tests/coroutine_tests/coroutine_promise_tests.cpp index d254cbfa..cea8eabb 100644 --- a/test/source/tests/coroutine_tests/coroutine_promise_tests.cpp +++ b/test/source/tests/coroutine_tests/coroutine_promise_tests.cpp @@ -27,6 +27,7 @@ namespace concurrencpp::tests { void test_lazy_result_promise(); } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; using worker_ptr = std::shared_ptr; namespace concurrencpp::tests { @@ -274,11 +275,13 @@ namespace concurrencpp::tests { void concurrencpp::tests::test_initialy_rescheduled_null_result_promise() { // null resume executor - assert_throws_with_error_message( + const auto expected_error = throw_helper::make_empty_argument_exception("", "parallel-coroutine", "executor"); + + assert_throws( [] { null_executor_null_result_coro({}, {}); }, - concurrencpp::details::consts::k_parallel_coroutine_null_exception_err_msg); + expected_error); test_initialy_rescheduled_null_result_promise_value(); test_initialy_rescheduled_null_result_promise_exception(); @@ -382,11 +385,13 @@ namespace concurrencpp::tests { void concurrencpp::tests::test_initialy_rescheduled_result_promise() { // null resume executor - assert_throws_with_error_message( + const auto expected_error = throw_helper::make_empty_argument_exception("", "parallel-coroutine", "executor"); + + assert_throws( [] { null_executor_result_coro({}, {}); }, - concurrencpp::details::consts::k_parallel_coroutine_null_exception_err_msg); + expected_error); test_initialy_rescheduled_result_promise_value(); test_initialy_rescheduled_result_promise_exception(); diff --git a/test/source/tests/executor_tests/inline_executor_tests.cpp b/test/source/tests/executor_tests/inline_executor_tests.cpp index f6fc34e6..d3541174 100644 --- a/test/source/tests/executor_tests/inline_executor_tests.cpp +++ b/test/source/tests/executor_tests/inline_executor_tests.cpp @@ -41,6 +41,7 @@ namespace concurrencpp::tests { } // namespace concurrencpp::tests using concurrencpp::details::thread; +using concurrencpp::details::throw_helper; void concurrencpp::tests::test_inline_executor_name() { auto executor = std::make_shared(); @@ -59,15 +60,19 @@ void concurrencpp::tests::test_inline_executor_shutdown() { // it's ok to shut down an executor more than once executor->shutdown(); - assert_throws([executor] { - executor->enqueue(concurrencpp::task {}); - }); - - assert_throws([executor] { - concurrencpp::task array[4]; - std::span span = array; - executor->enqueue(span); - }); + assert_throws( + [executor] { + executor->enqueue(concurrencpp::task {}); + }, + throw_helper::make_worker_shutdown_exception("inline_executor", "enqueue")); + + assert_throws( + [executor] { + concurrencpp::task array[4]; + std::span span = array; + executor->enqueue(span); + }, + throw_helper::make_worker_shutdown_exception("inline_executor", "enqueue")); } void concurrencpp::tests::test_inline_executor_max_concurrency_level() { diff --git a/test/source/tests/executor_tests/manual_executor_tests.cpp b/test/source/tests/executor_tests/manual_executor_tests.cpp index 0c37adc6..58ffd0fa 100644 --- a/test/source/tests/executor_tests/manual_executor_tests.cpp +++ b/test/source/tests/executor_tests/manual_executor_tests.cpp @@ -62,6 +62,8 @@ namespace concurrencpp::tests { using namespace std::chrono; +using concurrencpp::details::throw_helper; + void concurrencpp::tests::test_manual_executor_name() { auto executor = std::make_shared(); assert_equal(executor->name, concurrencpp::details::consts::k_manual_executor_name); @@ -74,67 +76,99 @@ void concurrencpp::tests::test_manual_executor_shutdown_method_access() { executor->shutdown(); assert_true(executor->shutdown_requested()); - assert_throws([executor] { - executor->enqueue(concurrencpp::task {}); - }); - - assert_throws([executor] { - concurrencpp::task array[4]; - std::span span = array; - executor->enqueue(span); - }); - - assert_throws([executor] { - executor->clear(); - }); - - assert_throws([executor] { - executor->loop_once(); - }); - - assert_throws([executor] { - executor->loop_once_for(milliseconds(100)); - }); - - assert_throws([executor] { - executor->loop_once_until(high_resolution_clock::now() + milliseconds(100)); - }); - - assert_throws([executor] { - executor->loop(100); - }); - - assert_throws([executor] { - executor->loop_for(100, milliseconds(100)); - }); - - assert_throws([executor] { - executor->loop_until(100, high_resolution_clock::now() + milliseconds(100)); - }); - - assert_throws([executor] { - executor->wait_for_task(); - }); - - assert_throws([executor] { - executor->wait_for_task_for(milliseconds(100)); - }); - - assert_throws([executor] { - executor->wait_for_task_until(high_resolution_clock::now() + milliseconds(100)); - }); - - assert_throws([executor] { - executor->wait_for_tasks(8); - }); - - assert_throws([executor] { - executor->wait_for_tasks_for(8, milliseconds(100)); - }); - - assert_throws([executor] { - executor->wait_for_tasks_until(8, high_resolution_clock::now() + milliseconds(100)); - }); + const auto enqueue_error = throw_helper::make_worker_shutdown_exception("manual_executor", "enqueue"); + + assert_throws( + [executor] { + executor->enqueue(concurrencpp::task {}); + }, + enqueue_error); + + assert_throws( + [executor] { + concurrencpp::task array[4]; + std::span span = array; + executor->enqueue(span); + }, + enqueue_error); + + assert_throws( + [executor] { + executor->clear(); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "clear")); + + assert_throws( + [executor] { + executor->loop_once(); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "loop_once")); + + assert_throws( + [executor] { + executor->loop_once_for(milliseconds(100)); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "loop_once_for")); + + assert_throws( + [executor] { + executor->loop_once_until(high_resolution_clock::now() + milliseconds(100)); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "loop_once_until")); + + assert_throws( + [executor] { + executor->loop(100); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "loop")); + + assert_throws( + [executor] { + executor->loop_for(100, milliseconds(100)); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "loop_for")); + + assert_throws( + [executor] { + executor->loop_until(100, high_resolution_clock::now() + milliseconds(100)); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "loop_until")); + + assert_throws( + [executor] { + executor->wait_for_task(); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "wait_for_task")); + + assert_throws( + [executor] { + executor->wait_for_task_for(milliseconds(100)); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "wait_for_task_for")); + + assert_throws( + [executor] { + executor->wait_for_task_until(high_resolution_clock::now() + milliseconds(100)); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "wait_for_task_until")); + + assert_throws( + [executor] { + executor->wait_for_tasks(8); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "wait_for_tasks")); + + assert_throws( + [executor] { + executor->wait_for_tasks_for(8, milliseconds(100)); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "wait_for_tasks_for")); + + assert_throws( + [executor] { + executor->wait_for_tasks_until(8, high_resolution_clock::now() + milliseconds(100)); + }, + throw_helper::make_worker_shutdown_exception("manual_executor", "wait_for_tasks_until")); } void concurrencpp::tests::test_manual_executor_shutdown_more_than_once() { diff --git a/test/source/tests/executor_tests/thread_executor_tests.cpp b/test/source/tests/executor_tests/thread_executor_tests.cpp index 183b1529..5fc77ddc 100644 --- a/test/source/tests/executor_tests/thread_executor_tests.cpp +++ b/test/source/tests/executor_tests/thread_executor_tests.cpp @@ -50,6 +50,8 @@ namespace concurrencpp::tests { } } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + void concurrencpp::tests::test_thread_executor_name() { auto executor = std::make_shared(); executor_shutdowner shutdown(executor); @@ -64,15 +66,19 @@ void concurrencpp::tests::test_thread_executor_shutdown_method_access() { executor->shutdown(); assert_true(executor->shutdown_requested()); - assert_throws([executor] { - executor->enqueue(concurrencpp::task {}); - }); + assert_throws( + [executor] { + executor->enqueue(concurrencpp::task {}); + }, + throw_helper::make_worker_shutdown_exception("thread_executor", "enqueue")); - assert_throws([executor] { - concurrencpp::task array[4]; - std::span span = array; - executor->enqueue(span); - }); + assert_throws( + [executor] { + concurrencpp::task array[4]; + std::span span = array; + executor->enqueue(span); + }, + throw_helper::make_worker_shutdown_exception("thread_executor", "enqueue")); } void concurrencpp::tests::test_thread_executor_shutdown_join() { diff --git a/test/source/tests/executor_tests/thread_pool_executor_tests.cpp b/test/source/tests/executor_tests/thread_pool_executor_tests.cpp index 47fd136a..063076e2 100644 --- a/test/source/tests/executor_tests/thread_pool_executor_tests.cpp +++ b/test/source/tests/executor_tests/thread_pool_executor_tests.cpp @@ -31,7 +31,7 @@ namespace concurrencpp::tests { void test_thread_pool_executor_bulk_post_foreign(); void test_thread_pool_executor_bulk_post_inline(); void test_thread_pool_executor_bulk_post(); - + void test_thread_pool_executor_bulk_submit_exception(); void test_thread_pool_executor_bulk_submit_foreign(); void test_thread_pool_executor_bulk_submit_inline(); @@ -44,6 +44,7 @@ namespace concurrencpp::tests { } // namespace concurrencpp::tests using concurrencpp::details::thread; +using concurrencpp::details::throw_helper; void concurrencpp::tests::test_thread_pool_executor_name() { const auto name = "abcde12345&*("; @@ -87,15 +88,19 @@ void concurrencpp::tests::test_thread_pool_executor_shutdown_method_access() { executor->shutdown(); assert_true(executor->shutdown_requested()); - assert_throws([executor] { - executor->enqueue(concurrencpp::task {}); - }); + assert_throws( + [executor] { + executor->enqueue(concurrencpp::task {}); + }, + throw_helper::make_worker_shutdown_exception("threadpool", "enqueue")); - assert_throws([executor] { - concurrencpp::task array[4]; - std::span span = array; - executor->enqueue(span); - }); + assert_throws( + [executor] { + concurrencpp::task array[4]; + std::span span = array; + executor->enqueue(span); + }, + throw_helper::make_worker_shutdown_exception("threadpool", "enqueue")); } void concurrencpp::tests::test_thread_pool_executor_shutdown_method_more_than_once() { diff --git a/test/source/tests/executor_tests/worker_thread_executor_tests.cpp b/test/source/tests/executor_tests/worker_thread_executor_tests.cpp index 92d0819b..c63c51c8 100644 --- a/test/source/tests/executor_tests/worker_thread_executor_tests.cpp +++ b/test/source/tests/executor_tests/worker_thread_executor_tests.cpp @@ -47,6 +47,7 @@ namespace concurrencpp::tests { } // namespace concurrencpp::tests using concurrencpp::details::thread; +using concurrencpp::details::throw_helper; void concurrencpp::tests::test_worker_thread_executor_name() { auto executor = std::make_shared(); @@ -62,15 +63,19 @@ void concurrencpp::tests::test_worker_thread_executor_shutdown_method_access() { executor->shutdown(); assert_true(executor->shutdown_requested()); - assert_throws([executor] { - executor->enqueue(concurrencpp::task {}); - }); + assert_throws( + [executor] { + executor->enqueue(concurrencpp::task {}); + }, + throw_helper::make_worker_shutdown_exception("worker_thread_executor", "enqueue")); - assert_throws([executor] { - concurrencpp::task array[4]; - std::span span = array; - executor->enqueue(span); - }); + assert_throws( + [executor] { + concurrencpp::task array[4]; + std::span span = array; + executor->enqueue(span); + }, + throw_helper::make_worker_shutdown_exception("worker_thread_executor", "enqueue")); } void concurrencpp::tests::test_worker_thread_executor_shutdown_thread_join() { diff --git a/test/source/tests/result_tests/generator_tests.cpp b/test/source/tests/result_tests/generator_tests.cpp index 8bfceaf8..6a4ab07f 100644 --- a/test/source/tests/result_tests/generator_tests.cpp +++ b/test/source/tests/result_tests/generator_tests.cpp @@ -28,6 +28,8 @@ namespace concurrencpp::tests { void test_generator_iterator_comparison_operators(); } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + void concurrencpp::tests::test_generator_move_constructor() { auto gen0 = []() -> generator { co_yield 1; @@ -70,11 +72,14 @@ void concurrencpp::tests::test_generator_begin() { }(); auto gen2(std::move(gen1)); - assert_throws_with_error_message( + auto generator_empty_exception = + throw_helper::make_empty_object_exception(generator::k_class_name, "begin"); + + assert_throws( [&gen1] { gen1.begin(); }, - concurrencpp::details::consts::k_empty_generator_begin_err_msg); + generator_empty_exception); } template diff --git a/test/source/tests/result_tests/lazy_result_tests.cpp b/test/source/tests/result_tests/lazy_result_tests.cpp index a5ca2b9b..6264152b 100644 --- a/test/source/tests/result_tests/lazy_result_tests.cpp +++ b/test/source/tests/result_tests/lazy_result_tests.cpp @@ -54,6 +54,8 @@ namespace concurrencpp::tests { void test_lazy_result_assignment_operator(); } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + namespace concurrencpp::tests { template lazy_result sync_lazy_coro() { @@ -141,11 +143,17 @@ void concurrencpp::tests::test_lazy_result_destructor() { template concurrencpp::result concurrencpp::tests::test_lazy_result_status_impl() { - assert_throws_with_error_message( - [] { + // empty result throws + { + const auto test_case = [] { lazy_result().status(); - }, - concurrencpp::details::consts::k_empty_lazy_result_status_err_msg); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(lazy_result::k_class_name, "status"); + + assert_throws(test_case, expected_exception); + } // value { @@ -243,11 +251,17 @@ namespace concurrencpp::tests { template void concurrencpp::tests::test_lazy_result_resolve_impl() { - assert_throws_with_error_message( - [] { + // empty result throws + { + const auto test_case = [] { lazy_result().resolve(); - }, - concurrencpp::details::consts::k_empty_lazy_result_resolve_err_msg); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(lazy_result::k_class_name, "resolve"); + + assert_throws(test_case, expected_exception); + } auto ex = std::make_shared(); executor_shutdowner es(ex); @@ -344,11 +358,17 @@ namespace concurrencpp::tests { template void concurrencpp::tests::test_lazy_result_co_await_operator_impl() { - assert_throws_with_error_message( - [] { + // empty result throws + { + const auto test_case = [] { lazy_result().operator co_await(); - }, - concurrencpp::details::consts::k_empty_lazy_result_operator_co_await_err_msg); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(lazy_result::k_class_name, "operator co_await"); + + assert_throws(test_case, expected_exception); + } runtime runtime; test_lazy_result_co_await_non_ready_coro_val(runtime.thread_executor()).get(); @@ -367,11 +387,17 @@ void concurrencpp::tests::test_lazy_result_co_await_operator() { template void concurrencpp::tests::test_lazy_result_run_impl(std::shared_ptr ex) { - assert_throws_with_error_message( - [] { + // empty result throws + { + const auto test_case = [] { lazy_result().run(); - }, - concurrencpp::details::consts::k_empty_lazy_result_run_err_msg); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(lazy_result::k_class_name, "run"); + + assert_throws(test_case, expected_exception); + } auto started = false; auto lazy = async_lazy_coro_val(started, ex); diff --git a/test/source/tests/result_tests/make_result_tests.cpp b/test/source/tests/result_tests/make_result_tests.cpp index 862599ea..62007090 100644 --- a/test/source/tests/result_tests/make_result_tests.cpp +++ b/test/source/tests/result_tests/make_result_tests.cpp @@ -24,6 +24,8 @@ namespace concurrencpp::tests { } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + template void concurrencpp::tests::test_make_ready_result_impl() { result result; @@ -47,12 +49,14 @@ void concurrencpp::tests::test_make_ready_result() { template void concurrencpp::tests::test_make_exceptional_result_impl() { + const auto expected_error = throw_helper::make_empty_argument_exception("", "make_exceptional_result", "exception_ptr"); + // empty exception_ptr makes make_exceptional_result throw. - assert_throws_with_error_message( + assert_throws( [] { make_exceptional_result({}); }, - concurrencpp::details::consts::k_make_exceptional_result_exception_null_error_msg); + expected_error); const size_t id = 123456789; auto res0 = make_exceptional_result(custom_exception(id)); @@ -94,11 +98,13 @@ void concurrencpp::tests::test_make_ready_lazy_result() { template void concurrencpp::tests::test_make_exceptional_lazy_result_impl() { // empty exception_ptr makes make_exceptional_result throw. - assert_throws_with_error_message( + const auto expected_error = throw_helper::make_empty_argument_exception("", "make_exceptional_lazy_result", "exception_ptr"); + + assert_throws( [] { make_exceptional_lazy_result({}); }, - concurrencpp::details::consts::k_make_exceptional_lazy_result_exception_null_error_msg); + expected_error); const size_t id = 123456789; auto res0 = make_exceptional_lazy_result(custom_exception(id)).run(); diff --git a/test/source/tests/result_tests/result_promise_tests.cpp b/test/source/tests/result_tests/result_promise_tests.cpp index 58a12108..15379833 100644 --- a/test/source/tests/result_tests/result_promise_tests.cpp +++ b/test/source/tests/result_tests/result_promise_tests.cpp @@ -20,8 +20,8 @@ namespace concurrencpp::tests { void test_result_promise_get_result_impl(); void test_result_promise_get_result(); - template - void test_result_promise_set_value_impl(const arguments_tuple_type& tuple_args); + template + void test_result_promise_set_value_impl(); void test_result_promise_set_value_thrown_exception(); void test_result_promise_set_value(); @@ -31,8 +31,6 @@ namespace concurrencpp::tests { template void test_result_promise_set_from_function_value_impl(); - template - void test_result_promise_set_from_function_exception_impl(); void test_result_promise_set_from_function(); template @@ -49,11 +47,21 @@ namespace concurrencpp::tests { template void test_rp_assignment_operator_impl(); void test_rp_assignment_operator(); + + template + void set_result_promise(result_promise& rp) { + if constexpr (std::is_same_v) { + rp.set_result(); + } else { + rp.set_result(value_gen::default_value()); + } + } } // namespace concurrencpp::tests -using concurrencpp::result_promise; using concurrencpp::result; using concurrencpp::result_status; +using concurrencpp::result_promise; +using concurrencpp::details::throw_helper; template void concurrencpp::tests::test_result_promise_constructor_impl() { @@ -75,7 +83,7 @@ void concurrencpp::tests::test_result_promise_constructor() { template void concurrencpp::tests::test_result_promise_destructor_impl() { - concurrencpp::result result; + result result; { result_promise rp; @@ -84,9 +92,11 @@ void concurrencpp::tests::test_result_promise_destructor_impl() { assert_true(static_cast(result)); assert_equal(result.status(), result_status::exception); - assert_throws([&] { - result.get(); - }); + assert_throws_with_error_message( + [&] { + result.get(); + }, + concurrencpp::details::consts::k_broken_task_exception_error_msg); } void concurrencpp::tests::test_result_promise_RAII_impl() { @@ -120,23 +130,32 @@ void concurrencpp::tests::test_result_promise_destructor() { template void concurrencpp::tests::test_result_promise_get_result_impl() { // empty result_promise should throw - assert_throws([] { - ::concurrencpp::result_promise rp; - auto dummy = std::move(rp); - rp.get_result(); - }); + { + const auto test_case = [] { + result_promise rp; + auto dummy = std::move(rp); + rp.get_result(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception("result_promise", "get_result"); + + assert_throws(test_case, expected_exception); + } // valid result_promise returns a valid result - ::concurrencpp::result_promise rp; + result_promise rp; auto result = rp.get_result(); assert_true(static_cast(result)); assert_equal(result.status(), concurrencpp::result_status::idle); // trying to get the future more than once throws - assert_throws([&] { - rp.get_result(); - }); + assert_throws_with_error_message( + [&] { + rp.get_result(); + }, + concurrencpp::details::consts::k_result_promise_get_result_already_retrieved_error_msg); } void concurrencpp::tests::test_result_promise_get_result() { @@ -147,48 +166,26 @@ void concurrencpp::tests::test_result_promise_get_result() { test_result_promise_get_result_impl(); } -template -void concurrencpp::tests::test_result_promise_set_value_impl(const arguments_tuple_type& tuple_args) { - // if an exception is thrown inside set_value (by building an object in place) the associated result object is un-ready - { - struct throws_on_construction { - throws_on_construction(int, int) { - throw std::exception(); - } - }; - - concurrencpp::result_promise rp; - auto result = rp.get_result(); - - assert_throws([&rp]() mutable { - rp.set_result(0, 0); - }); - - assert_true(static_cast(result)); - assert_true(static_cast(rp)); - assert_equal(result.status(), result_status::idle); - } - - auto set_rp = [](auto& rp, auto tuple) { - auto setter = [rp = std::move(rp)](auto&&... args) mutable { - rp.set_result(args...); - }; - - std::apply(setter, tuple); - }; - +template +void concurrencpp::tests::test_result_promise_set_value_impl() { // setting result to an empty rp throws { - concurrencpp::result_promise rp; + result_promise rp; auto dummy = std::move(rp); - assert_throws([&rp, set_rp, tuple_args]() mutable { - set_rp(rp, tuple_args); - }); + + const auto expected_exception = + throw_helper::make_empty_object_exception(result_promise::k_class_name, "set_result"); + + assert_throws( + [&rp] { + set_result_promise(rp); + }, + expected_exception); } - // setting a result marshals it to the associated result object and empties the result_promise + // setting a (valid) result passes it to the associated result object and empties the result_promise { - concurrencpp::result_promise rp; + result_promise rp; auto result = rp.get_result(); std::thread thread([result = std::move(result)]() mutable { @@ -196,7 +193,7 @@ void concurrencpp::tests::test_result_promise_set_value_impl(const arguments_tup test_ready_result(std::move(result)); }); - set_rp(rp, tuple_args); + set_result_promise(rp); assert_false(static_cast(rp)); thread.join(); @@ -204,52 +201,30 @@ void concurrencpp::tests::test_result_promise_set_value_impl(const arguments_tup } void concurrencpp::tests::test_result_promise_set_value_thrown_exception() { - - struct throws_on_construction { - throws_on_construction() { - static bool s_thrown = false; - - if (!s_thrown) { - s_thrown = true; - throw std::runtime_error(""); - } - } - }; - - result_promise rp; + // this test checks that even after an exception was thrown while constructing a value, the rp is idle and can be set again + result_promise rp; auto result = rp.get_result(); - assert_throws([&] { - rp.set_result(); + assert_throws([&] { + std::string str; + rp.set_result(str.max_size() + 10, 'a'); }); assert_equal(result.status(), result_status::idle); assert_true(static_cast(rp)); - rp.set_result(); // should not throw. + const char* str = "hello world"; + rp.set_result(str); // should not throw. assert_equal(result.status(), result_status::value); + assert_equal(result.get(), str); } void concurrencpp::tests::test_result_promise_set_value() { - const auto expected_int_result = value_gen::default_value(); - auto int_tuple = std::make_tuple(expected_int_result); - test_result_promise_set_value_impl(int_tuple); - - const auto expected_str_result = value_gen::default_value(); - const auto modified_str_result = std::string(" ") + expected_str_result + " "; - auto str_tuple = std::make_tuple(modified_str_result, 2, expected_str_result.size()); - test_result_promise_set_value_impl(str_tuple); - - std::tuple<> empty_tuple; - test_result_promise_set_value_impl(empty_tuple); - - auto& expected_int_ref_result = value_gen::default_value(); - std::tuple int_ref_tuple = {expected_int_ref_result}; - test_result_promise_set_value_impl(int_ref_tuple); - - auto& expected_str_ref_result = value_gen::default_value(); - std::tuple str_ref_tuple = {expected_str_ref_result}; - test_result_promise_set_value_impl(str_ref_tuple); + test_result_promise_set_value_impl(); + test_result_promise_set_value_impl(); + test_result_promise_set_value_impl(); + test_result_promise_set_value_impl(); + test_result_promise_set_value_impl(); test_result_promise_set_value_thrown_exception(); } @@ -260,18 +235,29 @@ void concurrencpp::tests::test_result_promise_set_exception_impl() { { result_promise rp; auto dummy = std::move(rp); - assert_throws([&rp] { - const auto exception = std::make_exception_ptr(std::exception()); - rp.set_exception(exception); - }); + + const auto expected_exception = + throw_helper::make_empty_object_exception(result_promise::k_class_name, + "set_exception"); + + assert_throws( + [&rp] { + rp.set_exception(std::make_exception_ptr(std::exception())); + }, + expected_exception); } // null exception_ptr throws std::invalid_argument { - assert_throws([] { - result_promise rp; - rp.set_exception(std::exception_ptr {}); - }); + const auto expected_exception = + throw_helper::make_empty_argument_exception(result_promise::k_class_name, "set_exception", "exception_ptr"); + + assert_throws( + [] { + result_promise rp; + rp.set_exception(std::exception_ptr {}); + }, + expected_exception); } // basic test: @@ -302,27 +288,45 @@ void concurrencpp::tests::test_result_promise_set_exception() { template void concurrencpp::tests::test_result_promise_set_from_function_value_impl() { - result_promise rp; - auto result = rp.get_result(); - rp.set_from_function(value_gen::default_value); - test_ready_result(std::move(result)); -} + // empty rp throws + { + result_promise rp; + auto dummy = std::move(rp); -template -void concurrencpp::tests::test_result_promise_set_from_function_exception_impl() { - result_promise rp; - auto result = rp.get_result(); - const auto id = 123456789; + const auto expected_exception = + throw_helper::make_empty_object_exception(result_promise::k_class_name, + "set_from_function"); + assert_throws( + [&rp] { + rp.set_from_function(value_gen::default_value); + }, + expected_exception); + } - rp.set_from_function([id]() -> decltype(auto) { - throw custom_exception(id); - return value_gen::default_value(); - }); + // setting a valid value + { + result_promise rp; + auto result = rp.get_result(); + rp.set_from_function(value_gen::default_value); + test_ready_result(std::move(result)); + } - assert_false(static_cast(rp)); - assert_true(static_cast(result)); - assert_equal(result.status(), result_status::exception); - test_ready_result_custom_exception(std::move(result), id); + // function throws an exception + { + result_promise rp; + auto result = rp.get_result(); + const auto id = 123456789; + + rp.set_from_function([id]() -> decltype(auto) { + throw custom_exception(id); + return value_gen::default_value(); + }); + + assert_false(static_cast(rp)); + assert_true(static_cast(result)); + assert_equal(result.status(), result_status::exception); + test_ready_result_custom_exception(std::move(result), id); + } } void concurrencpp::tests::test_result_promise_set_from_function() { @@ -331,12 +335,6 @@ void concurrencpp::tests::test_result_promise_set_from_function() { test_result_promise_set_from_function_value_impl(); test_result_promise_set_from_function_value_impl(); test_result_promise_set_from_function_value_impl(); - - test_result_promise_set_from_function_exception_impl(); - test_result_promise_set_from_function_exception_impl(); - test_result_promise_set_from_function_exception_impl(); - test_result_promise_set_from_function_exception_impl(); - test_result_promise_set_from_function_exception_impl(); } template diff --git a/test/source/tests/result_tests/result_resolve_await_tests.cpp b/test/source/tests/result_tests/result_resolve_await_tests.cpp index 4d0795ef..7e885867 100644 --- a/test/source/tests/result_tests/result_resolve_await_tests.cpp +++ b/test/source/tests/result_tests/result_resolve_await_tests.cpp @@ -7,6 +7,8 @@ #include "utils/test_ready_result.h" #include "utils/executor_shutdowner.h" +using concurrencpp::details::throw_helper; + namespace concurrencpp::tests { template result test_result_resolve_impl_result_ready_value(); @@ -128,11 +130,14 @@ template void concurrencpp::tests::test_result_resolve_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - result().resolve(); - }, - concurrencpp::details::consts::k_result_resolve_error_msg); + const auto test_case = [] { + result().resolve(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(result::k_class_name, "resolve"); + + assert_throws(test_case, expected_exception); } auto thread_executor = std::make_shared(); @@ -226,11 +231,15 @@ template void concurrencpp::tests::test_result_await_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - result().operator co_await(); - }, - concurrencpp::details::consts::k_result_operator_co_await_error_msg); + const auto test_case = [] { + result().operator co_await(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(result::k_class_name, + "operator co_await"); + + assert_throws(test_case, expected_exception); } auto thread_executor = std::make_shared(); diff --git a/test/source/tests/result_tests/result_tests.cpp b/test/source/tests/result_tests/result_tests.cpp index 497003e8..a04b6602 100644 --- a/test/source/tests/result_tests/result_tests.cpp +++ b/test/source/tests/result_tests/result_tests.cpp @@ -48,6 +48,8 @@ namespace concurrencpp::tests { using concurrencpp::result; using concurrencpp::result_promise; +using concurrencpp::details::throw_helper; + using namespace std::chrono; using namespace concurrencpp::tests; @@ -79,11 +81,14 @@ template void concurrencpp::tests::test_result_status_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - result().status(); - }, - concurrencpp::details::consts::k_result_status_error_msg); + const auto test_case = [] { + result().status(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(result::k_class_name, "status"); + + assert_throws(test_case, expected_exception); } // idle result @@ -140,11 +145,14 @@ template void concurrencpp::tests::test_result_get_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - result().get(); - }, - concurrencpp::details::consts::k_result_get_error_msg); + const auto test_case = [] { + result().get(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(result::k_class_name, "get"); + + assert_throws(test_case, expected_exception); } // get blocks until value is present and empties the result @@ -238,11 +246,15 @@ template void concurrencpp::tests::test_result_wait_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - result().wait(); - }, - concurrencpp::details::consts::k_result_wait_error_msg); + + const auto test_case = [] { + result().wait(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(result::k_class_name, "wait"); + + assert_throws(test_case, expected_exception); } // wait blocks until value is present @@ -352,11 +364,14 @@ template void concurrencpp::tests::test_result_wait_for_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - result().wait_for(seconds(1)); - }, - concurrencpp::details::consts::k_result_wait_for_error_msg); + const auto test_case = [] { + result().wait_for(seconds(1)); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(result::k_class_name, "wait_for"); + + assert_throws(test_case, expected_exception); } // if the result is ready by value, don't block and return status::value @@ -482,12 +497,16 @@ template void concurrencpp::tests::test_result_wait_until_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - const auto later = high_resolution_clock::now() + seconds(10); - result().wait_until(later); - }, - concurrencpp::details::consts::k_result_wait_until_error_msg); + + const auto test_case = [] { + const auto later = high_resolution_clock::now() + seconds(10); + result().wait_until(later); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(result::k_class_name, "wait_until"); + + assert_throws(test_case, expected_exception); } // if time_point <= now, the function is equivalent to result::status diff --git a/test/source/tests/result_tests/resume_on_tests.cpp b/test/source/tests/result_tests/resume_on_tests.cpp index 8e7732a3..7dbcbed0 100644 --- a/test/source/tests/result_tests/resume_on_tests.cpp +++ b/test/source/tests/result_tests/resume_on_tests.cpp @@ -36,12 +36,15 @@ namespace concurrencpp::tests { } } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + void concurrencpp::tests::test_resume_on_null_executor() { - assert_throws_with_error_message( + const auto expected_error = throw_helper::make_empty_argument_exception("", "resume_on", "executor"); + assert_throws( [] { resume_on_1_executor({}).get(); }, - concurrencpp::details::consts::k_resume_on_null_exception_err_msg); + expected_error); } void concurrencpp::tests::test_resume_on_shutdown_executor() { diff --git a/test/source/tests/result_tests/shared_result_await_tests.cpp b/test/source/tests/result_tests/shared_result_await_tests.cpp index 3b06cd21..c56e92e9 100644 --- a/test/source/tests/result_tests/shared_result_await_tests.cpp +++ b/test/source/tests/result_tests/shared_result_await_tests.cpp @@ -16,6 +16,7 @@ namespace concurrencpp::tests { using concurrencpp::result; using concurrencpp::details::thread; +using concurrencpp::details::throw_helper; /* * In this test suit, we need to check all the possible scenarios that "await" can have. @@ -190,11 +191,15 @@ template void concurrencpp::tests::test_shared_result_await_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - shared_result().operator co_await(); - }, - concurrencpp::details::consts::k_shared_result_operator_co_await_error_msg); + const auto test_case = [] { + shared_result().operator co_await(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(shared_result::k_class_name, + "operator co_await"); + + assert_throws(test_case, expected_exception); } // await can be called multiple times diff --git a/test/source/tests/result_tests/shared_result_resolve_tests.cpp b/test/source/tests/result_tests/shared_result_resolve_tests.cpp index 7791c795..600e5b7c 100644 --- a/test/source/tests/result_tests/shared_result_resolve_tests.cpp +++ b/test/source/tests/result_tests/shared_result_resolve_tests.cpp @@ -16,6 +16,7 @@ namespace concurrencpp::tests { using concurrencpp::result; using concurrencpp::details::thread; +using concurrencpp::details::throw_helper; /* * In this test suit, we need to check all the possible scenarios that result::resolve can have. @@ -161,11 +162,15 @@ template void concurrencpp::tests::test_shared_result_resolve_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - shared_result().resolve(); - }, - concurrencpp::details::consts::k_shared_result_resolve_error_msg); + const auto test_case = [] { + shared_result().resolve(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(shared_result::k_class_name, + "resolve"); + + assert_throws(test_case, expected_exception); } // resolve can be called multiple times diff --git a/test/source/tests/result_tests/shared_result_tests.cpp b/test/source/tests/result_tests/shared_result_tests.cpp index 3c0a317f..0d28a39d 100644 --- a/test/source/tests/result_tests/shared_result_tests.cpp +++ b/test/source/tests/result_tests/shared_result_tests.cpp @@ -60,6 +60,8 @@ namespace concurrencpp::tests { using concurrencpp::result; using concurrencpp::result_promise; +using concurrencpp::details::throw_helper; + using namespace std::chrono; using namespace concurrencpp::tests; @@ -100,11 +102,14 @@ template void concurrencpp::tests::test_shared_result_status_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - shared_result().status(); - }, - concurrencpp::details::consts::k_shared_result_status_error_msg); + const auto test_case = [] { + shared_result().status(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(shared_result::k_class_name, "status"); + + assert_throws(test_case, expected_exception); } // idle result @@ -158,11 +163,15 @@ template void concurrencpp::tests::test_shared_result_get_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - shared_result().get(); - }, - concurrencpp::details::consts::k_shared_result_get_error_msg); + + const auto test_case = [] { + shared_result().get(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(shared_result::k_class_name, "get"); + + assert_throws(test_case, expected_exception); } // get blocks until value is present @@ -294,11 +303,14 @@ template void concurrencpp::tests::test_shared_result_wait_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - shared_result().wait(); - }, - concurrencpp::details::consts::k_shared_result_wait_error_msg); + const auto test_case = [] { + shared_result().wait(); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(shared_result::k_class_name, "wait"); + + assert_throws(test_case, expected_exception); } // wait blocks until value is present @@ -415,11 +427,15 @@ template void concurrencpp::tests::test_shared_result_wait_for_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - shared_result().wait_for(seconds(1)); - }, - concurrencpp::details::consts::k_shared_result_wait_for_error_msg); + const auto test_case = [] { + shared_result().wait_for(seconds(1)); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(shared_result::k_class_name, + "wait_for"); + + assert_throws(test_case, expected_exception); } // if the result is ready by value, don't block and return status::value @@ -553,12 +569,17 @@ template void concurrencpp::tests::test_shared_result_wait_until_impl() { // empty result throws { - assert_throws_with_error_message( - [] { - const auto later = high_resolution_clock::now() + seconds(10); - shared_result().wait_until(later); - }, - concurrencpp::details::consts::k_shared_result_wait_until_error_msg); + + const auto test_case = [] { + const auto later = high_resolution_clock::now() + seconds(10); + shared_result().wait_until(later); + }; + + const auto expected_exception = + throw_helper::make_empty_object_exception(shared_result::k_class_name, + "wait_until"); + + assert_throws(test_case, expected_exception); } // if time_point <= now, the function is equivalent to result::status diff --git a/test/source/tests/result_tests/when_all_tests.cpp b/test/source/tests/result_tests/when_all_tests.cpp index 2ae41c74..60ea82ba 100644 --- a/test/source/tests/result_tests/when_all_tests.cpp +++ b/test/source/tests/result_tests/when_all_tests.cpp @@ -44,6 +44,8 @@ namespace concurrencpp::tests { void test_when_all_tuple(); } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + template void concurrencpp::tests::test_when_all_vector_empty_result(std::shared_ptr resume_executor) { constexpr size_t task_count = 63; @@ -57,11 +59,13 @@ void concurrencpp::tests::test_when_all_vector_empty_result(std::shared_ptr( + const auto expected_error = throw_helper::make_empty_argument_exception("", "when_all", "result"); + + assert_throws( [&results, resume_executor] { concurrencpp::when_all(resume_executor, results.begin(), results.end()); }, - concurrencpp::details::consts::k_when_all_empty_result_error_msg); + expected_error); const auto all_valid = std::all_of(results.begin(), results.begin() + task_count, [](const auto& result) { return static_cast(result); @@ -91,12 +95,14 @@ void concurrencpp::tests::test_when_all_vector_null_resume_executor() { results.emplace_back(rp.get_result()); } + const auto expected_error = throw_helper::make_empty_argument_exception("", "when_all", "resume_executor"); + // with results - assert_throws_with_error_message( + assert_throws( [&] { when_all(std::shared_ptr {}, results.begin(), results.end()); }, - concurrencpp::details::consts::k_when_all_null_resume_executor_error_msg); + expected_error); } template @@ -227,7 +233,9 @@ void concurrencpp::tests::test_when_all_tuple_empty_result(std::shared_ptr s_ref_res; - assert_throws_with_error_message( + const auto expected_error = throw_helper::make_empty_argument_exception("", "when_all", "result"); + + assert_throws( [&] { when_all(resume_executor, std::move(int_res), @@ -236,7 +244,7 @@ void concurrencpp::tests::test_when_all_tuple_empty_result(std::shared_ptr(int_res)); assert_true(static_cast(str_res)); @@ -254,19 +262,21 @@ void concurrencpp::tests::test_when_all_tuple_null_resume_executor() { result_promise rp_void; auto void_res = rp_void.get_result(); + const auto expected_error = throw_helper::make_empty_argument_exception("", "when_all", "resume_executor"); + // with results - assert_throws_with_error_message( + assert_throws( [&] { when_all(std::shared_ptr {}, std::move(int_res), std::move(str_res), std::move(void_res)); }, - concurrencpp::details::consts::k_when_all_null_resume_executor_error_msg); + expected_error); // no results - assert_throws_with_error_message( + assert_throws( [] { when_all(std::shared_ptr {}); }, - concurrencpp::details::consts::k_when_all_null_resume_executor_error_msg); + expected_error); } void concurrencpp::tests::test_when_all_tuple_empty_range(std::shared_ptr resume_executor) { diff --git a/test/source/tests/result_tests/when_any_tests.cpp b/test/source/tests/result_tests/when_any_tests.cpp index 5e63c23d..a893c0ef 100644 --- a/test/source/tests/result_tests/when_any_tests.cpp +++ b/test/source/tests/result_tests/when_any_tests.cpp @@ -42,6 +42,8 @@ namespace concurrencpp::tests { void test_when_any_tuple(); } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + template void concurrencpp::tests::test_when_any_vector_empty_result(std::shared_ptr resume_executor) { constexpr size_t task_count = 63; @@ -55,11 +57,13 @@ void concurrencpp::tests::test_when_any_vector_empty_result(std::shared_ptr( + const auto expected_error = throw_helper::make_empty_argument_exception("", "when_any", "result"); + + assert_throws( [&] { concurrencpp::when_any(resume_executor, results.begin(), results.end()); }, - concurrencpp::details::consts::k_when_any_empty_result_error_msg); + expected_error); const auto all_valid = std::all_of(results.begin(), results.begin() + task_count, [](const auto& result) { return static_cast(result); @@ -89,12 +93,14 @@ void concurrencpp::tests::test_when_any_vector_null_resume_executor() { results.emplace_back(rp.get_result()); } + const auto expected_error = throw_helper::make_empty_argument_exception("", "when_any", "resume_executor"); + // with results - assert_throws_with_error_message( + assert_throws( [&] { when_any(std::shared_ptr {}, results.begin(), results.end()); }, - concurrencpp::details::consts::k_when_any_null_resume_executor_error_msg); + expected_error); } template @@ -227,7 +233,9 @@ void concurrencpp::tests::test_when_any_tuple_empty_result(std::shared_ptr str_ref_res; - assert_throws_with_error_message( + const auto expected_error = throw_helper::make_empty_argument_exception("", "when_any", "result"); + + assert_throws( [&] { when_any(resume_executor, std::move(int_res), @@ -236,7 +244,7 @@ void concurrencpp::tests::test_when_any_tuple_empty_result(std::shared_ptr(int_res)); @@ -255,12 +263,14 @@ void concurrencpp::tests::test_when_any_tuple_null_resume_executor() { result_promise rp_void; auto void_res = rp_void.get_result(); + const auto expected_error = throw_helper::make_empty_argument_exception("", "when_any", "resume_executor"); + // with results - assert_throws_with_error_message( + assert_throws( [&] { when_any(std::shared_ptr {}, std::move(int_res), std::move(str_res), std::move(void_res)); }, - concurrencpp::details::consts::k_when_any_null_resume_executor_error_msg); + expected_error); } concurrencpp::result concurrencpp::tests::test_when_any_tuple_impl(std::shared_ptr resume_executor, diff --git a/test/source/tests/thread_tests/async_condition_variable_tests.cpp b/test/source/tests/thread_tests/async_condition_variable_tests.cpp index 2f5b4b83..e2392863 100644 --- a/test/source/tests/thread_tests/async_condition_variable_tests.cpp +++ b/test/source/tests/thread_tests/async_condition_variable_tests.cpp @@ -8,6 +8,8 @@ using namespace concurrencpp; +using concurrencpp::details::throw_helper; + namespace concurrencpp::tests { void test_async_condition_variable_await_null_resume_executor(); void test_async_condition_variable_await_unlocked_scoped_async_lock(); @@ -31,11 +33,14 @@ void tests::test_async_condition_variable_await_null_resume_executor() { auto scoped_lock = lock.lock(executor).run().get(); - assert_throws_with_error_message( + const auto expected_error = + throw_helper::make_empty_argument_exception(async_condition_variable::k_class_name, "await", "resume_executor"); + + assert_throws( [&] { cv.await({}, scoped_lock).run().get(); }, - concurrencpp::details::consts::k_async_condition_variable_await_invalid_resume_executor_err_msg); + expected_error); } void tests::test_async_condition_variable_await_unlocked_scoped_async_lock() { @@ -85,13 +90,16 @@ void tests::test_async_condition_variable_await_pred_null_resume_executor() { auto scoped_lock = lock.lock(executor).run().get(); - assert_throws_with_error_message( + const auto expected_error = + throw_helper::make_empty_argument_exception(async_condition_variable::k_class_name, "await", "resume_executor"); + + assert_throws( [&] { cv.await({}, scoped_lock, [] { return true; }); }, - concurrencpp::details::consts::k_async_condition_variable_await_invalid_resume_executor_err_msg); + expected_error); } void tests::test_async_condition_variable_await_pred_unlocked_scoped_async_lock() { diff --git a/test/source/tests/thread_tests/async_lock_tests.cpp b/test/source/tests/thread_tests/async_lock_tests.cpp index 5cfb1a88..eab65fa1 100644 --- a/test/source/tests/thread_tests/async_lock_tests.cpp +++ b/test/source/tests/thread_tests/async_lock_tests.cpp @@ -49,13 +49,17 @@ namespace concurrencpp::tests { } } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + void concurrencpp::tests::test_async_lock_lock_null_resume_executor() { - assert_throws_with_error_message( + const auto expected_error = throw_helper::make_empty_argument_exception(async_lock::k_class_name, "lock", "resume_executor"); + + assert_throws( [] { async_lock lock; lock.lock(std::shared_ptr {}); }, - concurrencpp::details::consts::k_async_lock_null_resume_executor_err_msg); + expected_error); } void concurrencpp::tests::test_async_lock_lock_resumption() { diff --git a/test/source/tests/thread_tests/scoped_async_lock_tests.cpp b/test/source/tests/thread_tests/scoped_async_lock_tests.cpp index 0b00269e..38a4e15e 100644 --- a/test/source/tests/thread_tests/scoped_async_lock_tests.cpp +++ b/test/source/tests/thread_tests/scoped_async_lock_tests.cpp @@ -20,6 +20,8 @@ namespace concurrencpp::tests { } // namespace concurrencpp::tests +using concurrencpp::details::throw_helper; + concurrencpp::result concurrencpp::tests::test_scoped_async_lock_constructor_impl(std::shared_ptr executor) { { // default constructor scoped_async_lock sal; @@ -87,13 +89,16 @@ void concurrencpp::tests::test_scoped_async_lock_lock() { executor_shutdowner es(executor); { // null resume_executor - assert_throws_contains_error_message( + const auto expected_error = + throw_helper::make_empty_argument_exception(scoped_async_lock::k_class_name, "lock", "resume_executor"); + + assert_throws( [] { async_lock lock; scoped_async_lock sal(lock, std::defer_lock); sal.lock(std::shared_ptr()).run().get(); }, - concurrencpp::details::consts::k_scoped_async_lock_null_resume_executor_err_msg); + expected_error); } { // empty scoped_async_lock diff --git a/test/source/tests/timer_tests/timer_queue_tests.cpp b/test/source/tests/timer_tests/timer_queue_tests.cpp index 9a4768a5..988f511c 100644 --- a/test/source/tests/timer_tests/timer_queue_tests.cpp +++ b/test/source/tests/timer_tests/timer_queue_tests.cpp @@ -7,6 +7,8 @@ #include +using concurrencpp::details::throw_helper; + using namespace std::chrono_literals; namespace concurrencpp::tests { @@ -22,57 +24,70 @@ void concurrencpp::tests::test_timer_queue_make_timer() { auto timer_queue = std::make_shared(120s); assert_false(timer_queue->shutdown_requested()); - assert_throws_with_error_message( + const auto null_executor_exception = + throw_helper::make_empty_argument_exception(timer_queue::k_class_name, "make_timer", "executor"); + + assert_throws( [timer_queue] { timer_queue->make_timer(100ms, 100ms, {}, [] { }); }, - concurrencpp::details::consts::k_timer_queue_make_timer_executor_null_err_msg); + null_executor_exception); timer_queue->shutdown(); assert_true(timer_queue->shutdown_requested()); - assert_throws_with_error_message( + const auto shutdown_exception = throw_helper::make_worker_shutdown_exception(timer_queue::k_class_name, "make_timer"); + + assert_throws( [timer_queue] { auto inline_executor = std::make_shared(); timer_queue->make_timer(100ms, 100ms, inline_executor, [] { }); }, - concurrencpp::details::consts::k_timer_queue_shutdown_err_msg); + shutdown_exception); } void concurrencpp::tests::test_timer_queue_make_oneshot_timer() { auto timer_queue = std::make_shared(120s); assert_false(timer_queue->shutdown_requested()); - assert_throws_with_error_message( + const auto null_executor_exception = + throw_helper::make_empty_argument_exception(timer_queue::k_class_name, "make_one_shot_timer", "executor"); + + assert_throws( [timer_queue] { timer_queue->make_one_shot_timer(100ms, {}, [] { }); }, - concurrencpp::details::consts::k_timer_queue_make_oneshot_timer_executor_null_err_msg); + null_executor_exception); timer_queue->shutdown(); assert_true(timer_queue->shutdown_requested()); - assert_throws_with_error_message( + const auto shutdown_exception = throw_helper::make_worker_shutdown_exception(timer_queue::k_class_name, "make_timer"); + + assert_throws( [timer_queue] { auto inline_executor = std::make_shared(); timer_queue->make_one_shot_timer(100ms, inline_executor, [] { }); }, - concurrencpp::details::consts::k_timer_queue_shutdown_err_msg); + shutdown_exception); } void concurrencpp::tests::test_timer_queue_make_delay_object() { auto timer_queue = std::make_shared(120s); assert_false(timer_queue->shutdown_requested()); - assert_throws_with_error_message( + const auto null_executor_exception = + throw_helper::make_empty_argument_exception(timer_queue::k_class_name, "make_delay_object", "executor"); + + assert_throws( [timer_queue] { timer_queue->make_delay_object(100ms, {}); }, - concurrencpp::details::consts::k_timer_queue_make_delay_object_executor_null_err_msg); + null_executor_exception); timer_queue->shutdown(); assert_true(timer_queue->shutdown_requested()); diff --git a/test/source/tests/timer_tests/timer_tests.cpp b/test/source/tests/timer_tests/timer_tests.cpp index 04d82b3a..c1eb5d8b 100644 --- a/test/source/tests/timer_tests/timer_tests.cpp +++ b/test/source/tests/timer_tests/timer_tests.cpp @@ -46,10 +46,15 @@ namespace concurrencpp::tests { void test_timer_assignment_operator_non_empty_to_empty(); void test_timer_assignment_operator_assign_to_self(); void test_timer_assignment_operator(); + + void test_timer_getters(); } // namespace concurrencpp::tests using namespace std::chrono; + using concurrencpp::timer; +using concurrencpp::details::throw_helper; + using time_point = std::chrono::time_point; namespace concurrencpp::tests { @@ -532,10 +537,15 @@ void concurrencpp::tests::test_timer_set_frequency_after_due_time() { void concurrencpp::tests::test_timer_set_frequency() { // empty timer throws - assert_throws([] { - timer timer; - timer.set_frequency(200ms); - }); + const auto expected_exception = + throw_helper::make_empty_object_exception(timer::k_class_name, "set_frequency"); + + assert_throws( + [] { + timer timer; + timer.set_frequency(200ms); + }, + expected_exception); test_timer_set_frequency_before_due_time(); test_timer_set_frequency_after_due_time(); @@ -658,6 +668,46 @@ void concurrencpp::tests::test_timer_assignment_operator() { test_timer_assignment_operator_assign_to_self(); } +void concurrencpp::tests::test_timer_getters() { + timer t; + + const auto get_due_time_exception = + throw_helper::make_empty_object_exception(timer::k_class_name, "get_due_time"); + + assert_throws( + [&t] { + t.get_due_time(); + }, + get_due_time_exception); + + const auto get_executor_exception = + throw_helper::make_empty_object_exception(timer::k_class_name, "get_executor"); + + assert_throws( + [&t] { + t.get_executor(); + }, + get_executor_exception); + + const auto get_frequency_exception = + throw_helper::make_empty_object_exception(timer::k_class_name, "get_frequency"); + + assert_throws( + [&t] { + t.get_frequency(); + }, + get_frequency_exception); + + const auto get_timer_queue_exception = + throw_helper::make_empty_object_exception(timer::k_class_name, "get_timer_queue"); + + assert_throws( + [&t] { + t.get_timer_queue(); + }, + get_timer_queue_exception); +} + using namespace concurrencpp::tests; int main() { @@ -671,6 +721,7 @@ int main() { test.add_step("oneshot_timer", test_timer_oneshot_timer); test.add_step("delay_object", test_timer_delay_object); test.add_step("operator =", test_timer_assignment_operator); + test.add_step("getters", test_timer_getters); test.launch_test(); return 0;