From ff0e4a57cccb2de28a8251bcf986373dea9ee38f Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Tue, 15 Aug 2023 10:37:10 -0700 Subject: [PATCH 01/17] Fix formatting. --- stlab/utility.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stlab/utility.hpp b/stlab/utility.hpp index 4dd23304..e25703a5 100644 --- a/stlab/utility.hpp +++ b/stlab/utility.hpp @@ -118,10 +118,10 @@ void for_each_argument(F&& f, Args&&... args) { /// Returns a copy of the argument. Used to pass an lvalue to function taking an rvalue or to /// copy a type with an `explicit` copy-constructor. template -constexpr std::decay_t copy(T&& value) noexcept( - noexcept(std::decay_t{static_cast(value)})) { - static_assert(!std::is_same, T>::value, "explicit copy of rvalue."); - return std::decay_t{static_cast(value)}; +constexpr std::decay_t copy(T&& value) noexcept(noexcept(std::decay_t{ + static_cast(value)})) { + static_assert(!std::is_same, T>::value, "explicit copy of rvalue."); + return std::decay_t{static_cast(value)}; } /**************************************************************************************************/ From 500dfb71499904ebcad2b77702de907b98989bff Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Wed, 16 Aug 2023 11:59:22 -0700 Subject: [PATCH 02/17] Requiring noexcept for executor tasks. --- stlab/concurrency/channel.hpp | 128 +++++++++++------------ stlab/concurrency/default_executor.hpp | 18 ++-- stlab/concurrency/executor_base.hpp | 14 +-- stlab/concurrency/future.hpp | 16 +-- stlab/concurrency/immediate_executor.hpp | 8 +- stlab/concurrency/main_executor.hpp | 50 ++++----- stlab/concurrency/serial_queue.hpp | 15 +-- stlab/concurrency/system_timer.hpp | 61 +++++------ stlab/concurrency/task.hpp | 64 +++++++----- test/channel_test_helper.cpp | 2 +- test/channel_test_helper.hpp | 2 +- test/executor_test.cpp | 26 ++--- test/future_test_helper.hpp | 15 +-- test/future_when_any_arguments_tests.cpp | 2 +- test/serial_queue_test.cpp | 24 ++--- 15 files changed, 223 insertions(+), 222 deletions(-) diff --git a/stlab/concurrency/channel.hpp b/stlab/concurrency/channel.hpp index 2e7ce9d4..470b985d 100755 --- a/stlab/concurrency/channel.hpp +++ b/stlab/concurrency/channel.hpp @@ -348,7 +348,7 @@ auto set_process_error(P& process, std::exception_ptr&& error) } template -auto set_process_error(P&, std::exception_ptr &&) +auto set_process_error(P&, std::exception_ptr&&) -> std::enable_if_t>, void> {} /**************************************************************************************************/ @@ -597,8 +597,8 @@ template { shared_process& _shared_process; - explicit - shared_process_sender_indexed(shared_process& sp) : _shared_process(sp) {} + explicit shared_process_sender_indexed(shared_process& sp) : + _shared_process(sp) {} void add_sender() override { ++_shared_process._sender_count; } @@ -652,9 +652,7 @@ struct shared_process_sender_helper; template struct shared_process_sender_helper, Args...> : shared_process_sender_indexed... { - - explicit - shared_process_sender_helper(shared_process& sp) : + explicit shared_process_sender_helper(shared_process& sp) : shared_process_sender_indexed(sp)... {} }; @@ -768,8 +766,6 @@ struct shared_process const std::tuple>...> _upstream; - - template shared_process(E&& e, F&& f) : shared_process_sender_helper, Args...>( @@ -954,12 +950,12 @@ struct shared_process std::chrono::nanoseconds::min()) broadcast(unwrap(*_process).yield()); else - execute_at(duration, - _executor)([_weak_this = make_weak_ptr(this->shared_from_this())] { - auto _this = _weak_this.lock(); - if (!_this) return; - _this->try_broadcast(); - }); + execute_at(duration, _executor)( + [_weak_this = make_weak_ptr(this->shared_from_this())]() noexcept { + auto _this = _weak_this.lock(); + if (!_this) return; + _this->try_broadcast(); + }); } /* @@ -978,25 +974,25 @@ struct shared_process } else { /* Schedule a timeout. */ _timeout_function_active = true; - execute_at(duration, - _executor)([_weak_this = make_weak_ptr(this->shared_from_this())] { - auto _this = _weak_this.lock(); - // It may be that the complete channel is gone in the meanwhile - if (!_this) return; - - // try_lock can fail spuriously - while (true) { - lock_t lock(_this->_timeout_function_control, std::try_to_lock); - if (!lock) continue; - - // we were cancelled - if (get_process_state(_this->_process).first != process_state::yield) { - _this->try_broadcast(); - _this->_timeout_function_active = false; + execute_at(duration, _executor)( + [_weak_this = make_weak_ptr(this->shared_from_this())]() noexcept { + auto _this = _weak_this.lock(); + // It may be that the complete channel is gone in the meanwhile + if (!_this) return; + + // try_lock can fail spuriously + while (true) { + lock_t lock(_this->_timeout_function_control, std::try_to_lock); + if (!lock) continue; + + // we were cancelled + if (get_process_state(_this->_process).first != process_state::yield) { + _this->try_broadcast(); + _this->_timeout_function_active = false; + } + return; } - return; - } - }); + }); } } catch (...) { // this catches exceptions during _process.await() and _process.yield() broadcast(std::move(std::current_exception())); @@ -1038,12 +1034,12 @@ struct shared_process std::chrono::nanoseconds::min()) broadcast(unwrap(*_process).yield()); else - execute_at(duration, - _executor)([_weak_this = make_weak_ptr(this->shared_from_this())] { - auto _this = _weak_this.lock(); - if (!_this) return; - _this->try_broadcast(); - }); + execute_at(duration, _executor)( + [_weak_this = make_weak_ptr(this->shared_from_this())]() noexcept { + auto _this = _weak_this.lock(); + if (!_this) return; + _this->try_broadcast(); + }); } /* @@ -1062,25 +1058,25 @@ struct shared_process } else { /* Schedule a timeout. */ _timeout_function_active = true; - execute_at(duration, - _executor)([_weak_this = make_weak_ptr(this->shared_from_this())] { - auto _this = _weak_this.lock(); - // It may be that the complete channel is gone in the meanwhile - if (!_this) return; - - // try_lock can fail spuriously - while (true) { - lock_t lock(_this->_timeout_function_control, std::try_to_lock); - if (!lock) continue; - - // we were cancelled - if (get_process_state(_this->_process).first != process_state::yield) { - _this->try_broadcast(); - _this->_timeout_function_active = false; + execute_at(duration, _executor)( + [_weak_this = make_weak_ptr(this->shared_from_this())]() noexcept { + auto _this = _weak_this.lock(); + // It may be that the complete channel is gone in the meanwhile + if (!_this) return; + + // try_lock can fail spuriously + while (true) { + lock_t lock(_this->_timeout_function_control, std::try_to_lock); + if (!lock) continue; + + // we were cancelled + if (get_process_state(_this->_process).first != process_state::yield) { + _this->try_broadcast(); + _this->_timeout_function_active = false; + } + return; } - return; - } - }); + }); } } catch (...) { // this catches exceptions during _process.await() and _process.yield() broadcast(std::move(std::current_exception())); @@ -1098,10 +1094,12 @@ struct shared_process REVISIT (sparent) : See above comments on step() and ensure consistency. What is this code doing, if we don't have a yield then it also assumes no await? + + This seems to be doing a lot for a (required) noexcept operation - are we sure? */ template - auto step() -> std::enable_if_t>> { + auto step() noexcept -> std::enable_if_t>> { using queue_t = typename Q::value_type; stlab::optional message; std::array do_cts; @@ -1135,7 +1133,7 @@ struct shared_process } void run() { - _executor([_p = make_weak_ptr(this->shared_from_this())] { + _executor([_p = make_weak_ptr(this->shared_from_this())]() noexcept { auto p = _p.lock(); if (p) p->template step(); }); @@ -1352,8 +1350,7 @@ auto zip(S s, R... r) { /**************************************************************************************************/ -struct buffer_size -{ +struct buffer_size { std::size_t _value; buffer_size(std::size_t b) : _value(b) {} }; @@ -1378,15 +1375,16 @@ struct annotated_process { F _f; annotations _annotations; - explicit annotated_process(executor_task_pair&& etp) : _f(std::move(etp._f)), _annotations(std::move(etp._executor)) {} + explicit annotated_process(executor_task_pair&& etp) : + _f(std::move(etp._f)), _annotations(std::move(etp._executor)) {} annotated_process(F f, const executor& e) : _f(std::move(f)), _annotations(e._executor) {} annotated_process(F f, buffer_size bs) : _f(std::move(f)), _annotations(bs._value) {} annotated_process(F f, executor&& e) : _f(std::move(f)), _annotations(std::move(e._executor)) {} annotated_process(F f, annotations&& a) : _f(std::move(f)), _annotations(std::move(a)) {} - annotated_process(executor_task_pair&& etp, buffer_size bs) : _f(std::move(etp._f)), _annotations(std::move(etp._executor), bs) {} - + annotated_process(executor_task_pair&& etp, buffer_size bs) : + _f(std::move(etp._f)), _annotations(std::move(etp._executor), bs) {} }; template @@ -1553,8 +1551,8 @@ class STLAB_NODISCARD() receiver { } auto operator|(sender send) { - return operator| - ([_send = std::move(send)](auto&& x) { _send(std::forward(x)); }); + return operator|( + [_send = std::move(send)](auto&& x) { _send(std::forward(x)); }); } }; diff --git a/stlab/concurrency/default_executor.hpp b/stlab/concurrency/default_executor.hpp index a4a966fd..831ad461 100644 --- a/stlab/concurrency/default_executor.hpp +++ b/stlab/concurrency/default_executor.hpp @@ -91,7 +91,7 @@ struct executor_type { using result_type = void; template - void operator()(F f) const { + auto operator()(F f) const -> std::enable_if_t { using f_t = decltype(f); dispatch_group_async_f(detail::group()._group, @@ -224,7 +224,7 @@ class waiter { class notification_queue { struct element_t { std::size_t _priority; - task _task; + task _task; template element_t(F&& f, std::size_t priority) : _priority{priority}, _task{std::forward(f)} {} @@ -250,7 +250,7 @@ class notification_queue { } // Must be called under a lock with a non-empty _q, always returns a valid task - auto pop_not_empty() -> task { + auto pop_not_empty() -> task { auto result = std::move(_q.front()._task); std::pop_heap(begin(_q), end(_q), element_t::greater()); _q.pop_back(); @@ -258,7 +258,7 @@ class notification_queue { } public: - auto try_pop() -> task { + auto try_pop() -> task { lock_t lock{_mutex, std::try_to_lock}; if (!lock || _q.empty()) return nullptr; return pop_not_empty(); @@ -275,7 +275,7 @@ class notification_queue { return true; } - auto pop() -> std::pair> { + auto pop() -> std::pair> { lock_t lock{_mutex}; _waiting = true; while (_q.empty() && !_done && _waiting) @@ -341,7 +341,7 @@ class priority_task_system { void run(unsigned i) { stlab::set_current_thread_name("cc.stlab.default_executor"); while (true) { - task f; + task f; for (unsigned n = 0; n != _count && !f; ++n) { f = _q[(i + n) % _count].try_pop(); @@ -408,7 +408,7 @@ class priority_task_system { stlab::set_current_thread_name("cc.stlab.default_executor.expansion"); while (true) { - task f; + task f; for (unsigned n = 0; n != _count && !f; ++n) { f = _q[(i + n) % _count].try_pop(); @@ -458,7 +458,7 @@ template struct executor_type { using result_type = void; - void operator()(task f) const { + void operator()(task&& f) const { static task_system

only_task_system{[] { at_pre_exit([]() noexcept { only_task_system.join(); }); return task_system

{}; @@ -473,7 +473,7 @@ template struct executor_type { using result_type = void; - void operator()(task f) const { + void operator()(task&& f) const { pts().execute(P)>(std::move(f)); } }; diff --git a/stlab/concurrency/executor_base.hpp b/stlab/concurrency/executor_base.hpp index e49a73a1..693a9cf7 100644 --- a/stlab/concurrency/executor_base.hpp +++ b/stlab/concurrency/executor_base.hpp @@ -24,7 +24,7 @@ namespace stlab { inline namespace v1 { /**************************************************************************************************/ -using executor_t = std::function)>; +using executor_t = std::function)>; /* * returns an executor that will schedule any passed task to it to execute @@ -35,16 +35,17 @@ template > executor_t execute_at(std::chrono::duration duration, executor_t executor) { return [_duration = std::move(duration), _executor = std::move(executor)](auto f) mutable { if (_duration != std::chrono::duration{}) - system_timer(_duration, [_f = std::move(f), _executor = std::move(_executor)]() mutable { - _executor(std::move(_f)); - }); + system_timer(_duration, + [_f = std::move(f), _executor = std::move(_executor)]() mutable noexcept { + _executor(std::move(_f)); + }); else _executor(std::move(f)); }; } -[[deprecated("Use chrono::duration as parameter instead")]] -inline executor_t execute_at(std::chrono::steady_clock::time_point when, executor_t executor) { +[[deprecated("Use chrono::duration as parameter instead")]] inline executor_t execute_at( + std::chrono::steady_clock::time_point when, executor_t executor) { using namespace std::chrono; return execute_at(duration_cast(when - steady_clock::now()), std::move(executor)); } @@ -79,7 +80,6 @@ executor_task_pair operator&(F&& f, executor e) { return executor_task_pair{std::move(e._executor), std::forward(f)}; } - /**************************************************************************************************/ } // namespace v1 diff --git a/stlab/concurrency/future.hpp b/stlab/concurrency/future.hpp index 392e2930..75982f55 100755 --- a/stlab/concurrency/future.hpp +++ b/stlab/concurrency/future.hpp @@ -256,7 +256,7 @@ struct shared_task { template struct shared_base> : std::enable_shared_from_this> { - using then_t = std::vector>>; + using then_t = std::vector>>; executor_t _executor; stlab::optional _result; @@ -294,7 +294,7 @@ struct shared_base> : std::enable_shared_from_this lock(_mutex); - if (!_ready) _then.emplace_back([](auto&&) {}, [_p = this->shared_from_this()] {}); + if (!_ready) _then.emplace_back([](auto&&) {}, [_p = this->shared_from_this()]() noexcept {}); } void set_exception(std::exception_ptr error) { @@ -361,7 +361,7 @@ struct shared_base> : std::enable_shared_from_this struct shared_base> : std::enable_shared_from_this> { - using then_t = std::pair>; + using then_t = std::pair>; executor_t _executor; stlab::optional _result; @@ -399,7 +399,7 @@ struct shared_base> : std::enable_shared_from_this< void _detach() { std::unique_lock lock(_mutex); - if (!_ready) _then = then_t([](auto&&) {}, [_p = this->shared_from_this()] {}); + if (!_ready) _then = then_t([](auto&&) {}, [_p = this->shared_from_this()] () noexcept {}); } void set_exception(std::exception_ptr error) { @@ -438,7 +438,7 @@ struct shared_base> : std::enable_shared_from_this< template <> struct shared_base : std::enable_shared_from_this> { - using then_t = std::vector>>; + using then_t = std::vector>>; using result_type = void; executor_t _executor; @@ -461,7 +461,7 @@ struct shared_base : std::enable_shared_from_this> { void _detach() { std::unique_lock lock(_mutex); - if (!_ready) _then.emplace_back([](auto&&) {}, [_p = this->shared_from_this()] {}); + if (!_ready) _then.emplace_back([](auto&&) {}, [_p = this->shared_from_this()]() noexcept {}); } void set_exception(std::exception_ptr error) { @@ -518,7 +518,7 @@ struct shared : shared_base, shared_task { void add_promise() override { ++_promise_count; } - void operator()(Args... args) override { + void operator()(Args... args) noexcept override { if (!_f) return; try { @@ -582,7 +582,7 @@ class packaged_task { packaged_task& operator=(packaged_task&& x) noexcept = default; template - void operator()(A&&... args) const { + void operator()(A&&... args) const noexcept { auto p = _p.lock(); if (p) (*p)(std::forward(args)...); } diff --git a/stlab/concurrency/immediate_executor.hpp b/stlab/concurrency/immediate_executor.hpp index 96f15edc..8f855bb1 100644 --- a/stlab/concurrency/immediate_executor.hpp +++ b/stlab/concurrency/immediate_executor.hpp @@ -10,6 +10,7 @@ #define STLAB_CONCURRENCY_IMMEDIATE_EXECUTOR_HPP #include +#include #include /**************************************************************************************************/ @@ -27,12 +28,7 @@ namespace detail { struct immediate_executor_type { template - void operator()(F&& f) const { - std::forward(f)(); - } - - template - void operator()(std::chrono::steady_clock::time_point, F&& f) const { + auto operator()(F&& f) const -> std::enable_if_t { std::forward(f)(); } }; diff --git a/stlab/concurrency/main_executor.hpp b/stlab/concurrency/main_executor.hpp index 54d9ea9c..b4067699 100644 --- a/stlab/concurrency/main_executor.hpp +++ b/stlab/concurrency/main_executor.hpp @@ -9,20 +9,23 @@ #ifndef STLAB_CONCURRENCY_MAIN_EXECUTOR_HPP #define STLAB_CONCURRENCY_MAIN_EXECUTOR_HPP +#include #include #include #if STLAB_MAIN_EXECUTOR(QT5) || STLAB_MAIN_EXECUTOR(QT6) #include -#if (STLAB_MAIN_EXECUTOR(QT5) && (QT_VERSION < QT_VERSION_CHECK(5,0,0) || QT_VERSION >= QT_VERSION_CHECK(6,0,0)) || \ - STLAB_MAIN_EXECUTOR(QT6) && (QT_VERSION < QT_VERSION_CHECK(6,0,0) || QT_VERSION >= QT_VERSION_CHECK(7,0,0))) +#if (STLAB_MAIN_EXECUTOR(QT5) && \ + (QT_VERSION < QT_VERSION_CHECK(5, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) || \ + STLAB_MAIN_EXECUTOR(QT6) && \ + (QT_VERSION < QT_VERSION_CHECK(6, 0, 0) || QT_VERSION >= QT_VERSION_CHECK(7, 0, 0))) #error "Mismatching Qt versions" #endif #include #include -#include #include +#include #elif STLAB_MAIN_EXECUTOR(LIBDISPATCH) #include #elif STLAB_MAIN_EXECUTOR(EMSCRIPTEN) @@ -82,7 +85,7 @@ class main_executor_type { public: template - void operator()(F f) const { + auto operator()(F f) const -> std::enable_if_t { auto event = std::make_unique(); event->set_task(std::move(f)); auto receiver = event->receiver(); @@ -98,7 +101,7 @@ struct main_executor_type { using result_type = void; template - void operator()(F f) const { + auto operator()(F f) const -> std::enable_if_t { using f_t = decltype(f); dispatch_async_f(dispatch_get_main_queue(), new f_t(std::move(f)), [](void* f_) { @@ -115,33 +118,32 @@ struct main_executor_type { using result_type = void; template - void operator()(F&& f) const { + auto operator()(F&& f) const -> std::enable_if_t { using function_type = typename std::remove_reference::type; auto p = new function_type(std::forward(f)); - /* - `emscripten_async_run_in_main_runtime_thread()` schedules a function to run on the main - JS thread, however, the code can be executed at any POSIX thread cancelation point if - wasm code is executing on the JS main thread. - Executing the code from a POSIX thread cancelation point can cause problems, including - deadlocks and data corruption. Consider: - ``` - mutex.lock(); // <-- If reentered, would deadlock here - new T; // <-- POSIX cancelation point, could reenter - ``` - The call to `emscripten_async_call()` bounces the call to execute as part of the main - run-loop on the current (main) thread. This avoids nasty reentrancy issues if executed - from a POSIX thread cancelation point. - */ + /* + `emscripten_async_run_in_main_runtime_thread()` schedules a function to run on the main + JS thread, however, the code can be executed at any POSIX thread cancelation point if + wasm code is executing on the JS main thread. + Executing the code from a POSIX thread cancelation point can cause problems, including + deadlocks and data corruption. Consider: + ``` + mutex.lock(); // <-- If reentered, would deadlock here + new T; // <-- POSIX cancelation point, could reenter + ``` + The call to `emscripten_async_call()` bounces the call to execute as part of the main + run-loop on the current (main) thread. This avoids nasty reentrancy issues if executed + from a POSIX thread cancelation point. + */ emscripten_async_run_in_main_runtime_thread( - EM_FUNC_SIG_VI, - static_cast([](void* f_) { + EM_FUNC_SIG_VI, static_cast([](void* f_) { emscripten_async_call( [](void* f_) { auto f = static_cast(f_); // Note the absence of exception handling. - // Operations queued to the task system cannot throw as a precondition. + // Operations queued to the task system cannot throw as a precondition. // We use packaged tasks to marshal exceptions. (*f)(); delete f; @@ -159,7 +161,7 @@ struct main_executor_type { using result_type = void; template - void operator()(F f) const { } + void operator()(F f) const {} }; #endif diff --git a/stlab/concurrency/serial_queue.hpp b/stlab/concurrency/serial_queue.hpp index a8878bd5..2ca57a66 100644 --- a/stlab/concurrency/serial_queue.hpp +++ b/stlab/concurrency/serial_queue.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -41,8 +42,8 @@ namespace detail { /**************************************************************************************************/ class serial_instance_t : public std::enable_shared_from_this { - using executor_t = std::function&&)>; - using queue_t = std::deque>; + using executor_t = std::function&&)>; + using queue_t = std::deque>; using lock_t = std::lock_guard; std::mutex _m; @@ -80,7 +81,7 @@ class serial_instance_t : public std::enable_shared_from_this pop_front_unsafe(local_queue)(); } - if (!empty()) _executor([_this(shared_from_this())]() { _this->all(); }); + if (!empty()) _executor([_this(shared_from_this())]() noexcept { _this->all(); }); } void single() { @@ -90,7 +91,7 @@ class serial_instance_t : public std::enable_shared_from_this f(); - if (!empty()) _executor([_this(shared_from_this())]() { _this->single(); }); + if (!empty()) _executor([_this(shared_from_this())]() noexcept { _this->single(); }); } // The kickstart allows us to grab a pointer to either the single or all @@ -125,7 +126,7 @@ class serial_instance_t : public std::enable_shared_from_this }); if (!running) { - _executor([_this(shared_from_this())]() { _this->kickstart(); }); + _executor([_this(shared_from_this())]() noexcept { _this->kickstart(); }); } } @@ -149,7 +150,9 @@ class serial_queue_t { [_e = std::move(e)](auto&& f) { _e(std::forward(f)); }, mode)) {} auto executor() const { - return [_impl = _impl](auto&& f) { _impl->enqueue(std::forward(f)); }; + return [_impl = _impl](auto&& f) -> std::enable_if_t { + _impl->enqueue(std::forward(f)); + }; } template diff --git a/stlab/concurrency/system_timer.hpp b/stlab/concurrency/system_timer.hpp index 6ac84f37..7ee9ac0b 100755 --- a/stlab/concurrency/system_timer.hpp +++ b/stlab/concurrency/system_timer.hpp @@ -11,11 +11,11 @@ /**************************************************************************************************/ -#include #include #include #include +#include #if STLAB_TASK_SYSTEM(LIBDISPATCH) #include @@ -23,18 +23,13 @@ #include #include #elif STLAB_TASK_SYSTEM(PORTABLE) - #include #include #include #include +#endif #include -// REVISIT (sparent) : for testing only -#if 0 && __APPLE__ -#include -#endif -#endif /**************************************************************************************************/ @@ -55,28 +50,27 @@ namespace detail { struct system_timer_type { using result_type = void; - template - [[deprecated("Use chrono::duration as parameter instead")]] - void operator()(std::chrono::steady_clock::time_point when, F f) const { + [[deprecated("Use chrono::duration as parameter instead")]] void operator()( + std::chrono::steady_clock::time_point when, F f) const { using namespace std::chrono; operator()(when - steady_clock::now(), std::move(f)); } template > - void operator()(std::chrono::duration duration, F f) const { + auto operator()(std::chrono::duration duration, F f) const + -> std::enable_if_t { using namespace std::chrono; using f_t = decltype(f); - dispatch_after_f( - dispatch_time(0, duration_cast(duration).count()), - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), new f_t(std::move(f)), - [](void* f_) { - auto f = static_cast(f_); - (*f)(); - delete f; - }); + dispatch_after_f(dispatch_time(0, duration_cast(duration).count()), + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + new f_t(std::move(f)), [](void* f_) { + auto f = static_cast(f_); + (*f)(); + delete f; + }); } }; @@ -108,16 +102,16 @@ class system_timer { CloseThreadpool(_pool); } - template - [[deprecated("Use chrono::duration as parameter instead")]] - void operator()(std::chrono::steady_clock::time_point when, F&& f) { + [[deprecated("Use chrono::duration as parameter instead")]] void operator()( + std::chrono::steady_clock::time_point when, F&& f) { using namespace std::chrono; operator()(when - steady_clock::now(), std::forward(f)); } template > - void operator()(std::chrono::duration duration, F&& f) { + auto operator()(std::chrono::duration duration, F&& f) + -> std::enable_if_f { using namespace std::chrono; auto timer = CreateThreadpoolTimer(&timer_callback_impl, new F(std::forward(f)), &_callBackEnvironment); @@ -140,7 +134,7 @@ class system_timer { (*f)(); } - template > + template > FILETIME duration_to_FILETIME(std::chrono::duration duration) const { using namespace std::chrono; FILETIME ft = {0, 0}; @@ -170,7 +164,7 @@ class system_timer { #elif STLAB_TASK_SYSTEM(PORTABLE) class system_timer { - using element_t = std::pair>; + using element_t = std::pair>; using queue_t = std::vector; using lock_t = std::unique_lock; @@ -191,7 +185,7 @@ class system_timer { void timed_queue_run() { while (true) { - task task; + task task; { lock_t lock(_timed_queue_mutex); @@ -227,14 +221,15 @@ class system_timer { } template - [[deprecated("Use chrono::duration as parameter instead")]] - void operator()(std::chrono::steady_clock::time_point when, F&& f) { + [[deprecated("Use chrono::duration as parameter instead")]] void operator()( + std::chrono::steady_clock::time_point when, F&& f) { using namespace std::chrono; operator()(when - steady_clock::now(), std::move(f)); } template > - void operator()(std::chrono::duration duration, F&& f) { + void operator()(std::chrono::duration duration, F&& f) + ->std::enable_if_f { lock_t lock(_timed_queue_mutex); _timed_queue.emplace_back(std::chrono::steady_clock::now() + duration, std::forward(f)); std::push_heap(std::begin(_timed_queue), std::end(_timed_queue), greater_first()); @@ -250,19 +245,19 @@ class system_timer { struct system_timer_type { using result_type = void; - + static system_timer& get_system_timer() { static system_timer only_system_timer; return only_system_timer; } - [[deprecated("Use chrono::duration as parameter instead")]] - void operator()(std::chrono::steady_clock::time_point when, task f) const { + [[deprecated("Use chrono::duration as parameter instead")]] void operator()( + std::chrono::steady_clock::time_point when, task&& f) const { operator()(when - std::chrono::steady_clock().now(), std::move(f)); } template > - void operator()(std::chrono::duration duration, task f) const { + void operator()(std::chrono::duration duration, task&& f) const { get_system_timer()(duration, std::move(f)); } }; diff --git a/stlab/concurrency/task.hpp b/stlab/concurrency/task.hpp index 074f6b8d..cf515af6 100644 --- a/stlab/concurrency/task.hpp +++ b/stlab/concurrency/task.hpp @@ -37,8 +37,8 @@ inline namespace v1 { template class task; -template -class task { +template +class task { template constexpr static bool maybe_empty = std::is_pointer>::value || std::is_member_pointer>::value || @@ -55,14 +55,14 @@ class task { } struct concept_t { - void (*dtor)(void*); + void (*dtor)(void*) noexcept; void (*move_ctor)(void*, void*) noexcept; const std::type_info& (*target_type)() noexcept; void* (*pointer)(void*) noexcept; const void* (*const_pointer)(const void*) noexcept; }; - using invoke_t = R (*)(void*, Args...); + using invoke_t = R (*)(void*, Args...) noexcept(NoExcept); template struct model; @@ -73,7 +73,7 @@ class task { model(G&& f) : _f(std::forward(f)) {} model(model&&) noexcept = delete; - static void dtor(void* self) { static_cast(self)->~model(); } + static void dtor(void* self) noexcept { static_cast(self)->~model(); } static void move_ctor(void* self, void* p) noexcept { new (p) model(std::move(static_cast(self)->_f)); } @@ -85,7 +85,7 @@ class task { signature to the actual captured model. */ - static auto invoke(void* self, Args... args) -> R { + static auto invoke(void* self, Args... args) noexcept(NoExcept) -> R { return (static_cast(self)->_f)(std::forward(args)...); } @@ -110,7 +110,7 @@ class task { model(G&& f) : _p(std::make_unique(std::forward(f))) {} model(model&&) noexcept = default; - static void dtor(void* self) { static_cast(self)->~model(); } + static void dtor(void* self) noexcept { static_cast(self)->~model(); } static void move_ctor(void* self, void* p) noexcept { new (p) model(std::move(*static_cast(self))); } @@ -122,11 +122,10 @@ class task { signature to the actual captured model. */ - static auto invoke(void* self, Args... args) -> R { + static auto invoke(void* self, Args... args) noexcept(NoExcept) -> R { return (*static_cast(self)->_p)(std::forward(args)...); } - static auto target_type() noexcept -> const std::type_info& { return typeid(F); } static auto pointer(void* self) noexcept -> void* { return static_cast(self)->_p.get(); @@ -147,9 +146,12 @@ class task { }; // empty (default) vtable - static void dtor(void*) {} + static void dtor(void*) noexcept {} static void move_ctor(void*, void*) noexcept {} - static auto invoke(void*, Args...) -> R { throw std::bad_function_call(); } + static auto invoke(void*, Args...) noexcept(NoExcept) -> R { + if constexpr (NoExcept) std::terminate(); + throw std::bad_function_call(); + } static auto target_type_() noexcept -> const std::type_info& { return typeid(void); } static auto pointer(void*) noexcept -> void* { return nullptr; } static auto const_pointer(const void*) noexcept -> const void* { return nullptr; } @@ -194,7 +196,9 @@ class task { _vtable_ptr->move_ctor(&x._model, &_model); } - template , task>::value, bool> = true> + template ()(std::declval()...)), + bool> = true> task(F&& f) { using small_t = model, true>; using large_t = model, false>; @@ -224,7 +228,8 @@ class task { task& operator=(std::nullptr_t) noexcept { return *this = task(); } template - task& operator=(F&& f) { + auto operator=(F&& f) + -> std::enable_if_t()...)), task&> { return *this = task(std::forward(f)); } @@ -248,7 +253,9 @@ class task { } template - auto operator()(Brgs&&... brgs) { return _invoke(&_model, std::forward(brgs)...); } + auto operator()(Brgs&&... brgs) noexcept(NoExcept) { + return _invoke(&_model, std::forward(brgs)...); + } friend inline void swap(task& x, task& y) { return x.swap(y); } friend inline bool operator==(const task& x, std::nullptr_t) { return !static_cast(x); } @@ -262,26 +269,31 @@ class task { // In C++17 constexpr implies inline and these definitions are deprecated #if defined(__GNUC__) && __GNUC__ < 7 && !defined(__clang__) -template -const typename task::concept_t task::_vtable = { - dtor, move_ctor, target_type_, pointer, const_pointer}; - -template -const typename task::invoke_t task::_invoke = _invoke; +template +const typename task::concept_t + task::_vtable = {dtor, move_ctor, target_type_, pointer, + const_pointer}; + +template +const typename task::invoke_t + task::_invoke = _invoke; #else -template -const typename task::concept_t task::_vtable; +template +const typename task::concept_t + task::_vtable; #endif #ifdef _MSC_VER -template +template template -const typename task::concept_t task::model::_vtable; +const typename task::concept_t + task::model::_vtable; -template +template template -const typename task::concept_t task::model::_vtable; +const typename task::concept_t + task::model::_vtable; #else diff --git a/test/channel_test_helper.cpp b/test/channel_test_helper.cpp index 166cce27..3d538c02 100644 --- a/test/channel_test_helper.cpp +++ b/test/channel_test_helper.cpp @@ -8,7 +8,7 @@ #include "channel_test_helper.hpp" -std::queue> channel_test_helper::manual_scheduler::_tasks; +std::queue> channel_test_helper::manual_scheduler::_tasks; std::mutex channel_test_helper::manual_scheduler::_mutex; int channel_test_helper::timed_sum::_x{0}; diff --git a/test/channel_test_helper.hpp b/test/channel_test_helper.hpp index 29c8d145..f90128e8 100644 --- a/test/channel_test_helper.hpp +++ b/test/channel_test_helper.hpp @@ -25,7 +25,7 @@ namespace channel_test_helper { class manual_scheduler { static std::mutex _mutex; - static std::queue> _tasks; + static std::queue> _tasks; public: static void clear() { diff --git a/test/executor_test.cpp b/test/executor_test.cpp index 46bd559b..8e0bc888 100644 --- a/test/executor_test.cpp +++ b/test/executor_test.cpp @@ -74,12 +74,12 @@ BOOST_AUTO_TEST_CASE(all_low_prio_tasks_are_executed) { atomic_bool done{false}; for (auto i = 0; i < 10; ++i) { - queue.executor()([_i = i, &m, &results] { + queue.executor()([_i = i, &m, &results]() noexcept { unique_lock block{m}; results.push_back(_i); }); } - queue.executor()([&done] { done = true; }); + queue.executor()([&done] () noexcept { done = true; }); while (!done) { rest(); @@ -99,12 +99,12 @@ BOOST_AUTO_TEST_CASE(all_default_prio_tasks_get_executed) { atomic_bool done{false}; for (auto i = 0; i < 10; ++i) { - queue.executor()([_i = i, &m, &results] { + queue.executor()([_i = i, &m, &results] () noexcept { unique_lock block{m}; results.push_back(_i); }); } - queue.executor()([&done] { done = true; }); + queue.executor()([&done] () noexcept { done = true; }); while (!done) { rest(); @@ -124,12 +124,12 @@ BOOST_AUTO_TEST_CASE(all_high_prio_tasks_get_executed) { atomic_bool done{false}; for (auto i = 0; i < 10; ++i) { - queue.executor()([_i = i, &m, &results] { + queue.executor()([_i = i, &m, &results] () noexcept { unique_lock block{m}; results.push_back(_i); }); } - queue.executor()([&done] { done = true; }); + queue.executor()([&done] () noexcept { done = true; }); while (!done) { rest(); @@ -147,7 +147,7 @@ BOOST_AUTO_TEST_CASE(task_system_restarts_after_it_went_pending) { mutex m; condition_variable cv; - default_executor([&] { + default_executor([&] () noexcept { rest(); { unique_lock block{m}; @@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(task_system_restarts_after_it_went_pending) { } } - default_executor([&] { + default_executor([&] () noexcept { rest(); { unique_lock block{m}; @@ -226,7 +226,7 @@ struct check_task { return 0; } - void operator()() { + void operator()() noexcept { --_currentPrioCount; ++taskRunning; @@ -298,17 +298,17 @@ BOOST_AUTO_TEST_CASE(MeasureTiming) { auto start = chrono::high_resolution_clock::now(); for (auto i = 0; i < iterations; ++i) { - low_executor([_i = i, &results, &counter] { + low_executor([_i = i, &results, &counter] () noexcept { results[_i] = 1; fibonacci(fiboN); ++counter; }); - default_executor([_i = i + iterations, &results, &counter] { + default_executor([_i = i + iterations, &results, &counter] () noexcept { results[_i] = 2; fibonacci(fiboN); ++counter; }); - high_executor([_i = i + iterations * 2, &results, &counter] { + high_executor([_i = i + iterations * 2, &results, &counter] () noexcept { results[_i] = 3; fibonacci(fiboN); ++counter; @@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(MeasureTiming) { } mutex block; - low_executor([&] { + low_executor([&] () noexcept { { unique_lock lock{block}; done = true; diff --git a/test/future_test_helper.hpp b/test/future_test_helper.hpp index 65628761..2a2b1179 100644 --- a/test/future_test_helper.hpp +++ b/test/future_test_helper.hpp @@ -28,7 +28,7 @@ template struct custom_scheduler { using result_type = void; - void operator()(stlab::task f) const { + void operator()(stlab::task f) const { ++counter(); // The implementation on Windows or the mac uses a scheduler that allows many tasks in the // pool in parallel @@ -43,9 +43,7 @@ struct custom_scheduler { static int usage_counter() { return counter().load(); } - static void reset() { - counter() = 0; - } + static void reset() { counter() = 0; } static std::atomic_int& counter() { static std::atomic_int counter; @@ -56,16 +54,13 @@ struct custom_scheduler { size_t _id = no; // only used for debugging purpose }; - - template stlab::executor_t make_executor() { - return [_executor = custom_scheduler{}](stlab::task f) mutable { + return [_executor = custom_scheduler{}](stlab::task f) mutable { _executor(std::move(f)); }; } - class test_exception : public std::exception { std::string _error; @@ -196,8 +191,8 @@ class test_functor_base : public P { std::atomic_int& _task_counter; public: - test_functor_base(F f, std::atomic_int& task_counter) - : _f(std::move(f)), _task_counter(task_counter) {} + test_functor_base(F f, std::atomic_int& task_counter) : + _f(std::move(f)), _task_counter(task_counter) {} ~test_functor_base() {} diff --git a/test/future_when_any_arguments_tests.cpp b/test/future_when_any_arguments_tests.cpp index a3057d6e..92a0aeb2 100644 --- a/test/future_when_any_arguments_tests.cpp +++ b/test/future_when_any_arguments_tests.cpp @@ -448,4 +448,4 @@ BOOST_AUTO_TEST_CASE(future_when_any_move_only_argument_with_one_argument) { BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter()); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/serial_queue_test.cpp b/test/serial_queue_test.cpp index 7ed34285..eec469cf 100644 --- a/test/serial_queue_test.cpp +++ b/test/serial_queue_test.cpp @@ -35,31 +35,31 @@ void test0(stlab::schedule_mode mode) { output.emplace_back(std::move(str)); }); - aq([&]() { strout("a1 ( 1)"); }); + aq([&]() noexcept { strout("a1 ( 1)"); }); - bq([&]() { strout(" b1 ( 2)"); }); + bq([&]() noexcept { strout(" b1 ( 2)"); }); - dq([&]() { strout(" d1 ( 3)"); }); + dq([&]() noexcept { strout(" d1 ( 3)"); }); b([&]() { strout(" b2 ( 4)"); }) .then(stlab::immediate_executor, [&]() { strout(" b2.1 ( 4.1)"); }) .detach(); - cq([&]() { strout(" c1 ( 5)"); }); + cq([&]() noexcept { strout(" c1 ( 5)"); }); - cq([&]() { strout(" c2 ( 6)"); }); + cq([&]() noexcept { strout(" c2 ( 6)"); }); - dq([&]() { strout(" d2 ( 7)"); }); + dq([&]() noexcept { strout(" d2 ( 7)"); }); - cq([&]() { strout(" c3 ( 8)"); }); + cq([&]() noexcept { strout(" c3 ( 8)"); }); - bq([&]() { strout(" b3 ( 9)"); }); + bq([&]() noexcept { strout(" b3 ( 9)"); }); - aq([&]() { strout("a2 (10)"); }); + aq([&]() noexcept { strout("a2 (10)"); }); - aq([&]() { strout("a3 (11)"); }); + aq([&]() noexcept { strout("a3 (11)"); }); - dq([&]() { strout(" d3 (12)"); }); + dq([&]() noexcept { strout(" d3 (12)"); }); while (true) { { @@ -132,7 +132,7 @@ struct serial_hash { : _q{stlab::default_executor, mode}, _h(std::move(s), e) {} void operator()(std::string s, std::uint64_t e) { - _q.executor()([this, _e = e, _s = std::move(s)]() { + _q.executor()([this, _e = e, _s = std::move(s)]() noexcept { _h(_s, _e); ++_c; }); From c4adc111293ff309201c8fe1082769304e85aa42 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Wed, 16 Aug 2023 13:12:05 -0700 Subject: [PATCH 03/17] Fixing typeos for CI. --- stlab/concurrency/system_timer.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stlab/concurrency/system_timer.hpp b/stlab/concurrency/system_timer.hpp index 7ee9ac0b..0c4746e4 100755 --- a/stlab/concurrency/system_timer.hpp +++ b/stlab/concurrency/system_timer.hpp @@ -111,7 +111,7 @@ class system_timer { template > auto operator()(std::chrono::duration duration, F&& f) - -> std::enable_if_f { + -> std::enable_if_t { using namespace std::chrono; auto timer = CreateThreadpoolTimer(&timer_callback_impl, new F(std::forward(f)), &_callBackEnvironment); @@ -229,7 +229,7 @@ class system_timer { template > void operator()(std::chrono::duration duration, F&& f) - ->std::enable_if_f { + ->std::enable_if_t { lock_t lock(_timed_queue_mutex); _timed_queue.emplace_back(std::chrono::steady_clock::now() + duration, std::forward(f)); std::push_heap(std::begin(_timed_queue), std::end(_timed_queue), greater_first()); From 1f0c8bcba3d5147c5ab45040e7a4e25bafca84b3 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Wed, 16 Aug 2023 13:34:56 -0700 Subject: [PATCH 04/17] Fixing typos and mistakes for CI. --- stlab/concurrency/system_timer.hpp | 2 +- stlab/concurrency/task.hpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/stlab/concurrency/system_timer.hpp b/stlab/concurrency/system_timer.hpp index 0c4746e4..1a462100 100755 --- a/stlab/concurrency/system_timer.hpp +++ b/stlab/concurrency/system_timer.hpp @@ -228,7 +228,7 @@ class system_timer { } template > - void operator()(std::chrono::duration duration, F&& f) + auto operator()(std::chrono::duration duration, F&& f) ->std::enable_if_t { lock_t lock(_timed_queue_mutex); _timed_queue.emplace_back(std::chrono::steady_clock::now() + duration, std::forward(f)); diff --git a/stlab/concurrency/task.hpp b/stlab/concurrency/task.hpp index cf515af6..811587f2 100644 --- a/stlab/concurrency/task.hpp +++ b/stlab/concurrency/task.hpp @@ -149,8 +149,11 @@ class task { static void dtor(void*) noexcept {} static void move_ctor(void*, void*) noexcept {} static auto invoke(void*, Args...) noexcept(NoExcept) -> R { - if constexpr (NoExcept) std::terminate(); - throw std::bad_function_call(); + if constexpr (NoExcept) { + std::terminate(); + } else { + throw std::bad_function_call(); + } } static auto target_type_() noexcept -> const std::type_info& { return typeid(void); } static auto pointer(void*) noexcept -> void* { return nullptr; } From f3f339d33173bbea18e762afabb2e2c4e0bf1ee6 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Wed, 16 Aug 2023 13:47:47 -0700 Subject: [PATCH 05/17] Updating GCC and fixing future for CI. --- .github/matrix.json | 4 ++-- stlab/concurrency/future.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/matrix.json b/.github/matrix.json index c32dc4d7..da5f6769 100644 --- a/.github/matrix.json +++ b/.github/matrix.json @@ -1,9 +1,9 @@ { "config": [ { - "name": "Linux GCC 11", + "name": "Linux GCC 12", "compiler": "gcc", - "version": "11", + "version": "12", "os": "ubuntu-22.04" }, { diff --git a/stlab/concurrency/future.hpp b/stlab/concurrency/future.hpp index 75982f55..ff814cd0 100755 --- a/stlab/concurrency/future.hpp +++ b/stlab/concurrency/future.hpp @@ -372,7 +372,7 @@ struct shared_base> : std::enable_shared_from_this< explicit shared_base(executor_t s) : _executor(std::move(s)) {} - void reset() { _then.second = task{}; } + void reset() { _then.second = task{}; } template auto recover(future&& p, F&& f) { From 25bec08d4423839cccb41efbd136b79998ccb8c0 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Wed, 16 Aug 2023 15:57:16 -0700 Subject: [PATCH 06/17] Removing noexcept() from function signatures for GCC prior to 13.2 --- stlab/concurrency/default_executor.hpp | 9 ++++-- stlab/concurrency/immediate_executor.hpp | 6 ++-- stlab/concurrency/main_executor.hpp | 8 +++-- stlab/concurrency/serial_queue.hpp | 3 +- stlab/concurrency/system_timer.hpp | 8 +++-- stlab/concurrency/task.hpp | 7 ++-- stlab/type_traits.hpp | 41 ++++++++++++++++++++++++ 7 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 stlab/type_traits.hpp diff --git a/stlab/concurrency/default_executor.hpp b/stlab/concurrency/default_executor.hpp index 831ad461..3f60c23a 100644 --- a/stlab/concurrency/default_executor.hpp +++ b/stlab/concurrency/default_executor.hpp @@ -9,10 +9,13 @@ #ifndef STLAB_CONCURRENCY_DEFAULT_EXECUTOR_HPP #define STLAB_CONCURRENCY_DEFAULT_EXECUTOR_HPP -#include -#include #include + #include +#include + +#include +#include #include #include @@ -91,7 +94,7 @@ struct executor_type { using result_type = void; template - auto operator()(F f) const -> std::enable_if_t { + auto operator()(F f) const -> std::enable_if_t> { using f_t = decltype(f); dispatch_group_async_f(detail::group()._group, diff --git a/stlab/concurrency/immediate_executor.hpp b/stlab/concurrency/immediate_executor.hpp index 8f855bb1..31a52bea 100644 --- a/stlab/concurrency/immediate_executor.hpp +++ b/stlab/concurrency/immediate_executor.hpp @@ -9,9 +9,9 @@ #ifndef STLAB_CONCURRENCY_IMMEDIATE_EXECUTOR_HPP #define STLAB_CONCURRENCY_IMMEDIATE_EXECUTOR_HPP -#include #include -#include + +#include /**************************************************************************************************/ @@ -28,7 +28,7 @@ namespace detail { struct immediate_executor_type { template - auto operator()(F&& f) const -> std::enable_if_t { + auto operator()(F&& f) const -> std::enable_if_t::value> { std::forward(f)(); } }; diff --git a/stlab/concurrency/main_executor.hpp b/stlab/concurrency/main_executor.hpp index b4067699..d77ffd15 100644 --- a/stlab/concurrency/main_executor.hpp +++ b/stlab/concurrency/main_executor.hpp @@ -14,6 +14,8 @@ #include +#include + #if STLAB_MAIN_EXECUTOR(QT5) || STLAB_MAIN_EXECUTOR(QT6) #include #if (STLAB_MAIN_EXECUTOR(QT5) && \ @@ -85,7 +87,7 @@ class main_executor_type { public: template - auto operator()(F f) const -> std::enable_if_t { + auto operator()(F f) const -> std::enable_if_t::value> { auto event = std::make_unique(); event->set_task(std::move(f)); auto receiver = event->receiver(); @@ -101,7 +103,7 @@ struct main_executor_type { using result_type = void; template - auto operator()(F f) const -> std::enable_if_t { + auto operator()(F f) const -> std::enable_if_t::value> { using f_t = decltype(f); dispatch_async_f(dispatch_get_main_queue(), new f_t(std::move(f)), [](void* f_) { @@ -118,7 +120,7 @@ struct main_executor_type { using result_type = void; template - auto operator()(F&& f) const -> std::enable_if_t { + auto operator()(F&& f) const -> std::enable_if_t::value> { using function_type = typename std::remove_reference::type; auto p = new function_type(std::forward(f)); diff --git a/stlab/concurrency/serial_queue.hpp b/stlab/concurrency/serial_queue.hpp index 2ca57a66..1af9de82 100644 --- a/stlab/concurrency/serial_queue.hpp +++ b/stlab/concurrency/serial_queue.hpp @@ -18,6 +18,7 @@ #include #include +#include #ifndef STLAB_DISABLE_FUTURE_COROUTINES #define STLAB_DISABLE_FUTURE_COROUTINES() @@ -150,7 +151,7 @@ class serial_queue_t { [_e = std::move(e)](auto&& f) { _e(std::forward(f)); }, mode)) {} auto executor() const { - return [_impl = _impl](auto&& f) -> std::enable_if_t { + return [_impl = _impl](auto&& f) -> std::enable_if_t::value)> { _impl->enqueue(std::forward(f)); }; } diff --git a/stlab/concurrency/system_timer.hpp b/stlab/concurrency/system_timer.hpp index 1a462100..c6efc97a 100755 --- a/stlab/concurrency/system_timer.hpp +++ b/stlab/concurrency/system_timer.hpp @@ -31,6 +31,8 @@ #include +#include + /**************************************************************************************************/ namespace stlab { @@ -59,7 +61,7 @@ struct system_timer_type { template > auto operator()(std::chrono::duration duration, F f) const - -> std::enable_if_t { + -> std::enable_if_t::value> { using namespace std::chrono; using f_t = decltype(f); @@ -111,7 +113,7 @@ class system_timer { template > auto operator()(std::chrono::duration duration, F&& f) - -> std::enable_if_t { + -> std::enable_if_t::value> { using namespace std::chrono; auto timer = CreateThreadpoolTimer(&timer_callback_impl, new F(std::forward(f)), &_callBackEnvironment); @@ -229,7 +231,7 @@ class system_timer { template > auto operator()(std::chrono::duration duration, F&& f) - ->std::enable_if_t { + -> std::enable_if_t::value> { lock_t lock(_timed_queue_mutex); _timed_queue.emplace_back(std::chrono::steady_clock::now() + duration, std::forward(f)); std::push_heap(std::begin(_timed_queue), std::end(_timed_queue), greater_first()); diff --git a/stlab/concurrency/task.hpp b/stlab/concurrency/task.hpp index 811587f2..8fcf99d0 100644 --- a/stlab/concurrency/task.hpp +++ b/stlab/concurrency/task.hpp @@ -37,8 +37,11 @@ inline namespace v1 { template class task; -template -class task { +template +class task {}; + +template +class task { template constexpr static bool maybe_empty = std::is_pointer>::value || std::is_member_pointer>::value || diff --git a/stlab/type_traits.hpp b/stlab/type_traits.hpp new file mode 100644 index 00000000..5933adb8 --- /dev/null +++ b/stlab/type_traits.hpp @@ -0,0 +1,41 @@ +/* + Copyright 2015 Adobe + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +*/ + +/**************************************************************************************************/ + +#ifndef STLAB_TYPE_TRAITS_HPP +#define STLAB_TYPE_TRAITS_HPP + +#include +#include +#include + +/** + @brief This file contains type traits included in versions of C++ later than 14. Only the + minimal items used are included. They are not conditional on the C++ version to avoid ODR + violations. + */ + +namespace stlab { + +inline namespace v1 { + +/** + @brief See std::is_invocable + + This is necessary for GCC prior to 13.2 which does not implement mangling noexcept_expr. + */ + +template +struct is_nothrow_invocable { + static constexpr bool value = noexcept(std::invoke(std::declval(), std::declval()...)); +}; + +} // namespace v1 + +} // namespace stlab + +#endif // STLAB_TYPE_TRAITS_HPP From d26abdab56c116ff625e1b51484c14b8a1030993 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Wed, 16 Aug 2023 16:00:00 -0700 Subject: [PATCH 07/17] Reverting accidental save. --- stlab/concurrency/task.hpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/stlab/concurrency/task.hpp b/stlab/concurrency/task.hpp index 8fcf99d0..811587f2 100644 --- a/stlab/concurrency/task.hpp +++ b/stlab/concurrency/task.hpp @@ -37,11 +37,8 @@ inline namespace v1 { template class task; -template -class task {}; - -template -class task { +template +class task { template constexpr static bool maybe_empty = std::is_pointer>::value || std::is_member_pointer>::value || From 3e113b2478db7169cec6ef5e15c463d4f8592af6 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Wed, 16 Aug 2023 16:35:28 -0700 Subject: [PATCH 08/17] Fixing task for MSVC (can't deduce noexcept). --- stlab/concurrency/default_executor.hpp | 2 +- stlab/concurrency/serial_queue.hpp | 3 +- stlab/concurrency/task.hpp | 143 +++++++++++++++++-------- 3 files changed, 104 insertions(+), 44 deletions(-) diff --git a/stlab/concurrency/default_executor.hpp b/stlab/concurrency/default_executor.hpp index 3f60c23a..e85f2778 100644 --- a/stlab/concurrency/default_executor.hpp +++ b/stlab/concurrency/default_executor.hpp @@ -94,7 +94,7 @@ struct executor_type { using result_type = void; template - auto operator()(F f) const -> std::enable_if_t> { + auto operator()(F f) const -> std::enable_if_t::value> { using f_t = decltype(f); dispatch_group_async_f(detail::group()._group, diff --git a/stlab/concurrency/serial_queue.hpp b/stlab/concurrency/serial_queue.hpp index 1af9de82..f580737d 100644 --- a/stlab/concurrency/serial_queue.hpp +++ b/stlab/concurrency/serial_queue.hpp @@ -151,7 +151,8 @@ class serial_queue_t { [_e = std::move(e)](auto&& f) { _e(std::forward(f)); }, mode)) {} auto executor() const { - return [_impl = _impl](auto&& f) -> std::enable_if_t::value)> { + return [_impl = _impl]( + auto&& f) -> std::enable_if_t::value> { _impl->enqueue(std::forward(f)); }; } diff --git a/stlab/concurrency/task.hpp b/stlab/concurrency/task.hpp index 811587f2..0f2a1c5f 100644 --- a/stlab/concurrency/task.hpp +++ b/stlab/concurrency/task.hpp @@ -30,15 +30,12 @@ inline namespace v1 { /**************************************************************************************************/ -/* - tasks are functions with a mutable call operator to support moving items through for single - invocations. -*/ -template -class task; +namespace detail { -template -class task { +/**************************************************************************************************/ + +template +class task_base { template constexpr static bool maybe_empty = std::is_pointer>::value || std::is_member_pointer>::value || @@ -192,17 +189,17 @@ class task { public: using result_type = R; - constexpr task() noexcept = default; - constexpr task(std::nullptr_t) noexcept : task() {} - task(const task&) = delete; - task(task&& x) noexcept : _vtable_ptr(x._vtable_ptr), _invoke(x._invoke) { + constexpr task_base() noexcept = default; + constexpr task_base(std::nullptr_t) noexcept : task_base() {} + task_base(const task_base&) = delete; + task_base(task_base&& x) noexcept : _vtable_ptr(x._vtable_ptr), _invoke(x._invoke) { _vtable_ptr->move_ctor(&x._model, &_model); } template ()(std::declval()...)), bool> = true> - task(F&& f) { + task_base(F&& f) { using small_t = model, true>; using large_t = model, false>; using model_t = std::conditional_t<(sizeof(small_t) <= small_size) && @@ -216,11 +213,11 @@ class task { _invoke = &model_t::invoke; } - ~task() { _vtable_ptr->dtor(&_model); }; + ~task_base() { _vtable_ptr->dtor(&_model); }; - task& operator=(const task&) = delete; + task_base& operator=(const task_base&) = delete; - task& operator=(task&& x) noexcept { + task_base& operator=(task_base&& x) noexcept { _vtable_ptr->dtor(&_model); _vtable_ptr = x._vtable_ptr; _invoke = x._invoke; @@ -228,15 +225,15 @@ class task { return *this; } - task& operator=(std::nullptr_t) noexcept { return *this = task(); } + task_base& operator=(std::nullptr_t) noexcept { return *this = task_base(); } template auto operator=(F&& f) - -> std::enable_if_t()...)), task&> { - return *this = task(std::forward(f)); + -> std::enable_if_t()...)), task_base&> { + return *this = task_base(std::forward(f)); } - void swap(task& x) noexcept { std::swap(*this, x); } + void swap(task_base& x) noexcept { std::swap(*this, x); } explicit operator bool() const { return _vtable_ptr->const_pointer(&_model) != nullptr; } @@ -260,11 +257,18 @@ class task { return _invoke(&_model, std::forward(brgs)...); } - friend inline void swap(task& x, task& y) { return x.swap(y); } - friend inline bool operator==(const task& x, std::nullptr_t) { return !static_cast(x); } - friend inline bool operator==(std::nullptr_t, const task& x) { return !static_cast(x); } - friend inline bool operator!=(const task& x, std::nullptr_t) { return static_cast(x); } - friend inline bool operator!=(std::nullptr_t, const task& x) { return static_cast(x); } + friend inline bool operator==(const task_base& x, std::nullptr_t) { + return !static_cast(x); + } + friend inline bool operator==(std::nullptr_t, const task_base& x) { + return !static_cast(x); + } + friend inline bool operator!=(const task_base& x, std::nullptr_t) { + return static_cast(x); + } + friend inline bool operator!=(std::nullptr_t, const task_base& x) { + return static_cast(x); + } }; #if STLAB_CPP_VERSION_LESS_THAN(17) @@ -273,30 +277,30 @@ class task { #if defined(__GNUC__) && __GNUC__ < 7 && !defined(__clang__) template -const typename task::concept_t - task::_vtable = {dtor, move_ctor, target_type_, pointer, - const_pointer}; +const typename task_base::concept_t + task_base::_vtable = {dtor, move_ctor, target_type_, pointer, + const_pointer}; template -const typename task::invoke_t - task::_invoke = _invoke; +const typename task_base::invoke_t + task_base::_invoke = _invoke; #else template -const typename task::concept_t - task::_vtable; +const typename task_base::concept_t + task_base::_vtable; #endif #ifdef _MSC_VER template template -const typename task::concept_t - task::model::_vtable; +const typename task_base::concept_t + task_base::model::_vtable; template template -const typename task::concept_t - task::model::_vtable; +const typename task_base::concept_t + task_base::model::_vtable; #else @@ -304,23 +308,25 @@ const typename task::concept_t template template -const typename task::concept_t task::template model::_vtable = { - dtor, move_ctor, target_type, pointer, const_pointer}; +const typename task_base::concept_t + task_base::template model::_vtable = {dtor, move_ctor, target_type, + pointer, const_pointer}; template template -const typename task::concept_t task::template model::_vtable = { - dtor, move_ctor, target_type, pointer, const_pointer}; +const typename task_base::concept_t + task_base::template model::_vtable = {dtor, move_ctor, target_type, + pointer, const_pointer}; #else template template -const typename task::concept_t task::model::_vtable; +const typename task_base::concept_t task_base::model::_vtable; template template -const typename task::concept_t task::model::_vtable; +const typename task_base::concept_t task_base::model::_vtable; #endif @@ -330,6 +336,59 @@ const typename task::concept_t task::model::_vt /**************************************************************************************************/ +} // namespace detail + +/**************************************************************************************************/ + +template +class task; + +template +class task : detail::task_base { + using base_type = detail::task_base; + +public: + using base_type::base_type; + using base_type::target_type; + using typename base_type::result_type; + using base_type::operator(); + using base_type::operator bool; + using base_type::swap; + using base_type::target; + + friend inline void swap(task& x, task& y) { return x.swap(y); } + friend inline bool operator==(const task& x, std::nullptr_t) { + return static_cast(x) == nullptr; + } + friend inline bool operator==(std::nullptr_t, const task& x) { return x == nullptr; } + friend inline bool operator!=(const task& x, std::nullptr_t) { return !(x == nullptr); } + friend inline bool operator!=(std::nullptr_t, const task& x) { return !(x == nullptr); } +}; + +template +class task : detail::task_base { + using base_type = detail::task_base; + +public: + using base_type::base_type; + using base_type::target_type; + using typename base_type::result_type; + using base_type::operator(); + using base_type::operator bool; + using base_type::swap; + using base_type::target; + + friend inline void swap(task& x, task& y) { return x.swap(y); } + friend inline bool operator==(const task& x, std::nullptr_t) { + return static_cast(x) == nullptr; + } + friend inline bool operator==(std::nullptr_t, const task& x) { return x == nullptr; } + friend inline bool operator!=(const task& x, std::nullptr_t) { return !(x == nullptr); } + friend inline bool operator!=(std::nullptr_t, const task& x) { return !(x == nullptr); } +}; + +/**************************************************************************************************/ + } // namespace v1 /**************************************************************************************************/ From 949c359b82dfe3f227056d94eea4abb83de2a9ee Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Wed, 16 Aug 2023 17:07:46 -0700 Subject: [PATCH 09/17] Fixing range based for loop issue with GCC. --- test/forest_test.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/test/forest_test.cpp b/test/forest_test.cpp index cb3f5a12..32487aed 100644 --- a/test/forest_test.cpp +++ b/test/forest_test.cpp @@ -31,7 +31,8 @@ void print(const forest& f) { --depth; } - for (std::size_t i{0}; i < depth; ++i) std::cout << " "; + for (std::size_t i{0}; i < depth; ++i) + std::cout << " "; if (is_leading(first)) { std::cout << "<"; @@ -174,8 +175,7 @@ auto test_edge_traversal(Forest& f, Iterator fi, Iterator li) { Iterator first{fi}; Iterator last{li}; while (first != last) { - if (first.edge() == Edge) - expected += *first; + if (first.edge() == Edge) expected += *first; ++first; } BOOST_CHECK(expected.size() == f.size()); @@ -250,13 +250,15 @@ BOOST_AUTO_TEST_CASE(reverse_traversal) { /*preorder*/ { auto a = test_edge_traversal(f, rfirst, rlast); - auto b = test_edge_traversal(f, rfirst, rlast); + auto b = + test_edge_traversal(f, rfirst, rlast); BOOST_CHECK(a == b); } /*postorder*/ { auto a = test_edge_traversal(f, rfirst, rlast); - auto b = test_edge_traversal(f, rfirst, rlast); + auto b = + test_edge_traversal(f, rfirst, rlast); BOOST_CHECK(a == b); } } @@ -265,7 +267,7 @@ BOOST_AUTO_TEST_CASE(reverse_traversal) { template auto find_node(Forest& f, const T& x) { - return std::find_if(f.begin(), f.end(), [&](const auto& v){ return v == x; }); + return std::find_if(f.begin(), f.end(), [&](const auto& v) { return v == x; }); } /**************************************************************************************************/ @@ -285,13 +287,13 @@ BOOST_AUTO_TEST_CASE(child_traversal) { BOOST_CHECK(to_string(child_range(parent)) == expected); - #if 0 +#if 0 // I'm not sure reverse_child_iterator ever worked. forest::reverse_child_iterator first{child_begin(parent)}; forest::reverse_child_iterator last{child_end(parent)}; std::string result{to_string(first, last)}; BOOST_CHECK(result == expected); - #endif +#endif } /**************************************************************************************************/ @@ -432,8 +434,9 @@ BOOST_AUTO_TEST_CASE(swap) { BOOST_AUTO_TEST_CASE(test_equal_shape) { auto f1{big_test_forest()}; auto f2{f1}; - - for (auto& x : preorder_range(f2)) { + + auto r{preorder_range(f2)}; + for (auto& x : r) { x = "X"; } @@ -448,7 +451,7 @@ BOOST_AUTO_TEST_CASE(test_transcribe_forest) { auto f1{big_test_forest()}; stlab::forest f2; - forests::transcribe(f1, forests::transcriber(f2), [](const std::string& x){ + forests::transcribe(f1, forests::transcriber(f2), [](const std::string& x) { assert(!x.empty()); return static_cast(x.front()); }); From 7162c07cb22cf4b46ddb6e96453872fee05acec2 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Sat, 19 Aug 2023 08:32:45 -0700 Subject: [PATCH 10/17] Formatting forest.hpp --- stlab/forest.hpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/stlab/forest.hpp b/stlab/forest.hpp index 3c749474..f4ec69f9 100755 --- a/stlab/forest.hpp +++ b/stlab/forest.hpp @@ -60,18 +60,14 @@ auto trailing_of(I i) { /**************************************************************************************************/ -constexpr auto is_leading(forest_edge e) { - return e == forest_edge::leading; -} +constexpr auto is_leading(forest_edge e) { return e == forest_edge::leading; } template // I models a FullorderIterator auto is_leading(const I& i) { return is_leading(i.edge()); } -constexpr auto is_trailing(forest_edge e) { - return e == forest_edge::trailing; -} +constexpr auto is_trailing(forest_edge e) { return e == forest_edge::trailing; } template // I models a FullorderIterator auto is_trailing(const I& i) { @@ -779,12 +775,10 @@ class forest { insert(end(), const_child_iterator(x.begin()), const_child_iterator(x.end())); } forest(forest&& x) noexcept : forest() { splice(end(), x); } - forest& operator=(const forest& x) { - return *this = forest(x); - } + forest& operator=(const forest& x) { return *this = forest(x); } forest& operator=(forest&& x) noexcept { auto tmp{std::move(x)}; // this is `release()` - clear(); // these two lines are `reset()` + clear(); // these two lines are `reset()` splice(end(), tmp); return *this; } From 2a56104a37326501d4a4a59a4ad93b7cc2d2a3e6 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Sat, 19 Aug 2023 10:26:06 -0700 Subject: [PATCH 11/17] Revert "Fixing task for MSVC (can't deduce noexcept)." This reverts commit 3e113b2478db7169cec6ef5e15c463d4f8592af6. --- stlab/concurrency/task.hpp | 143 +++++++++++-------------------------- 1 file changed, 42 insertions(+), 101 deletions(-) diff --git a/stlab/concurrency/task.hpp b/stlab/concurrency/task.hpp index 0f2a1c5f..811587f2 100644 --- a/stlab/concurrency/task.hpp +++ b/stlab/concurrency/task.hpp @@ -30,12 +30,15 @@ inline namespace v1 { /**************************************************************************************************/ -namespace detail { - -/**************************************************************************************************/ +/* + tasks are functions with a mutable call operator to support moving items through for single + invocations. +*/ +template +class task; -template -class task_base { +template +class task { template constexpr static bool maybe_empty = std::is_pointer>::value || std::is_member_pointer>::value || @@ -189,17 +192,17 @@ class task_base { public: using result_type = R; - constexpr task_base() noexcept = default; - constexpr task_base(std::nullptr_t) noexcept : task_base() {} - task_base(const task_base&) = delete; - task_base(task_base&& x) noexcept : _vtable_ptr(x._vtable_ptr), _invoke(x._invoke) { + constexpr task() noexcept = default; + constexpr task(std::nullptr_t) noexcept : task() {} + task(const task&) = delete; + task(task&& x) noexcept : _vtable_ptr(x._vtable_ptr), _invoke(x._invoke) { _vtable_ptr->move_ctor(&x._model, &_model); } template ()(std::declval()...)), bool> = true> - task_base(F&& f) { + task(F&& f) { using small_t = model, true>; using large_t = model, false>; using model_t = std::conditional_t<(sizeof(small_t) <= small_size) && @@ -213,11 +216,11 @@ class task_base { _invoke = &model_t::invoke; } - ~task_base() { _vtable_ptr->dtor(&_model); }; + ~task() { _vtable_ptr->dtor(&_model); }; - task_base& operator=(const task_base&) = delete; + task& operator=(const task&) = delete; - task_base& operator=(task_base&& x) noexcept { + task& operator=(task&& x) noexcept { _vtable_ptr->dtor(&_model); _vtable_ptr = x._vtable_ptr; _invoke = x._invoke; @@ -225,15 +228,15 @@ class task_base { return *this; } - task_base& operator=(std::nullptr_t) noexcept { return *this = task_base(); } + task& operator=(std::nullptr_t) noexcept { return *this = task(); } template auto operator=(F&& f) - -> std::enable_if_t()...)), task_base&> { - return *this = task_base(std::forward(f)); + -> std::enable_if_t()...)), task&> { + return *this = task(std::forward(f)); } - void swap(task_base& x) noexcept { std::swap(*this, x); } + void swap(task& x) noexcept { std::swap(*this, x); } explicit operator bool() const { return _vtable_ptr->const_pointer(&_model) != nullptr; } @@ -257,18 +260,11 @@ class task_base { return _invoke(&_model, std::forward(brgs)...); } - friend inline bool operator==(const task_base& x, std::nullptr_t) { - return !static_cast(x); - } - friend inline bool operator==(std::nullptr_t, const task_base& x) { - return !static_cast(x); - } - friend inline bool operator!=(const task_base& x, std::nullptr_t) { - return static_cast(x); - } - friend inline bool operator!=(std::nullptr_t, const task_base& x) { - return static_cast(x); - } + friend inline void swap(task& x, task& y) { return x.swap(y); } + friend inline bool operator==(const task& x, std::nullptr_t) { return !static_cast(x); } + friend inline bool operator==(std::nullptr_t, const task& x) { return !static_cast(x); } + friend inline bool operator!=(const task& x, std::nullptr_t) { return static_cast(x); } + friend inline bool operator!=(std::nullptr_t, const task& x) { return static_cast(x); } }; #if STLAB_CPP_VERSION_LESS_THAN(17) @@ -277,30 +273,30 @@ class task_base { #if defined(__GNUC__) && __GNUC__ < 7 && !defined(__clang__) template -const typename task_base::concept_t - task_base::_vtable = {dtor, move_ctor, target_type_, pointer, - const_pointer}; +const typename task::concept_t + task::_vtable = {dtor, move_ctor, target_type_, pointer, + const_pointer}; template -const typename task_base::invoke_t - task_base::_invoke = _invoke; +const typename task::invoke_t + task::_invoke = _invoke; #else template -const typename task_base::concept_t - task_base::_vtable; +const typename task::concept_t + task::_vtable; #endif #ifdef _MSC_VER template template -const typename task_base::concept_t - task_base::model::_vtable; +const typename task::concept_t + task::model::_vtable; template template -const typename task_base::concept_t - task_base::model::_vtable; +const typename task::concept_t + task::model::_vtable; #else @@ -308,25 +304,23 @@ const typename task_base::concept_t template template -const typename task_base::concept_t - task_base::template model::_vtable = {dtor, move_ctor, target_type, - pointer, const_pointer}; +const typename task::concept_t task::template model::_vtable = { + dtor, move_ctor, target_type, pointer, const_pointer}; template template -const typename task_base::concept_t - task_base::template model::_vtable = {dtor, move_ctor, target_type, - pointer, const_pointer}; +const typename task::concept_t task::template model::_vtable = { + dtor, move_ctor, target_type, pointer, const_pointer}; #else template template -const typename task_base::concept_t task_base::model::_vtable; +const typename task::concept_t task::model::_vtable; template template -const typename task_base::concept_t task_base::model::_vtable; +const typename task::concept_t task::model::_vtable; #endif @@ -336,59 +330,6 @@ const typename task_base::concept_t task_base::model -class task; - -template -class task : detail::task_base { - using base_type = detail::task_base; - -public: - using base_type::base_type; - using base_type::target_type; - using typename base_type::result_type; - using base_type::operator(); - using base_type::operator bool; - using base_type::swap; - using base_type::target; - - friend inline void swap(task& x, task& y) { return x.swap(y); } - friend inline bool operator==(const task& x, std::nullptr_t) { - return static_cast(x) == nullptr; - } - friend inline bool operator==(std::nullptr_t, const task& x) { return x == nullptr; } - friend inline bool operator!=(const task& x, std::nullptr_t) { return !(x == nullptr); } - friend inline bool operator!=(std::nullptr_t, const task& x) { return !(x == nullptr); } -}; - -template -class task : detail::task_base { - using base_type = detail::task_base; - -public: - using base_type::base_type; - using base_type::target_type; - using typename base_type::result_type; - using base_type::operator(); - using base_type::operator bool; - using base_type::swap; - using base_type::target; - - friend inline void swap(task& x, task& y) { return x.swap(y); } - friend inline bool operator==(const task& x, std::nullptr_t) { - return static_cast(x) == nullptr; - } - friend inline bool operator==(std::nullptr_t, const task& x) { return x == nullptr; } - friend inline bool operator!=(const task& x, std::nullptr_t) { return !(x == nullptr); } - friend inline bool operator!=(std::nullptr_t, const task& x) { return !(x == nullptr); } -}; - -/**************************************************************************************************/ - } // namespace v1 /**************************************************************************************************/ From 0fdb98d55da607fbcadc5ba10b9c52c118e6dc96 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Sat, 19 Aug 2023 10:50:35 -0700 Subject: [PATCH 12/17] Another approach to deduce noexcept. --- .github/workflows/stlab.yml | 6 ++-- stlab/concurrency/task.hpp | 60 ++++++++++++++++++++++++------------- test/forest_test.cpp | 3 +- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/.github/workflows/stlab.yml b/.github/workflows/stlab.yml index 7f7712ca..26d31c95 100644 --- a/.github/workflows/stlab.yml +++ b/.github/workflows/stlab.yml @@ -4,7 +4,7 @@ on: pull_request: push: branches: - - main + - main jobs: generate-matrix: @@ -67,7 +67,7 @@ jobs: ./emsdk install latest ./emsdk activate latest echo 'source "$HOME/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile - + # Override Emsdk's bundled node (14.18.2) to the GH Actions system installation (>= 16.16.0) sed -i "/^NODE_JS = .*/c\NODE_JS = '`which node`'" .emscripten echo "Overwrote .emscripten config file to:" @@ -122,7 +122,7 @@ jobs: run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 mkdir ..\build - cmake -S. -B../build -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=23 -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake + cmake -S. -B../build -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake - name: Build // Unix if: ${{ startsWith(matrix.config.os, 'ubuntu') || startsWith(matrix.config.os, 'macos') }} diff --git a/stlab/concurrency/task.hpp b/stlab/concurrency/task.hpp index 811587f2..a546a0df 100644 --- a/stlab/concurrency/task.hpp +++ b/stlab/concurrency/task.hpp @@ -34,11 +34,9 @@ inline namespace v1 { tasks are functions with a mutable call operator to support moving items through for single invocations. */ -template -class task; -template -class task { +template +class task_ { template constexpr static bool maybe_empty = std::is_pointer>::value || std::is_member_pointer>::value || @@ -192,17 +190,17 @@ class task { public: using result_type = R; - constexpr task() noexcept = default; - constexpr task(std::nullptr_t) noexcept : task() {} - task(const task&) = delete; - task(task&& x) noexcept : _vtable_ptr(x._vtable_ptr), _invoke(x._invoke) { + constexpr task_() noexcept = default; + constexpr task_(std::nullptr_t) noexcept : task_() {} + task_(const task_&) = delete; + task_(task_&& x) noexcept : _vtable_ptr(x._vtable_ptr), _invoke(x._invoke) { _vtable_ptr->move_ctor(&x._model, &_model); } template ()(std::declval()...)), bool> = true> - task(F&& f) { + task_(F&& f) { using small_t = model, true>; using large_t = model, false>; using model_t = std::conditional_t<(sizeof(small_t) <= small_size) && @@ -216,11 +214,11 @@ class task { _invoke = &model_t::invoke; } - ~task() { _vtable_ptr->dtor(&_model); }; + ~task_() { _vtable_ptr->dtor(&_model); }; - task& operator=(const task&) = delete; + task_& operator=(const task_&) = delete; - task& operator=(task&& x) noexcept { + task_& operator=(task_&& x) noexcept { _vtable_ptr->dtor(&_model); _vtable_ptr = x._vtable_ptr; _invoke = x._invoke; @@ -228,15 +226,15 @@ class task { return *this; } - task& operator=(std::nullptr_t) noexcept { return *this = task(); } + task_& operator=(std::nullptr_t) noexcept { return *this = task_(); } template auto operator=(F&& f) - -> std::enable_if_t()...)), task&> { - return *this = task(std::forward(f)); + -> std::enable_if_t()...)), task_&> { + return *this = task_(std::forward(f)); } - void swap(task& x) noexcept { std::swap(*this, x); } + void swap(task_& x) noexcept { std::swap(*this, x); } explicit operator bool() const { return _vtable_ptr->const_pointer(&_model) != nullptr; } @@ -260,13 +258,33 @@ class task { return _invoke(&_model, std::forward(brgs)...); } - friend inline void swap(task& x, task& y) { return x.swap(y); } - friend inline bool operator==(const task& x, std::nullptr_t) { return !static_cast(x); } - friend inline bool operator==(std::nullptr_t, const task& x) { return !static_cast(x); } - friend inline bool operator!=(const task& x, std::nullptr_t) { return static_cast(x); } - friend inline bool operator!=(std::nullptr_t, const task& x) { return static_cast(x); } + friend inline void swap(task_& x, task_& y) { return x.swap(y); } + friend inline bool operator==(const task_& x, std::nullptr_t) { return !static_cast(x); } + friend inline bool operator==(std::nullptr_t, const task_& x) { return !static_cast(x); } + friend inline bool operator!=(const task_& x, std::nullptr_t) { return static_cast(x); } + friend inline bool operator!=(std::nullptr_t, const task_& x) { return static_cast(x); } +}; + +/**************************************************************************************************/ + +template