source/base: Add the prepare callback functionality
diff --git a/src/sdeventplus/internal/sdevent.hpp b/src/sdeventplus/internal/sdevent.hpp
index e6c420c..dfb6285 100644
--- a/src/sdeventplus/internal/sdevent.hpp
+++ b/src/sdeventplus/internal/sdevent.hpp
@@ -32,6 +32,9 @@
virtual int
sd_event_source_set_description(sd_event_source* source,
const char* description) const = 0;
+ virtual int
+ sd_event_source_set_prepare(sd_event_source* source,
+ sd_event_handler_t callback) const = 0;
virtual int sd_event_source_get_pending(sd_event_source* source) const = 0;
virtual int sd_event_source_get_priority(sd_event_source* source,
int64_t* priority) const = 0;
@@ -101,6 +104,11 @@
{
return ::sd_event_source_set_description(source, description);
}
+ int sd_event_source_set_prepare(sd_event_source* source,
+ sd_event_handler_t callback) const override
+ {
+ return ::sd_event_source_set_prepare(source, callback);
+ }
int sd_event_source_get_pending(sd_event_source* source) const override
{
return ::sd_event_source_get_pending(source);
diff --git a/src/sdeventplus/source/base.cpp b/src/sdeventplus/source/base.cpp
index d99d562..d361333 100644
--- a/src/sdeventplus/source/base.cpp
+++ b/src/sdeventplus/source/base.cpp
@@ -1,4 +1,5 @@
#include <cerrno>
+#include <cstdio>
#include <exception>
#include <sdeventplus/exception.hpp>
#include <sdeventplus/internal/sdevent.hpp>
@@ -17,6 +18,30 @@
set_enabled(SD_EVENT_OFF);
}
+int Base::prepareCallback()
+{
+ try
+ {
+ prepare(*this);
+ return 0;
+ }
+ catch (const std::system_error& e)
+ {
+ fprintf(stderr, "sdeventplus: prepareCallback: %s\n", e.what());
+ return -e.code().value();
+ }
+ catch (const std::exception& e)
+ {
+ fprintf(stderr, "sdeventplus: prepareCallback: %s\n", e.what());
+ return -ENOSYS;
+ }
+ catch (...)
+ {
+ fprintf(stderr, "sdeventplus: prepareCallback: Unknown error\n");
+ return -ENOSYS;
+ }
+}
+
const char* Base::get_description()
{
const char* description;
@@ -38,6 +63,28 @@
}
}
+static int prepare_callback(sd_event_source*, void* userdata)
+{
+ if (userdata == nullptr)
+ {
+ fprintf(stderr, "sdeventplus: prepare_callback: Missing userdata\n");
+ return -EINVAL;
+ }
+ return reinterpret_cast<Base*>(userdata)->prepareCallback();
+}
+
+void Base::set_prepare(Callback&& callback)
+{
+ int r = sdevent->sd_event_source_set_prepare(
+ source.get(), callback ? prepare_callback : nullptr);
+ if (r < 0)
+ {
+ prepare = nullptr;
+ throw SdEventError(-r, "sd_event_source_set_prepare");
+ }
+ prepare = std::move(callback);
+}
+
int Base::get_pending()
{
int r = sdevent->sd_event_source_get_pending(source.get());
diff --git a/src/sdeventplus/source/base.hpp b/src/sdeventplus/source/base.hpp
index 20ab820..748f340 100644
--- a/src/sdeventplus/source/base.hpp
+++ b/src/sdeventplus/source/base.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
+#include <functional>
#include <sdeventplus/internal/sdevent.hpp>
#include <sdeventplus/internal/sdref.hpp>
#include <systemd/sd-bus.h>
@@ -14,6 +15,8 @@
class Base
{
public:
+ using Callback = std::function<void(Base& source)>;
+
virtual ~Base();
// We don't want to allow any kind of slicing.
@@ -22,8 +25,11 @@
Base(Base&& source) = delete;
Base& operator=(Base&& source) = delete;
+ int prepareCallback();
+
const char* get_description();
void set_description(const char* description);
+ void set_prepare(Callback&& callback);
int get_pending();
int64_t get_priority();
void set_priority(int64_t priority);
@@ -39,6 +45,9 @@
internal::SdEvent* sdevent = &internal::sdevent_impl);
Base(sd_event_source* source, std::false_type,
internal::SdEvent* sdevent = &internal::sdevent_impl);
+
+ private:
+ Callback prepare;
};
} // namespace source
diff --git a/test/source/base.cpp b/test/source/base.cpp
index ce95f12..13c534a 100644
--- a/test/source/base.cpp
+++ b/test/source/base.cpp
@@ -1,3 +1,4 @@
+#include <cerrno>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <sdeventplus/exception.hpp>
@@ -5,6 +6,7 @@
#include <sdeventplus/source/base.hpp>
#include <sdeventplus/test/sdevent.hpp>
#include <string>
+#include <system_error>
#include <type_traits>
namespace sdeventplus
@@ -16,6 +18,7 @@
using testing::DoAll;
using testing::Return;
+using testing::SaveArg;
using testing::SetArgPointee;
class BaseImpl : public Base
@@ -132,6 +135,73 @@
EXPECT_THROW(base->set_description(expected), SdEventError);
}
+TEST_F(BaseMethodTest, SetPrepareCallback)
+{
+ bool completed = false;
+ Base::Callback callback = [&completed](Base&) { completed = true; };
+ sd_event_handler_t event_handler;
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(DoAll(SaveArg<1>(&event_handler), Return(0)));
+ base->set_prepare(std::move(callback));
+ EXPECT_FALSE(callback);
+ EXPECT_FALSE(completed);
+
+ EXPECT_EQ(0, event_handler(nullptr, base.get()));
+ EXPECT_TRUE(completed);
+}
+
+TEST_F(BaseMethodTest, SetPrepareCallbackNoUserdata)
+{
+ Base::Callback callback = [](Base&) {};
+ sd_event_handler_t event_handler;
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(DoAll(SaveArg<1>(&event_handler), Return(0)));
+ base->set_prepare(std::move(callback));
+ EXPECT_FALSE(callback);
+
+ EXPECT_EQ(-EINVAL, event_handler(nullptr, nullptr));
+}
+
+TEST_F(BaseMethodTest, SetPrepareNull)
+{
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, nullptr))
+ .WillOnce(Return(0));
+ base->set_prepare(nullptr);
+ EXPECT_EQ(-ENOSYS, base->prepareCallback());
+}
+
+TEST_F(BaseMethodTest, SetPrepareSystemError)
+{
+ Base::Callback callback = [](Base&) {
+ throw std::system_error(EBUSY, std::generic_category());
+ };
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(Return(0));
+ base->set_prepare(std::move(callback));
+ EXPECT_FALSE(callback);
+ EXPECT_EQ(-EBUSY, base->prepareCallback());
+}
+
+TEST_F(BaseMethodTest, SetPrepareUnknownException)
+{
+ Base::Callback callback = [](Base&) { throw static_cast<int>(1); };
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(Return(0));
+ base->set_prepare(std::move(callback));
+ EXPECT_FALSE(callback);
+ EXPECT_EQ(-ENOSYS, base->prepareCallback());
+}
+
+TEST_F(BaseMethodTest, SetPrepareError)
+{
+ Base::Callback callback = [](Base&) {};
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(Return(-EINVAL));
+ EXPECT_THROW(base->set_prepare(std::move(callback)), SdEventError);
+ EXPECT_TRUE(callback);
+ EXPECT_EQ(-ENOSYS, base->prepareCallback());
+}
+
TEST_F(BaseMethodTest, GetPendingSuccess)
{
EXPECT_CALL(mock, sd_event_source_get_pending(expected_source))