Fix #88: Timeout handling. (#91)

fix timeout handling

* Despite what is documented in sd_bus_get_timeout(3), the timeout
  returned is actually an absolute time point of Linux's CLOCK_MONOTONIC
  clock. Hence, we first have to subtract the current time from the
  timeout in order to get a relative time that can be passed to poll.

* For async call timeouts to reliably work, we need a way to notify the
  event loop of a connection that is currently blocked waiting in poll.
  I.e. assume the event loop thread entered poll with a timeout set to
  T1. Afterwards, the main thread starts an async call C with a timeout
  T2 < T1. In order for C to be canceled after its timeout T1 has
  elapsed, we have to be able to notify the event loop so that it can
  update its poll data.

Co-authored-by: Urs Ritzmann <ursritzmann@protonmail.ch>
Co-authored-by: Lukasz Marcul <lukasz.marcul@onemeter.com>
This commit is contained in:
Benjamin Kaufmann
2021-12-20 10:00:29 +01:00
committed by GitHub
parent 0b8f2d9752
commit bb0f3f0242
13 changed files with 249 additions and 30 deletions

View File

@ -31,6 +31,7 @@
#include <memory>
#include <chrono>
#include <cstdint>
#include <optional>
namespace sdbus {
@ -47,11 +48,60 @@ namespace sdbus {
class IConnection
{
public:
/*!
* Poll Data for external event loop implementations.
*
* To integrate sdbus with your app's own custom event handling system
* you can use this method to query which file descriptors, poll events
* and timeouts you should add to your app's poll(2), or select(2)
* call in your main event loop.
*
* If you are unsure what this all means then use
* enterEventLoop() or enterEventLoopAsync() instead.
*
* See: getEventLoopPollData()
*/
struct PollData
{
/*!
* The read fd to be monitored by the event loop.
*/
int fd;
/*!
* The events to use for poll(2) alongside fd.
*/
short int events;
/*!
* Absolute timeout value in micro seconds and based of CLOCK_MONOTONIC.
*/
uint64_t timeout_usec;
/*!
* Get the event poll timeout.
*
* The timeout is an absolute value based of CLOCK_MONOTONIC.
*
* @return a duration since the CLOCK_MONOTONIC epoch started.
*/
[[nodiscard]] std::chrono::microseconds getAbsoluteTimeout() const {
return std::chrono::microseconds(timeout_usec);
}
/*!
* Get the timeout as relative value from now
*
* @return std::nullopt if the timeout is indefinite. A duration otherwise.
*/
[[nodiscard]] std::optional<std::chrono::microseconds> getRelativeTimeout() const;
/*!
* Get a converted, relative timeout which can be passed as argument 'timeout' to poll(2)
*
* @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block. An integer in milli
* seconds otherwise.
*/
[[nodiscard]] int getPollTimeout() const;
};
virtual ~IConnection() = default;

View File

@ -49,6 +49,7 @@ namespace sdbus {
class MethodReply;
namespace internal {
class ISdBus;
class IConnection;
}
}
@ -195,9 +196,13 @@ namespace sdbus {
void dontExpectReply();
bool doesntExpectReply() const;
protected:
MethodCall(void *msg, internal::ISdBus* sdbus, const internal::IConnection* connection, adopt_message_t) noexcept;
private:
MethodReply sendWithReply(uint64_t timeout = 0) const;
MethodReply sendWithNoReply() const;
const internal::IConnection* connection_{};
};
class MethodReply : public Message