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