libsemigroups  v3.0.0
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
runner.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2019-2025 James D. Mitchell
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17//
18
19// This file contains two classes Runner and Race for competitively running
20// different functions/member functions in different threads, and obtaining the
21// winner.
22
23#ifndef LIBSEMIGROUPS_RUNNER_HPP_
24#define LIBSEMIGROUPS_RUNNER_HPP_
25
26#include <algorithm> // for max
27#include <array> // for array
28#include <atomic> // for atomic
29#include <chrono> // for time_point, nanoseconds
30#include <cstddef> // for size_t
31#include <functional> // for function
32#include <memory> // for unique_ptr
33#include <mutex> // for mutex
34#include <string> // for basic_string, string
35#include <string_view> // for basic_string_view
36#include <thread> // for sleep_for, thread
37#include <tuple> // for apply
38#include <utility> // for move, forward
39#include <vector> // for vector
40
41#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
42
43#include "detail/function-ref.hpp" // for FunctionRef
44#include "detail/report.hpp" // for report_default
45#include "detail/string.hpp" // for unicode_string_length
46
47namespace libsemigroups {
48
54
63 // Not noexcept because std::chrono::duration_cast isn't
64 [[nodiscard]] static inline std::chrono::nanoseconds
65 delta(std::chrono::high_resolution_clock::time_point const& t) {
68 return duration_cast<std::chrono::nanoseconds>(high_resolution_clock::now()
69 - t);
70 }
71
80
92 class Reporter {
93 public:
95 using time_point = std::chrono::high_resolution_clock::time_point;
98
99 private:
100 std::string _divider;
101 std::string _prefix;
102 nanoseconds _report_time_interval;
103
104 mutable std::atomic<time_point> _last_report;
105 mutable time_point _start_time;
106
107 public:
115 // not noexcept because std::string constructor isn't
117
129
131 Reporter(Reporter const& that);
132
135
138
141
142 ~Reporter() = default;
143
161 // not noexcept because operator- for time_points can throw.
162 // TODO(later) remove? Used by Action
163 [[nodiscard]] bool report() const;
164
186 _report_time_interval = val;
187 return *this;
188 }
189
208 template <typename Time>
209 Reporter& report_every(Time t) noexcept {
210 return report_every(nanoseconds(t));
211 }
212
222 [[nodiscard]] nanoseconds report_every() const noexcept {
223 return _report_time_interval;
224 }
225
238 [[nodiscard]] time_point start_time() const noexcept {
239 return _start_time;
240 }
241
247 // Not sure if this is noexcept or not,
248 // std::chrono::high_resolution_clock::now is noexcept, but JDM couldn't
249 // find the noexcept spec of the copy assignment operator of a time_point.
250 Reporter const& reset_start_time() const {
252 _start_time = _last_report;
253 return *this;
254 }
255
269 [[nodiscard]] time_point last_report() const noexcept {
270 return _last_report;
271 }
272
283 // Not sure if this is noexcept or not,
284 // std::chrono::high_resolution_clock::now is noexcept, but JDM couldn't
285 // find the noexcept spec of the copy assignment operator of a time_point.
288 return *this;
289 }
290
302 // Not noexcept because std::string::operator= isn't
304 _prefix = val;
305 return *this;
306 }
307
320 [[nodiscard]] std::string const& report_prefix() const noexcept {
321 return _prefix;
322 }
323
324 Reporter& report_divider(std::string const& val) {
325 _divider = val;
326 return *this;
327 }
328
329 [[nodiscard]] std::string const& report_divider() const noexcept {
330 return _divider;
331 }
332
333 void emit_divider() {
334 if (!_divider.empty()) {
335 report_no_prefix(_divider, "");
336 }
337 }
338 };
339
359 class Runner : public Reporter {
360 public:
388
389 private:
391 // Runner - data - private
393
395 mutable std::atomic<state> _state;
396 detail::FunctionRef<bool(void)> _stopper;
397
398 public:
400 // Runner - constructors + destructor - public
402
409 // not noexcept because Reporter() isn't
411
423
431 Runner(Runner const& other);
432
440 Runner(Runner&& other);
441
449 Runner& operator=(Runner const& other);
450
459
460 virtual ~Runner() = default;
461
463 // Runner - non-virtual member functions - public
465
466 // The following mem fns don't return a reference to Runner to avoid the
467 // unexpected behaviour of e.g. Sims1.run_for() returning a reference to not
468 // a Sims1.
469
474 // At the end of this either finished, or dead.
475 void run();
476
486 // At the end of this either finished, dead, or timed_out.
488
500 template <typename Time>
501 void run_for(Time t) {
503 }
504
512 // not noexcept because operator-(time_point, time_point) isn't
513 [[nodiscard]] inline bool timed_out() const {
514 return (running_for() ? delta(start_time()) >= _run_for
516 }
517
523 // At the end of this either finished, dead, or stopped_by_predicate.
524 template <typename Func>
525 void run_until(Func&& func);
526
530 void run_until(bool (*func)()) {
531 run_until(detail::FunctionRef<bool(void)>(func));
532 }
533
539 // not noexcept because it calls timed_out which is not noexcept
541
553 // Not noexcept because finished_impl isn't
554 [[nodiscard]] bool finished() const;
555
562 [[nodiscard]] virtual bool success() const {
563 return finished();
564 }
565
578 [[nodiscard]] bool started() const noexcept {
580 }
581
594 [[nodiscard]] bool running() const noexcept {
598 }
599
610 void kill() noexcept {
611 set_state(state::dead);
612 }
613
626 [[nodiscard]] bool dead() const noexcept {
627 return current_state() == state::dead;
628 }
629
639 // not noexcept because timed_out isn't
640 [[nodiscard]] bool stopped() const {
641 return (running() ? (timed_out() || stopped_by_predicate())
643 }
644
661 // noexcept should depend on whether _stopper can throw or not
662 [[nodiscard]] inline bool stopped_by_predicate() const {
663 if (running_until()) {
664 LIBSEMIGROUPS_ASSERT(_stopper.valid());
665 return _stopper();
666 } else {
668 }
669 }
670
686 [[nodiscard]] bool running_for() const noexcept {
687 return _state == state::running_for;
688 }
689
705 [[nodiscard]] bool running_until() const noexcept {
706 return _state == state::running_until;
707 }
708
721 [[nodiscard]] state current_state() const noexcept {
722 return _state;
723 }
724
725 private:
726 virtual void run_impl() = 0;
727 [[nodiscard]] virtual bool finished_impl() const = 0;
728 [[nodiscard]] virtual bool success_impl() const {
729 return finished();
730 }
731
732 void set_state(state stt) const noexcept {
733 // We can set the state back to never_run if run_impl throws, and we are
734 // restoring the old state.
735 if (!dead()) {
736 // It can be that *this* becomes dead after this function has been
737 // called.
738 _state = stt;
739 }
740 }
741 }; // class Runner
742} // namespace libsemigroups
743
744#include "runner.tpp"
745#endif // LIBSEMIGROUPS_RUNNER_HPP_
Collection of values related to reporting.
Definition runner.hpp:92
Reporter(Reporter &&that)
Default move constructor.
time_point last_report() const noexcept
Get the time point of the last report.
Definition runner.hpp:269
bool report() const
Check if it is time to report.
Reporter & operator=(Reporter &&that)
Default move assignment operator.
nanoseconds report_every() const noexcept
Get the minimum elapsed time between reports.
Definition runner.hpp:222
Reporter const & reset_start_time() const
Reset the start time (and last report) to now.
Definition runner.hpp:250
Reporter & report_every(Time t) noexcept
Set the minimum elapsed time between reports in a unit of time other than nanoseconds.
Definition runner.hpp:209
Reporter & init()
Initialize an existing Reporter object.
Reporter & operator=(Reporter const &that)
Default copy assignment operator.
Reporter const & reset_last_report() const
Set the last report time point to now.
Definition runner.hpp:286
std::chrono::high_resolution_clock::time_point time_point
Alias for std::chrono::high_resolution_clock::time_point.
Definition runner.hpp:95
std::string const & report_prefix() const noexcept
Get the current prefix string for reporting.
Definition runner.hpp:320
Reporter & report_prefix(std::string const &val)
Set the prefix string for reporting.
Definition runner.hpp:303
static std::chrono::nanoseconds delta(std::chrono::high_resolution_clock::time_point const &t)
The time between a given point and now.
Definition runner.hpp:65
Reporter()
Default constructor.
Reporter(Reporter const &that)
Default copy constructor.
std::chrono::nanoseconds nanoseconds
Alias for std::chrono::nanoseconds.
Definition runner.hpp:97
Reporter & report_every(nanoseconds val) noexcept
Set the minimum elapsed time between reports in nanoseconds.
Definition runner.hpp:184
time_point start_time() const noexcept
Get the start time.
Definition runner.hpp:238
void run()
Run until finished.
bool started() const noexcept
Check if run has been called at least once before.
Definition runner.hpp:578
void run_until(Func &&func)
Run until a nullary predicate returns true or finished.
void run_until(bool(*func)())
Run until a nullary predicate returns true or finished.
Definition runner.hpp:530
Runner & operator=(Runner &&other)
Move assignment operator.
void report_why_we_stopped() const
Report why run stopped.
bool stopped() const
Check if the runner is stopped.
Definition runner.hpp:640
bool dead() const noexcept
Check if the runner is dead.
Definition runner.hpp:626
void run_for(Time t)
Run for a specified amount of time.
Definition runner.hpp:501
Runner()
Default constructor.
bool running_until() const noexcept
Check if the runner is currently running until a nullary predicate returns true.
Definition runner.hpp:705
Runner & operator=(Runner const &other)
Copy assignment operator.
virtual bool success() const
Check if run has been run to completion successfully.
Definition runner.hpp:562
Runner(Runner &&other)
Move constructor.
Runner & init()
Initialize an existing Runner object.
bool running() const noexcept
Check if currently running.
Definition runner.hpp:594
bool timed_out() const
Check if the amount of time passed to run_for has elapsed.
Definition runner.hpp:513
void run_for(std::chrono::nanoseconds t)
Run for a specified amount of time.
state
Enum class for the state of the Runner.
Definition runner.hpp:362
@ running_for
Definition runner.hpp:371
@ not_running
Definition runner.hpp:384
@ timed_out
Definition runner.hpp:377
@ stopped_by_predicate
Definition runner.hpp:380
@ running_to_finish
Definition runner.hpp:368
@ never_run
Definition runner.hpp:365
@ running_until
Definition runner.hpp:374
@ dead
Indicates that the Runner was killed (by another thread).
Definition runner.hpp:386
state current_state() const noexcept
Return the current state.
Definition runner.hpp:721
bool running_for() const noexcept
Check if the runner is currently running for a particular length of time.
Definition runner.hpp:686
bool finished() const
Check if run has been run to completion or not.
bool stopped_by_predicate() const
Check if the runner was stopped, or should stop, because of the argument last passed to run_until.
Definition runner.hpp:662
void kill() noexcept
Stop run from running (thread-safe).
Definition runner.hpp:610
Runner(Runner const &other)
Copy constructor.
T duration_cast(T... args)
constexpr std::chrono::nanoseconds FOREVER
Value indicating forever (system dependent but possibly approx. 292 years).
Definition runner.hpp:79
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
void report_no_prefix(std::string_view sv, Args &&... args)
No doc.
Definition report.hpp:146