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