ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
promise.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_threads of the \aliblong.
4///
5/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8#if !ALIB_SINGLE_THREADED
9ALIB_EXPORT namespace alib { namespace threads {
10
11
12//==================================================================================================
13/// A small class which aggregates C++ standard library mechanics provided with <c>std::promise</c>
14/// and <c>std::future</c> into one simple interface.
15///
16/// The following features and facts are notable:
17/// 1. The class is not designed for multiple threads to wait on this promise to be fulfilled.
18/// Only one thread is allowed to wait.<br>
19/// For other use-cases, see alternative type #"threads::Event".
20///
21/// 2. When fulfillment is acknowledged, a standardized \e state can be given.
22/// Besides the default state #"State::OK", two other states are
23/// built-in. Custom states can be defined and given.<br>
24/// An application might agree that, for example, each thread has to stop in the case that
25/// somewhere state #"State::EmergencyStop" was set.
26/// Note that some mechanics have to be implemented which ensure that such an emergency state is
27/// propagated to all relevant entities.
28///
29/// The class cannot be copied (just like <c>std::promise</c> can't be). Therefore, usually
30/// a pointer to an object is passed to the fulfilling thread and the waiting thread is responsible
31/// for ensuring the lifecycle of the object survived until the promise is fulfilled.
32///
33/// With debug-compilations, the field #"DbgWaitTimeLimit" enables the raise of \alib warnings in
34/// case a certain wait time is exceeded when using the unlimited blocking method
35///
36/// Furthermore, two \alib warnings may be raised with destruction:
37/// 1. When the promise was not fulfilled.
38/// This warning can be silenced by setting <b>DbgFulfillCI.Line</b> to a positive value.
39/// 2. When the promise was not awaited.
40/// This warning can be silenced by setting <b>DbgWaitCI.Line</b> to a positive value.
41///
42/// \par Availability
43/// This type is not available if the configuration macro #"ALIB_SINGLE_THREADED" is set.
44//==================================================================================================
45class Promise {
46 public:
47 /// Lists possible states. With construction of class #"%Promise", #"%Unfulfilled" is set.
48 /// #"%State::Error" or a custom value could be used if the promise could not be fulfilled for any
49 /// reason. #"%State::EmergencyStop" could be the right choice if the whole application should
50 /// stop. But this is all up to the using code.
51 enum class State {
52 Unfulfilled , ///< The state after construction.
53 OK , ///< The default state of successful fulfillment.
54 Error , ///< A default error state. (Use-case dependent.)
55 EmergencyStop , ///< A state indicating that everything is to be stopped.
56 ///< (Use-case dependent.)
57 Custom , ///< The first element defining a custom state. Further custom states
58 ///< with higher underlying integral values can be defined.
59 };
60
61 protected:
62
63 std::promise<State> promise; ///< Used for implementation.
64 std::future<State> future; ///< Used for implementation.
65
66 public:
67 #if ALIB_DEBUG
68 /// This is a threshold that causes the non-timed #".Wait" method to raise an
69 /// \alib_warning in debug-builds in case a thread is blocked longer than the given duration.
70 ///
71 /// To disable warnings in cases that high block times are suitable, set this value to \c 0.
72 /// The default value is two seconds.
73 Ticks::Duration DbgWaitTimeLimit = Ticks::Duration::FromAbsoluteSeconds(2);
74
75 /// Debug-information about the first caller to #"Fulfill".
76 /// A second (forbidden) call will be asserted with information about where the first invocation
77 /// was made.
79
80 /// Debug-information about the first caller to a successful wait.
81 /// A second call will be asserted with information about where the first invocation to a
82 /// successful wait was made.
84
85 #endif // ALIB_DEBUG
86
87
88 /// Default constructor.
90 #if ALIB_DEBUG
91 DbgFulfillCI.Line= -1;
92 DbgWaitCI .Line= -1;
93 #endif
94 future= promise.get_future();
95 }
96
97 /// Destructor.
99 ALIB_ASSERT_WARNING( DbgFulfillCI.Line != -1, "THREADS",
100 "Promise not fulfilled on destruction." )
101 ALIB_ASSERT_WARNING( DbgWaitCI .Line != -1, "THREADS",
102 "Promise not awaited on destruction." )
103 }
104
105 /// With debug-compilations, a #"alib_mod_assert;warning is raised" on destruction, in
106 /// case either #"Fulfill" was not called or a waiting method was not called (or both).
107 /// With an invocation of this method, such warnings can be omitted.<br>
108 /// Note that the function is available in release-builds as well, but empty and optimized-out.
110 #if ALIB_DEBUG
111 DbgFulfillCI.Line =
112 DbgWaitCI .Line = 0;
113 #endif
114 }
115
116 #if DOXYGEN
117 /// Sets the state to #"State::Unfulfilled".
118 /// This is to be invoked by the "fulfilling" thread which received this object, for example,
119 /// as a part of a command, to signal that the promise is considered fulfilled.<br>
120 /// A thread waiting with methods #".Wait", #"WaitUntil", or #"WaitFor(const Ticks::Duration&)"
121 /// will be woken up.
122 /// @param ci Caller information. Only available with debug-builds.
123 /// Use the macro #"ALIB_CALLER_PRUNED" to pass this parameter, respectively.
124 /// Use the macro #"ALIB_CALLER_PRUNED_COMMA" if \p{state} should be non-defaulted.
125 /// @param state The result to set.
126 /// Defaults to #"Promise::State;OK".
127 void Fulfill(const CallerInfo& ci, State state= State::OK);
128
129 #elif !ALIB_DEBUG
130 void Fulfill(State state= State::OK) {
131 ALIB_ASSERT_ERROR(DbgFulfillCI.File == nullptr, "THREADS",
132 "Promise was already fulfilled. Repeated calls not allowed.\n Fullfilled at: "
133 , DbgFulfillCI )
134 promise.set_value(state);
135 }
136 #elif !ALIB_STRINGS
137 void Fulfill(const CallerInfo& ci,State state= State::OK) {
138 (void) ci;
139 ALIB_ASSERT_ERROR(DbgFulfillCI.File == nullptr, "THREADS",
140 "Promise was already fulfilled. Repeated calls not allowed.\n Fullfilled at: "
141 , DbgFulfillCI )
142 promise.set_value(state);
143 }
144 #else
145 ALIB_DLL void Fulfill(const CallerInfo& ci,State state= State::OK);
146 #endif
147
148
149 #if DOXYGEN
150 /// Waits an unlimited time for the promise to become fulfilled.
151 /// @param ci Caller information. Only available with debug-builds.
152 /// Use the macro #"ALIB_CALLER_PRUNED" to pass this parameter
153 /// @return The state given by the second thread with #"Fulfill".
155 #elif !ALIB_DEBUG
156 State Wait() { return future.get(); }
157 #elif !ALIB_STRINGS
158 State Wait(const CallerInfo& ci)
159 {
160 ALIB_ASSERT_ERROR(DbgWaitCI.File == nullptr, "THREADS",
161 "Promise was already awaited. Repeated calls not allowed.\n Awaited at: "
162 , DbgWaitCI )
163
164 if ( !DbgWaitTimeLimit.IsZero() )
165 {
166 Ticks::Duration waitDuration= DbgWaitTimeLimit;
167 Ticks overallTimer;
168 Ticks waitTimer;
169 while ( future.wait_for( (waitDuration - waitTimer.Age()).Export() )
170 != std::future_status::ready )
171 {
172 if ( waitTimer.Age() < waitDuration )
173 continue; // spurious wakeup
174
175 assert::Raise( ci, 1, "THREADS", "Waiting for a Promise since (ms): ",
176 int(overallTimer.Age().InAbsoluteMilliseconds()));
177 waitTimer.Reset();
178 }
179 }
180 DbgWaitCI= ci;
181 return future.get();
182 }
183 #else
184 ALIB_DLL State Wait(const CallerInfo& ci);
185 #endif
186
187 #if DOXYGEN
188 /// Waits for the promise to become fulfilled, but only for a given duration.
189 /// @param maxWaitTimeSpan The maximum time to wait.
190 /// @param ci Caller information. Only available with debug-builds.
191 /// Use the macro #"ALIB_COMMA_CALLER_PRUNED" to pass this parameter.
192 /// @return Either <b>State::Unfulfilled</b>, or the state given by the second thread
193 /// with #"Fulfill".
194 State WaitFor( const Ticks::Duration::TDuration& maxWaitTimeSpan, const CallerInfo& ci );
195 #elif !ALIB_DEBUG
196 State WaitFor( const Ticks::Duration::TDuration& maxWaitTimeSpan )
197 {
198 return future.wait_for(maxWaitTimeSpan) == std::future_status::timeout
200 : future.get();
201 }
202 #elif !ALIB_STRINGS
203 State WaitFor( const Ticks::Duration::TDuration& maxWaitTimeSpan, const CallerInfo& ci )
204 {
205 ALIB_ASSERT_ERROR(DbgWaitCI.File == nullptr, "THREADS",
206 "Promise was already awaited. Repeated calls not allowed.\n Awaited at: "
207 , DbgWaitCI )
208
209 if ( future.wait_for(maxWaitTimeSpan) == std::future_status::timeout )
210 return State::Unfulfilled;
211
212 DbgWaitCI= ci;
213 return future.get();
214 }
215 #else
216 ALIB_DLL State WaitFor( const Ticks::Duration::TDuration& maxWaitTimeSpan,
217 const CallerInfo& ci );
218 #endif
219
220
221 #if ALIB_DEBUG
222 /// Waits for the promise to become fulfilled, but only for a given duration.
223 /// @param maxWaitTimeSpan The maximum time to wait.
224 /// @param ci Caller information. Only available with debug-builds.
225 /// Use the macro #"ALIB_COMMA_CALLER_PRUNED" to pass this parameter.
226 /// @return Either <b>State::Unfulfilled</b>, or the state given by the second thread
227 /// with #"Fulfill".
228 State WaitFor( const Ticks::Duration& maxWaitTimeSpan, const CallerInfo& ci )
229 { return WaitFor( maxWaitTimeSpan.Export(), ci ); }
230 #else
231 State WaitFor( const Ticks::Duration& maxWaitTimeSpan )
232 { return WaitFor( maxWaitTimeSpan.Export() ); }
233 #endif
234
235 #if DOXYGEN
236 /// Waits for the promise to become fulfilled, but only until a given point in time.
237 /// @param wakeUpTime The point in time to wake up, even if not notified.
238 /// @param ci Caller information. Only available with debug-builds.
239 /// Use the macro #"ALIB_COMMA_CALLER_PRUNED" to pass this parameter.
240 /// @return Either <b>State::Unfulfilled</b>, or the state given by the second thread
241 /// with #"Fulfill".
242 State WaitUntil( const Ticks& wakeUpTime, const CallerInfo& ci );
243 #elif !ALIB_DEBUG
244 State WaitUntil( const Ticks& wakeUpTime )
245 {
246 return future.wait_until(wakeUpTime.Export()) == std::future_status::timeout
248 : future.get();
249 }
250 #elif !ALIB_STRINGS
251 State WaitUntil( const Ticks& wakeUpTime, const CallerInfo& ci )
252 {
253 ALIB_ASSERT_ERROR(DbgWaitCI.File == nullptr, "THREADS",
254 "Promise was already awaited. Repeated calls not allowed.\n Awaited at: "
255 , DbgWaitCI )
256
257 if ( future.wait_until(wakeUpTime.Export()) == std::future_status::timeout )
258 return State::Unfulfilled;
259
260 DbgWaitCI= ci;
261 return future.get();
262 }
263 #else
264 ALIB_DLL State WaitUntil( const Ticks& wakeUpTime, const CallerInfo& ci );
265 #endif
266}; // class Promise
267
268
269} // namespace alib[threads]
270
271/// Type alias in namespace #"%alib".
273
274} // namespace [alib]
275#endif // !ALIB_SINGLE_THREADED
#define ALIB_DLL
#define ALIB_ASSERT_WARNING(cond, domain,...)
#define ALIB_EXPORT
#define ALIB_ASSERT_ERROR(cond, domain,...)
void Fulfill(const CallerInfo &ci, State state=State::OK)
lang::CallerInfo DbgFulfillCI
Definition promise.hpp:78
void DbgOmitDestructionWarning()
Definition promise.hpp:109
State Wait(const CallerInfo &ci)
lang::CallerInfo DbgWaitCI
Definition promise.hpp:83
Ticks::Duration DbgWaitTimeLimit
Definition promise.hpp:73
@ Unfulfilled
The state after construction.
Definition promise.hpp:52
@ Error
A default error state. (Use-case dependent.).
Definition promise.hpp:54
@ OK
The default state of successful fulfillment.
Definition promise.hpp:53
State WaitUntil(const Ticks &wakeUpTime, const CallerInfo &ci)
std::future< State > future
Used for implementation.
Definition promise.hpp:64
State WaitFor(const Ticks::Duration::TDuration &maxWaitTimeSpan, const CallerInfo &ci)
Promise()
Default constructor.
Definition promise.hpp:89
State WaitFor(const Ticks::Duration &maxWaitTimeSpan, const CallerInfo &ci)
Definition promise.hpp:228
~Promise()
Destructor.
Definition promise.hpp:98
std::promise< State > promise
Used for implementation.
Definition promise.hpp:63
TTimePoint Export() const
void Raise(const lang::CallerInfo &ci, int type, std::string_view domain, TArgs &&... args)
Definition assert.hpp:178
Definition alox.cpp:14
threads::Promise Promise
Type alias in namespace #"%alib".
Definition promise.hpp:272
lang::CallerInfo CallerInfo
Type alias in namespace #"%alib".
time::Ticks Ticks
Type alias in namespace #"%alib".
Definition ticks.hpp:86
const char * File
The name of the source file as given by compiler.