blob: 2058462876abebf3718426b716b073b4eb15036c [file] [log] [blame]
#pragma once
#include <chrono>
#include <memory>
#include <phosphor-logging/elog.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <sdbusplus/bus.hpp>
#include <systemd/sd-event.h>
#include <xyz/openbmc_project/Common/error.hpp>
namespace sdevent
{
namespace event
{
namespace io
{
class IO;
} // namespace io
using EventPtr = sd_event*;
class Event;
/** @brief Get an instance of the 'default' event. */
Event newDefault();
namespace details
{
/** @brief unique_ptr functor to release an event reference. */
struct EventDeleter
{
void operator()(sd_event* ptr) const
{
deleter(ptr);
}
decltype(&sd_event_unref) deleter = sd_event_unref;
};
/* @brief Alias 'event' to a unique_ptr type for auto-release. */
using Event = std::unique_ptr<sd_event, EventDeleter>;
} // namespace details
using namespace phosphor::logging;
/** @class Event
* @brief Provides C++ bindings to the sd_event_* class functions.
*/
class Event
{
private:
using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
Error::InternalFailure;
public:
/* Define all of the basic class operations:
* Not allowed:
* - Default constructor to avoid nullptrs.
* - Copy operations due to internal unique_ptr.
* Allowed:
* - Move operations.
* - Destructor.
*/
Event() = delete;
Event(const Event&) = delete;
Event& operator=(const Event&) = delete;
Event(Event&&) = default;
Event& operator=(Event&&) = default;
~Event() = default;
/** @brief Conversion constructor from 'EventPtr'.
*
* Increments ref-count of the event-pointer and releases it when
* done.
*/
explicit Event(EventPtr e);
/** @brief Constructor for 'Event'.
*
* Takes ownership of the event-pointer and releases it when done.
*/
Event(EventPtr e, std::false_type);
/** @brief Release ownership of the stored event-pointer. */
EventPtr release()
{
return evt.release();
}
/** @brief Wait indefinitely for new event sources. */
void loop()
{
auto rc = sd_event_loop(evt.get());
if (rc < 0)
{
log<level::ERR>("Error in call to sd_event_loop",
entry("RC=%d", rc));
elog<InternalFailure>();
}
}
/** @brief Stop the loop. */
void exit(int status = 0)
{
auto rc = sd_event_exit(evt.get(), status);
if (rc < 0)
{
log<level::ERR>("Error in call to sd_event_exit",
entry("RC=%d", rc),
entry("STATUS=%d", status));
elog<InternalFailure>();
}
}
/** @brief Get the loop exit code. */
auto getExitStatus()
{
int status;
auto rc = sd_event_get_exit_code(evt.get(), &status);
if (rc < 0)
{
log<level::ERR>("Error in call to sd_event_get_exit_code",
entry("RC=%d", rc));
elog<InternalFailure>();
}
return status;
}
/** @brief Attach to a DBus loop. */
void attach(sdbusplus::bus::bus& bus)
{
bus.attach_event(evt.get(), SD_EVENT_PRIORITY_NORMAL);
}
/** @brief C++ wrapper for sd_event_now. */
auto now()
{
using namespace std::chrono;
uint64_t usec;
auto rc = sd_event_now(evt.get(), CLOCK_MONOTONIC, &usec);
if (rc < 0)
{
log<level::ERR>("Error in call to sd_event_now",
entry("RC=%d", rc));
elog<InternalFailure>();
}
microseconds d(usec);
return steady_clock::time_point(d);
}
friend class io::IO;
private:
EventPtr get()
{
return evt.get();
}
details::Event evt;
};
inline Event::Event(EventPtr l) : evt(sd_event_ref(l))
{
}
inline Event::Event(EventPtr l, std::false_type) : evt(l)
{
}
inline Event newDefault()
{
using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
Error::InternalFailure;
sd_event* e = nullptr;
auto rc = sd_event_default(&e);
if (rc < 0)
{
log<level::ERR>("Error in call to sd_event_default",
entry("RC=%d", rc));
elog<InternalFailure>();
}
return Event(e, std::false_type());
}
} // namespace event
} // namespace sdevent