ALib C++ Library
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 \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 an
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 #if ALIB_DEBUG
93 DbgFulfillCI.Line= -1;
94 DbgWaitCI .Line= -1;
95 #endif
96 future= promise.get_future();
97 }
98
99 /// Destructor.
101 ALIB_ASSERT_WARNING( DbgFulfillCI.Line != -1, "THREADS",
102 "Promise not fulfilled on destruction." )
103 ALIB_ASSERT_WARNING( DbgWaitCI .Line != -1, "THREADS",
104 "Promise not awaited on destruction." )
105 }
106
107 /// With debug-compilations, a \ref alib_mod_assert "warning is raised" on destruction, in
108 /// case either #Fulfill was not called or a waiting method was not called (or both).
109 /// With an invocation of this method, such warnings can be omitted.<br>
110 /// Note that the function is available in release-builds as well, but empty and optimized-out.
112 #if ALIB_DEBUG
113 DbgFulfillCI.Line =
114 DbgWaitCI .Line = 0;
115 #endif
116 }
117
118 #if DOXYGEN
119 /// Sets the state to \alib{threads::Promise;State;State::Unfulfilled}.
120 /// This is to be invoked by the "fulfilling" thread which received this object, for example,
121 /// as a part of a command, to signal that the promise is considered fulfilled.<br>
122 /// A thread waiting with methods #Wait, #WaitUntil, or #WaitFor will be woken up.
123 /// @param ci Caller information. Only available with debug-builds.
124 /// Use macro \ref ALIB_CALLER_PRUNED to pass this parameter, respectively.
125 /// Use macro \ref ALIB_CALLER_PRUNED_COMMA if \p{state} should be non-defaulted.
126 /// @param state The result to set.
127 /// Defaults to \alib{threads;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 macro \ref 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 macro \ref 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 macro \ref 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 macro \ref 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
void Fulfill(const CallerInfo &ci, State state=State::OK)
lang::CallerInfo DbgFulfillCI
Definition promise.inl:80
void DbgOmitDestructionWarning()
Definition promise.inl:111
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:229
~Promise()
Destructor.
Definition promise.inl:100
std::promise< State > promise
Used for implementation.
Definition promise.inl:65
TTimePoint Export() const
#define ALIB_DLL
Definition alib.inl:503
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1067
#define ALIB_EXPORT
Definition alib.inl:497
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1066
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.