ALib C++ Framework
by
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
promise.inl
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/// \emoji :copyright: 2013-2025 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, #"Unfulfilled" is set.
48 /// \b Error or a custom value could be used if the promise could not be fulfilled for any
49 /// reason. \b EmergencyStop could be the right choice if the whole application should stop.
50 /// But this is all up to the using code.
51 enum class State
52 {
53 Unfulfilled , ///< The state after construction.
54 OK , ///< The default state of successful fulfillment.
55 Error , ///< A default error state. (Use-case dependent.)
56 EmergencyStop , ///< A state indicating that everything is to be stopped.
57 ///< (Use-case dependent.)
58 Custom , ///< The first element defining a custom state. Further custom states
59 ///< with higher underlying integral values can be defined.
60 };
61
62 protected:
63
64 std::promise<State> promise; ///< Used for implementation.
65 std::future<State> future; ///< Used for implementation.
66
67 public:
68 #if ALIB_DEBUG
69 /// This is a threshold that causes the non-timed #".Wait" method to raise an
70 /// \alib_warning in debug-builds in case a thread is blocked longer than the given duration.
71 ///
72 /// To disable warnings in cases that high block times are suitable, set this value to \c 0.
73 /// The default value is two seconds.
74 Ticks::Duration DbgWaitTimeLimit = Ticks::Duration::FromAbsoluteSeconds(2);
75
76 /// Debug-information about the first caller to #"Fulfill".
77 /// A second (forbidden) call will be asserted with information about where the first invocation
78 /// was made.
80
81 /// Debug-information about the first caller to a successful wait.
82 /// A second call will be asserted with information about where the first invocation to a
83 /// successful wait was made.
85
86 #endif // ALIB_DEBUG
87
88
89 /// Default constructor.
91 #if ALIB_DEBUG
92 DbgFulfillCI.Line= -1;
93 DbgWaitCI .Line= -1;
94 #endif
95 future= promise.get_future();
96 }
97
98 /// Destructor.
100 ALIB_ASSERT_WARNING( DbgFulfillCI.Line != -1, "THREADS",
101 "Promise not fulfilled on destruction." )
102 ALIB_ASSERT_WARNING( DbgWaitCI .Line != -1, "THREADS",
103 "Promise not awaited on destruction." )
104 }
105
106 /// With debug-compilations, a #"alib_mod_assert;warning is raised" on destruction, in
107 /// case either #"Fulfill" was not called or a waiting method was not called (or both).
108 /// With an invocation of this method, such warnings can be omitted.<br>
109 /// Note that the function is available in release-builds as well, but empty and optimized-out.
111 #if ALIB_DEBUG
112 DbgFulfillCI.Line =
113 DbgWaitCI .Line = 0;
114 #endif
115 }
116
117 #if DOXYGEN
118 /// Sets the state to #"State::Unfulfilled".
119 /// This is to be invoked by the "fulfilling" thread which received this object, for example,
120 /// as a part of a command, to signal that the promise is considered fulfilled.<br>
121 /// A thread waiting with methods #".Wait", #"WaitUntil", or #"WaitFor(const Ticks::Duration&)"
122 /// will be woken up.
123 /// @param ci Caller information. Only available with debug-builds.
124 /// Use the macro #"ALIB_CALLER_PRUNED" to pass this parameter, respectively.
125 /// Use the macro #"ALIB_CALLER_PRUNED_COMMA" if \p{state} should be non-defaulted.
126 /// @param state The result to set.
127 /// Defaults to #"Promise::State;OK".
128 void Fulfill(const CallerInfo& ci, State state= State::OK);
129
130 #elif !ALIB_DEBUG
131 void Fulfill(State state= State::OK) {
132 ALIB_ASSERT_ERROR(DbgFulfillCI.File == nullptr, "THREADS",
133 "Promise was already fulfilled. Repeated calls not allowed.\n Fullfilled at: "
134 , DbgFulfillCI )
135 promise.set_value(state);
136 }
137 #elif !ALIB_STRINGS
138 void Fulfill(const CallerInfo& ci,State state= State::OK) {
139 (void) ci;
140 ALIB_ASSERT_ERROR(DbgFulfillCI.File == nullptr, "THREADS",
141 "Promise was already fulfilled. Repeated calls not allowed.\n Fullfilled at: "
142 , DbgFulfillCI )
143 promise.set_value(state);
144 }
145 #else
146 ALIB_DLL void Fulfill(const CallerInfo& ci,State state= State::OK);
147 #endif
148
149
150 #if DOXYGEN
151 /// Waits an unlimited time for the promise to become fulfilled.
152 /// @param ci Caller information. Only available with debug-builds.
153 /// Use the macro #"ALIB_CALLER_PRUNED" to pass this parameter
154 /// @return The state given by the second thread with #"Fulfill".
156 #elif !ALIB_DEBUG
157 State Wait() { return future.get(); }
158 #elif !ALIB_STRINGS
159 State Wait(const CallerInfo& ci)
160 {
161 ALIB_ASSERT_ERROR(DbgWaitCI.File == nullptr, "THREADS",
162 "Promise was already awaited. Repeated calls not allowed.\n Awaited at: "
163 , DbgWaitCI )
164
165 if ( !DbgWaitTimeLimit.IsZero() )
166 {
167 Ticks::Duration waitDuration= DbgWaitTimeLimit;
168 Ticks overallTimer;
169 Ticks waitTimer;
170 while ( future.wait_for( (waitDuration - waitTimer.Age()).Export() )
171 != std::future_status::ready )
172 {
173 if ( waitTimer.Age() < waitDuration )
174 continue; // spurious wakeup
175
176 assert::Raise( ci, 1, "THREADS", "Waiting for a Promise since (ms): ",
177 int(overallTimer.Age().InAbsoluteMilliseconds()));
178 waitTimer.Reset();
179 }
180 }
181 DbgWaitCI= ci;
182 return future.get();
183 }
184 #else
185 ALIB_DLL State Wait(const CallerInfo& ci);
186 #endif
187
188 #if DOXYGEN
189 /// Waits for the promise to become fulfilled, but only for a given duration.
190 /// @param maxWaitTimeSpan The maximum time to wait.
191 /// @param ci Caller information. Only available with debug-builds.
192 /// Use the macro #"ALIB_COMMA_CALLER_PRUNED" to pass this parameter.
193 /// @return Either <b>State::Unfulfilled</b>, or the state given by the second thread
194 /// with #"Fulfill".
195 State WaitFor( const Ticks::Duration::TDuration& maxWaitTimeSpan, const CallerInfo& ci );
196 #elif !ALIB_DEBUG
197 State WaitFor( const Ticks::Duration::TDuration& maxWaitTimeSpan )
198 {
199 return future.wait_for(maxWaitTimeSpan) == std::future_status::timeout
201 : future.get();
202 }
203 #elif !ALIB_STRINGS
204 State WaitFor( const Ticks::Duration::TDuration& maxWaitTimeSpan, const CallerInfo& ci )
205 {
206 ALIB_ASSERT_ERROR(DbgWaitCI.File == nullptr, "THREADS",
207 "Promise was already awaited. Repeated calls not allowed.\n Awaited at: "
208 , DbgWaitCI )
209
210 if ( future.wait_for(maxWaitTimeSpan) == std::future_status::timeout )
211 return State::Unfulfilled;
212
213 DbgWaitCI= ci;
214 return future.get();
215 }
216 #else
217 ALIB_DLL State WaitFor( const Ticks::Duration::TDuration& maxWaitTimeSpan,
218 const CallerInfo& ci );
219 #endif
220
221
222 #if ALIB_DEBUG
223 /// Waits for the promise to become fulfilled, but only for a given duration.
224 /// @param maxWaitTimeSpan The maximum time to wait.
225 /// @param ci Caller information. Only available with debug-builds.
226 /// Use the macro #"ALIB_COMMA_CALLER_PRUNED" to pass this parameter.
227 /// @return Either <b>State::Unfulfilled</b>, or the state given by the second thread
228 /// with #"Fulfill".
229 State WaitFor( const Ticks::Duration& maxWaitTimeSpan, const CallerInfo& ci )
230 { return WaitFor( maxWaitTimeSpan.Export(), ci ); }
231 #else
232 State WaitFor( const Ticks::Duration& maxWaitTimeSpan )
233 { return WaitFor( maxWaitTimeSpan.Export() ); }
234 #endif
235
236 #if DOXYGEN
237 /// Waits for the promise to become fulfilled, but only until a given point in time.
238 /// @param wakeUpTime The point in time to wake up, even if not notified.
239 /// @param ci Caller information. Only available with debug-builds.
240 /// Use the macro #"ALIB_COMMA_CALLER_PRUNED" to pass this parameter.
241 /// @return Either <b>State::Unfulfilled</b>, or the state given by the second thread
242 /// with #"Fulfill".
243 State WaitUntil( const Ticks& wakeUpTime, const CallerInfo& ci );
244 #elif !ALIB_DEBUG
245 State WaitUntil( const Ticks& wakeUpTime )
246 {
247 return future.wait_until(wakeUpTime.Export()) == std::future_status::timeout
249 : future.get();
250 }
251 #elif !ALIB_STRINGS
252 State WaitUntil( const Ticks& wakeUpTime, const CallerInfo& ci )
253 {
254 ALIB_ASSERT_ERROR(DbgWaitCI.File == nullptr, "THREADS",
255 "Promise was already awaited. Repeated calls not allowed.\n Awaited at: "
256 , DbgWaitCI )
257
258 if ( future.wait_until(wakeUpTime.Export()) == std::future_status::timeout )
259 return State::Unfulfilled;
260
261 DbgWaitCI= ci;
262 return future.get();
263 }
264 #else
265 ALIB_DLL State WaitUntil( const Ticks& wakeUpTime, const CallerInfo& ci );
266 #endif
267}; // class Promise
268
269
270} // namespace alib[threads]
271
272/// Type alias in namespace \b alib.
274
275} // namespace [alib]
276#endif // !ALIB_SINGLE_THREADED
#define ALIB_DLL
Definition alib.inl:573
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1145
#define ALIB_EXPORT
Definition alib.inl:562
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1144
void Fulfill(const CallerInfo &ci, State state=State::OK)
lang::CallerInfo DbgFulfillCI
Definition promise.inl:79
void DbgOmitDestructionWarning()
Definition promise.inl:110
State Wait(const CallerInfo &ci)
lang::CallerInfo DbgWaitCI
Definition promise.inl:84
Ticks::Duration DbgWaitTimeLimit
Definition promise.inl:74
@ Unfulfilled
The state after construction.
Definition promise.inl:53
@ Error
A default error state. (Use-case dependent.).
Definition promise.inl:55
@ OK
The default state of successful fulfillment.
Definition promise.inl:54
State WaitUntil(const Ticks &wakeUpTime, const CallerInfo &ci)
std::future< State > future
Used for implementation.
Definition promise.inl:65
State WaitFor(const Ticks::Duration::TDuration &maxWaitTimeSpan, const CallerInfo &ci)
Promise()
Default constructor.
Definition promise.inl:90
State WaitFor(const Ticks::Duration &maxWaitTimeSpan, const CallerInfo &ci)
Definition promise.inl:229
~Promise()
Destructor.
Definition promise.inl:99
std::promise< State > promise
Used for implementation.
Definition promise.inl:64
TTimePoint Export() const
void Raise(const lang::CallerInfo &ci, int type, std::string_view domain, TArgs &&... args)
Definition assert.inl:181
threads::Promise Promise
Type alias in namespace alib.
Definition promise.inl:273
lang::CallerInfo CallerInfo
Type alias in namespace alib.
time::Ticks Ticks
Type alias in namespace alib.
Definition ticks.inl:79
const char * File
The name of the source file as given by compiler.