libsemigroups  v3.1.2
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
59
68 // Not noexcept because std::chrono::duration_cast isn't
69 [[nodiscard]] static inline std::chrono::nanoseconds
70 delta(std::chrono::high_resolution_clock::time_point const& t) {
73 return duration_cast<std::chrono::nanoseconds>(high_resolution_clock::now()
74 - t);
75 }
76
85
97 class Reporter {
98 public:
100 using time_point = std::chrono::high_resolution_clock::time_point;
103
104 private:
105 std::string _divider;
106 std::string _prefix;
107 nanoseconds _report_time_interval;
108
109 mutable std::atomic<time_point> _last_report;
110 mutable time_point _start_time;
111
112 public:
120 // not noexcept because std::string constructor isn't
122
134
136 Reporter(Reporter const& that);
137
140
143
146
147 ~Reporter() = default;
148
166 // not noexcept because operator- for time_points can throw.
167 // TODO(later) remove? Used by Action
168 [[nodiscard]] bool report() const;
169
191 _report_time_interval = val;
192 return *this;
193 }
194
213 template <typename Time>
214 Reporter& report_every(Time t) noexcept {
215 return report_every(nanoseconds(t));
216 }
217
227 [[nodiscard]] nanoseconds report_every() const noexcept {
228 return _report_time_interval;
229 }
230
243 [[nodiscard]] time_point start_time() const noexcept {
244 return _start_time;
245 }
246
252 // Not sure if this is noexcept or not,
253 // std::chrono::high_resolution_clock::now is noexcept, but JDM couldn't
254 // find the noexcept spec of the copy assignment operator of a time_point.
255 Reporter const& reset_start_time() const {
257 _start_time = _last_report;
258 return *this;
259 }
260
274 [[nodiscard]] time_point last_report() const noexcept {
275 return _last_report;
276 }
277
288 // Not sure if this is noexcept or not,
289 // std::chrono::high_resolution_clock::now is noexcept, but JDM couldn't
290 // find the noexcept spec of the copy assignment operator of a time_point.
293 return *this;
294 }
295
307 // Not noexcept because std::string::operator= isn't
309 _prefix = val;
310 return *this;
311 }
312
325 [[nodiscard]] std::string const& report_prefix() const noexcept {
326 return _prefix;
327 }
328
339 // Not noexcept because std::string::operator= isn't
341 _divider = val;
342 return *this;
343 }
344
356 [[nodiscard]] std::string const& report_divider() const noexcept {
357 return _divider;
358 }
359
368 if (!_divider.empty()) {
369 report_no_prefix(_divider, "");
370 }
371 }
372 };
373
393 class Runner : public Reporter {
394 public:
422
423 private:
425 // Runner - data - private
427
429 mutable std::atomic<state> _state;
430 detail::FunctionRef<bool(void)> _stopper;
431
432 public:
434 // Runner - constructors + destructor - public
436
443 // not noexcept because Reporter() isn't
445
457
465 Runner(Runner const& other);
466
474 Runner(Runner&& other);
475
483 Runner& operator=(Runner const& other);
484
493
494 virtual ~Runner();
495
497 // Runner - non-virtual member functions - public
499
500 // The following mem fns don't return a reference to Runner to avoid the
501 // unexpected behaviour of e.g. Sims1.run_for() returning a reference to not
502 // a Sims1.
503
508 // At the end of this either finished, or dead.
509 void run();
510
520 // At the end of this either finished, dead, or timed_out.
522
534 template <typename Time>
535 void run_for(Time t) {
537 }
538
546 // not noexcept because operator-(time_point, time_point) isn't
547 [[nodiscard]] inline bool timed_out() const {
548 return (running_for() ? delta(start_time()) >= _run_for
550 }
551
557 // At the end of this either finished, dead, or stopped_by_predicate.
558 template <typename Func>
559 void run_until(Func&& func);
560
564 void run_until(bool (*func)()) {
565 run_until(detail::FunctionRef<bool(void)>(func));
566 }
567
573 // not noexcept because it calls timed_out which is not noexcept
575
587 // Not noexcept because finished_impl isn't
588 [[nodiscard]] bool finished() const;
589
596 [[nodiscard]] virtual bool success() const {
597 return finished();
598 }
599
612 [[nodiscard]] bool started() const noexcept {
614 }
615
628 [[nodiscard]] bool running() const noexcept {
632 }
633
644 void kill() noexcept {
645 set_state(state::dead);
646 }
647
660 [[nodiscard]] bool dead() const noexcept {
661 return current_state() == state::dead;
662 }
663
673 // not noexcept because timed_out isn't
674 [[nodiscard]] bool stopped() const {
675 return (running() ? (timed_out() || stopped_by_predicate())
677 }
678
695 // noexcept should depend on whether _stopper can throw or not
696 [[nodiscard]] inline bool stopped_by_predicate() const {
697 if (running_until()) {
698 LIBSEMIGROUPS_ASSERT(_stopper.valid());
699 return _stopper();
700 } else {
702 }
703 }
704
720 [[nodiscard]] bool running_for() const noexcept {
721 return _state == state::running_for;
722 }
723
737 [[nodiscard]] std::chrono::nanoseconds
738 running_for_how_long() const noexcept {
739 return _run_for;
740 }
741
757 [[nodiscard]] bool running_until() const noexcept {
758 return _state == state::running_until;
759 }
760
773 [[nodiscard]] state current_state() const noexcept {
774 return _state;
775 }
776
777 private:
778 virtual void run_impl() = 0;
779 [[nodiscard]] virtual bool finished_impl() const = 0;
780
781 void set_state(state stt) const noexcept {
782 // We can set the state back to never_run if run_impl throws, and we are
783 // restoring the old state.
784 if (!dead()) {
785 // It can be that *this* becomes dead after this function has been
786 // called.
787 _state = stt;
788 }
789 }
790 }; // class Runner
791} // namespace libsemigroups
792
793#include "runner.tpp"
794#endif // LIBSEMIGROUPS_RUNNER_HPP_
Reporter(Reporter &&that)
Default move constructor.
time_point last_report() const noexcept
Get the time point of the last report.
Definition runner.hpp:274
std::string const & report_divider() const noexcept
Get the current divider string for reporting.
Definition runner.hpp:356
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:227
Reporter const & reset_start_time() const
Reset the start time (and last report) to now.
Definition runner.hpp:255
Reporter & report_every(Time t) noexcept
Set the minimum elapsed time between reports in a unit of time other than nanoseconds.
Definition runner.hpp:214
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:291
std::chrono::high_resolution_clock::time_point time_point
Alias for std::chrono::high_resolution_clock::time_point.
Definition runner.hpp:100
std::string const & report_prefix() const noexcept
Get the current prefix string for reporting.
Definition runner.hpp:325
Reporter & report_prefix(std::string const &val)
Set the prefix string for reporting.
Definition runner.hpp:308
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:70
void emit_divider()
Emits the current divider string for reporting.
Definition runner.hpp:367
Reporter()
Default constructor.
Reporter(Reporter const &that)
Default copy constructor.
std::chrono::nanoseconds nanoseconds
Alias for std::chrono::nanoseconds.
Definition runner.hpp:102
Reporter & report_every(nanoseconds val) noexcept
Set the minimum elapsed time between reports in nanoseconds.
Definition runner.hpp:189
time_point start_time() const noexcept
Get the start time.
Definition runner.hpp:243
Reporter & report_divider(std::string const &val)
Set the divider string for reporting.
Definition runner.hpp:340
void run()
Run until finished.
bool started() const noexcept
Check if run has been called at least once before.
Definition runner.hpp:612
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:564
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:674
bool dead() const noexcept
Check if the runner is dead.
Definition runner.hpp:660
void run_for(Time t)
Run for a specified amount of time.
Definition runner.hpp:535
Runner()
Default constructor.
bool running_until() const noexcept
Check if the runner is currently running until a nullary predicate returns true.
Definition runner.hpp:757
Runner & operator=(Runner const &other)
Copy assignment operator.
virtual bool success() const
Check if run has been run to completion successfully.
Definition runner.hpp:596
Runner(Runner &&other)
Move constructor.
std::chrono::nanoseconds running_for_how_long() const noexcept
Return the last value passed to run_for.
Definition runner.hpp:738
Runner & init()
Initialize an existing Runner object.
bool running() const noexcept
Check if currently running.
Definition runner.hpp:628
bool timed_out() const
Check if the amount of time passed to run_for has elapsed.
Definition runner.hpp:547
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:396
@ running_for
Definition runner.hpp:405
@ not_running
Definition runner.hpp:418
@ timed_out
Definition runner.hpp:411
@ stopped_by_predicate
Definition runner.hpp:414
@ running_to_finish
Definition runner.hpp:402
@ never_run
Definition runner.hpp:399
@ running_until
Definition runner.hpp:408
@ dead
Indicates that the Runner was killed (by another thread).
Definition runner.hpp:420
state current_state() const noexcept
Return the current state.
Definition runner.hpp:773
bool running_for() const noexcept
Check if the runner is currently running for a particular length of time.
Definition runner.hpp:720
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:696
void kill() noexcept
Stop run from running (thread-safe).
Definition runner.hpp:644
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:84
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:151