Skip to content

Commit 8f72891

Browse files
committed
Document Timeout
1 parent 3ca7ff7 commit 8f72891

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

lib/base/io-engine.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ void AsioConditionVariable::Wait(boost::asio::yield_context yc)
146146
m_Timer.async_wait(yc[ec]);
147147
}
148148

149+
/**
150+
* Cancels any pending timeout callback.
151+
*
152+
* Must be called in the strand in which the callback was scheduled!
153+
*/
149154
void Timeout::Cancel()
150155
{
151156
m_Cancelled->store(true);

lib/base/io-engine.hpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,37 @@ class AsioConditionVariable
165165
/**
166166
* I/O timeout emulator
167167
*
168+
* This class provides a workaround for Boost.ASIO's lack of built-in timeout support.
169+
* While Boost.ASIO handles asynchronous operations, it does not natively support timeouts for these operations.
170+
* This class uses a boost::asio::deadline_timer to emulate a timeout by scheduling a callback to be triggered
171+
* after a specified duration, effectively adding timeout behavior where none exists.
172+
* The callback is executed within the provided strand, ensuring thread-safety.
173+
*
174+
* The constructor returns immediately after scheduling the timeout callback.
175+
* The callback itself is invoked asynchronously when the timeout occurs.
176+
* This allows the caller to continue execution while the timeout is running in the background.
177+
*
178+
* The class provides a Cancel() method to unschedule any pending callback. If the callback has already been run,
179+
* calling Cancel() has no effect. This method can be used to abort the timeout early if the monitored operation
180+
* completes before the callback has been run. The Timeout destructor also automatically cancels any pending callback.
181+
* A callback is considered pending even if the timeout has already expired,
182+
* but the callback has not been executed yet due to a busy strand.
183+
*
168184
* @ingroup base
169185
*/
170186
class Timeout
171187
{
172188
public:
173189
using Timer = boost::asio::deadline_timer;
174190

191+
/**
192+
* Schedules onTimeout to be triggered after timeoutFromNow on strand.
193+
*
194+
* @param strand The strand in which the callback will be executed.
195+
* The caller must also run in this strand, as well as Cancel() and the destructor!
196+
* @param timeoutFromNow The duration after which the timeout callback will be triggered.
197+
* @param onTimeout The callback to invoke when the timeout occurs.
198+
*/
175199
template<class OnTimeout>
176200
Timeout(boost::asio::io_context::strand& strand, const Timer::duration_type& timeoutFromNow, OnTimeout onTimeout)
177201
: m_Timer(strand.context(), timeoutFromNow), m_Cancelled(Shared<Atomic<bool>>::Make(false))
@@ -192,6 +216,11 @@ class Timeout
192216
Timeout& operator=(const Timeout&) = delete;
193217
Timeout& operator=(Timeout&&) = delete;
194218

219+
/**
220+
* Cancels any pending timeout callback.
221+
*
222+
* Must be called in the strand in which the callback was scheduled!
223+
*/
195224
~Timeout()
196225
{
197226
Cancel();
@@ -201,6 +230,14 @@ class Timeout
201230

202231
private:
203232
Timer m_Timer;
233+
234+
/**
235+
* Indicates whether the Timeout has been cancelled.
236+
*
237+
* This must be Shared<> between the lambda in the constructor and Cancel() for the case
238+
* the destructor calls Cancel() while the lambda is already queued in the strand.
239+
* The whole Timeout instance can't be kept alive by the lambda because this would delay the destructor.
240+
*/
204241
Shared<Atomic<bool>>::Ptr m_Cancelled;
205242
};
206243

0 commit comments

Comments
 (0)