(작성중( sdbus timer
2026. 4. 23. 15:55
cmake_minimum_required(VERSION 3.14)
project(timer_example CXX)
set(CMAKE_CXX_STANDARD 20)
find_package(sdbus-c++ REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SYSTEMD REQUIRED libsystemd)
add_executable(server server.cpp)
target_include_directories(server PRIVATE ${SYSTEMD_INCLUDE_DIRS})
target_link_libraries(server
SDBusCpp::sdbus-c++
${SYSTEMD_LIBRARIES}
)
#include <sdbus-c++/sdbus-c++.h>
#include <systemd/sd-event.h>
#include <iostream>
#include <csignal>
//------------------------------------------------------------
// 전역 sd_event (signal 핸들러에서 루프 종료용)
//------------------------------------------------------------
static sd_event* g_event = nullptr;
static void onSignal(int)
{
if (g_event)
sd_event_exit(g_event, 0);
}
//------------------------------------------------------------
// TimerService: connection을 주입받아 타이머 관리
//------------------------------------------------------------
class TimerService
{
public:
explicit TimerService(sdbus::IConnection& conn)
: mConn_(conn)
{}
~TimerService()
{
stopTimer();
}
void startTimer()
{
// 기존 타이머 제거
stopTimer();
// ✅ getSdEventLoop()로 sd_event 핸들 가져오기
sd_event* event = mConn_.getSdEventLoop();
if (!event) {
std::cerr << "[Timer] sd_event loop not available\n";
return;
}
uint64_t now{};
sd_event_now(event, CLOCK_MONOTONIC, &now);
int r = sd_event_add_time(
event,
&timerSource_,
CLOCK_MONOTONIC,
now + 5 * 1'000'000, // 5초 후 (microsecond)
0,
onTimer,
this
);
if (r < 0) {
std::cerr << "[Timer] 등록 실패: " << strerror(-r) << "\n";
return;
}
std::cout << "[Timer] 시작 (5초 후 onTimerFired 호출)\n";
}
void stopTimer()
{
if (!timerSource_) return;
sd_event_source_unref(timerSource_);
timerSource_ = nullptr;
std::cout << "[Timer] 해제\n";
}
private:
void onTimerFired()
{
std::cout << "[Timer] 5초 경과 → onTimerFired 실행!\n";
// timerSource_ 정리 (단발성)
stopTimer();
// 원하는 작업 수행
std::cout << "[Timer] 작업 완료\n";
}
static int onTimer(sd_event_source* source,
uint64_t usec,
void* userdata)
{
auto* self = static_cast<TimerService*>(userdata);
self->onTimerFired();
return 0;
}
sdbus::IConnection& mConn_;
sd_event_source* timerSource_{nullptr};
};
//------------------------------------------------------------
// main
//------------------------------------------------------------
int main()
{
// 1. sd_event 생성
sd_event* event{};
int r = sd_event_default(&event);
if (r < 0) {
std::cerr << "sd_event_default failed: " << strerror(-r) << "\n";
return 1;
}
g_event = event;
// 2. SIGINT/SIGTERM 처리
std::signal(SIGINT, onSignal);
std::signal(SIGTERM, onSignal);
// 3. D-Bus connection 생성
auto conn = sdbus::createSessionBusConnection(
sdbus::ServiceName{"org.example.Calculator"}
);
// 4. ✅ sdbus connection을 sd_event에 attach
conn->attachSdEventLoop(event);
// 5. TimerService 생성 및 타이머 시작
// attachSdEventLoop() 이후에 getSdEventLoop()가 유효한 포인터 반환
TimerService timerService{*conn};
timerService.startTimer();
std::cout << "[Server] Running... (Ctrl+C to stop)\n";
// 6. ✅ sd_event 루프 실행 (sdbus + 타이머 통합)
r = sd_event_loop(event);
if (r < 0) {
std::cerr << "sd_event_loop failed: " << strerror(-r) << "\n";
}
// 7. 정리
conn->detachSdEventLoop();
sd_event_unref(event);
g_event = nullptr;
std::cout << "[Server] Stopped\n";
return 0;
}
# 빌드
mkdir build && cd build
cmake ..
make -j$(nproc)
# 실행
./server
#include <sdbus-c++/sdbus-c++.h>
#include <systemd/sd-event.h>
#include <iostream>
class TimerService
{
public:
// ✅ 생성자에서 connection 주입
explicit TimerService(sdbus::IConnection& conn)
: mConnection_(conn)
{}
~TimerService()
{
stopTimer();
}
void startTimer()
{
stopTimer();
// ✅ 주입받은 connection으로 getEventLoopHandle() 호출
sd_event* event = static_cast<sd_event*>(
mConnection_.getEventLoopHandle()
);
uint64_t now{};
sd_event_now(event, CLOCK_MONOTONIC, &now);
int r = sd_event_add_time(
event,
&timerSource_,
CLOCK_MONOTONIC,
now + 5 * 1'000'000,
0,
onTimer,
this
);
if (r < 0) {
std::cerr << "[Timer] 등록 실패: " << strerror(-r) << "\n";
return;
}
std::cout << "[Timer] 시작 (5초 후 실행)\n";
}
void stopTimer()
{
if (!timerSource_) return;
sd_event_source_unref(timerSource_);
timerSource_ = nullptr;
std::cout << "[Timer] 해제\n";
}
private:
void onTimerFired()
{
std::cout << "[Timer] 5초 경과 → onTimerFired 실행!\n";
stopTimer();
// 원하는 작업 수행
}
static int onTimer(sd_event_source* source,
uint64_t usec,
void* userdata)
{
auto* self = static_cast<TimerService*>(userdata);
self->onTimerFired();
return 0;
}
sdbus::IConnection& mConnection_; // ✅ 주입받은 connection
sd_event_source* timerSource_{nullptr};
};int main()
{
auto conn = sdbus::createSessionBusConnection(
sdbus::ServiceName{"org.example.Calculator"}
);
// ✅ 같은 connection 주입
TimerService timerService{*conn};
timerService.startTimer();
conn->enterEventLoop();
return 0;
}
타이머해제방법
// ✅ 방법 1: SD_EVENT_OFF로 비활성화 (source는 유지)
sd_event_source_set_enabled(timerSource_, SD_EVENT_OFF);
// ✅ 방법 2: unref로 완전 제거
sd_event_source_unref(timerSource_);
timerSource_ = nullptr;
class CalculatorImpl
: public sdbus::AdaptorInterfaces<org::example::Calculator_adaptor>
{
public:
CalculatorImpl(sdbus::IConnection& conn, sdbus::ObjectPath path)
: AdaptorInterfaces(conn, std::move(path))
{
registerAdaptor();
setupTimer();
}
~CalculatorImpl()
{
stopTimer();
unregisterAdaptor();
}
protected:
int32_t Add(const int32_t& a, const int32_t& b) override
{
// Add 호출 시 타이머 시작
startTimer();
return a + b;
}
int32_t Sub(const int32_t& a, const int32_t& b) override
{
// Sub 호출 시 타이머 해제
stopTimer();
return a - b;
}
private:
void setupTimer()
{
sd_event* event = static_cast<sd_event*>(
getObject().getConnection().getEventLoopHandle()
);
uint64_t now{};
sd_event_now(event, CLOCK_MONOTONIC, &now);
sd_event_add_time(
event,
&timerSource_,
CLOCK_MONOTONIC,
now + 5 * 1'000'000,
0,
onTimer,
this
);
// ✅ 처음엔 비활성화 상태로 등록
sd_event_source_set_enabled(timerSource_, SD_EVENT_OFF);
std::cout << "[Timer] Registered (inactive)\n";
}
// ✅ 타이머 시작
void startTimer()
{
if (!timerSource_) return;
sd_event* event = static_cast<sd_event*>(
getObject().getConnection().getEventLoopHandle()
);
// 현재 시간 기준으로 재설정
uint64_t now{};
sd_event_now(event, CLOCK_MONOTONIC, &now);
sd_event_source_set_time(timerSource_, now + 5 * 1'000'000);
sd_event_source_set_enabled(timerSource_, SD_EVENT_ONESHOT);
std::cout << "[Timer] Started\n";
}
// ✅ 타이머 해제
void stopTimer()
{
if (!timerSource_) return;
// 방법 1: 비활성화만 (재사용 가능)
sd_event_source_set_enabled(timerSource_, SD_EVENT_OFF);
std::cout << "[Timer] Stopped\n";
// 방법 2: 완전 제거 (재사용 불가)
// sd_event_source_unref(timerSource_);
// timerSource_ = nullptr;
}
static int onTimer(sd_event_source* source,
uint64_t usec,
void* userdata)
{
auto* self = static_cast<CalculatorImpl*>(userdata);
std::cout << "[Timer] Fired!\n";
// 반복 타이머
sd_event_source_set_time(source, usec + 5 * 1'000'000);
sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
return 0;
}
sd_event_source* timerSource_{nullptr};
};SD_EVENT_OFF vs unref 비교
항목
SD_EVENT_OFF
unref
source 메모리
유지
해제
재시작 가능
✅ set_enabled(ONESHOT)
❌ 재등록 필요
적합한 상황
일시 중지 후 재사용
완전히 제거
일시적으로 멈췄다가 다시 쓸 거라면 SD_EVENT_OFF, 완전히 제거할 거라면 unref 를 사용하면 됩니다.
void setupTimer()
{
sd_event* event = static_cast<sd_event*>(
getObject().getConnection().getEventLoopHandle()
);
uint64_t now{};
sd_event_now(event, CLOCK_MONOTONIC, &now);
// ✅ 람다를 heap에 저장 후 userdata로 전달
auto* callback = new std::function<void(sd_event_source*, uint64_t)>(
[this](sd_event_source* source, uint64_t usec) {
std::cout << "[Timer] Fired!\n";
// 원하는 작업
emitSubResult(42);
// 반복 타이머
sd_event_source_set_time(source, usec + 5 * 1'000'000);
sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
}
);
sd_event_add_time(
event,
&timerSource_,
CLOCK_MONOTONIC,
now + 5 * 1'000'000,
0,
// ✅ wrapper: userdata를 std::function으로 캐스팅해서 호출
[](sd_event_source* source, uint64_t usec, void* userdata) -> int {
auto* cb = static_cast<std::function<void(sd_event_source*, uint64_t)>*>(userdata);
(*cb)(source, usec);
return 0;
},
callback // userdata
);
// ✅ source 소멸 시 람다 자동 정리
sd_event_source_set_userdata(timerSource_, callback);
sd_event_source_set_destroy_callback(timerSource_,
[](void* userdata) {
auto* cb = static_cast<std::function<void(sd_event_source*, uint64_t)>*>(userdata);
delete cb;
}
);
}
더깔끔하게ㅜ핼퍼로
// TimerHelper.h
#pragma once
#include <systemd/sd-event.h>
#include <functional>
#include <stdexcept>
inline sd_event_source* addTimer(
sd_event* event,
uint64_t intervalUs, // microsecond
std::function<void(sd_event_source*, uint64_t)> callback)
{
uint64_t now{};
sd_event_now(event, CLOCK_MONOTONIC, &now);
auto* cb = new std::function<void(sd_event_source*, uint64_t)>(
std::move(callback)
);
sd_event_source* source{nullptr};
int r = sd_event_add_time(
event,
&source,
CLOCK_MONOTONIC,
now + intervalUs,
0,
[](sd_event_source* s, uint64_t usec, void* userdata) -> int {
auto* cb = static_cast<
std::function<void(sd_event_source*, uint64_t)>*>(userdata);
(*cb)(s, usec);
return 0;
},
cb
);
if (r < 0) {
delete cb;
throw std::runtime_error("Failed to add timer");
}
sd_event_source_set_destroy_callback(source,
[](void* userdata) {
delete static_cast<
std::function<void(sd_event_source*, uint64_t)>*>(userdata);
}
);
return source;
}헬퍼함수 사용예시
#include "TimerHelper.h"
void setupTimer()
{
sd_event* event = static_cast<sd_event*>(
getObject().getConnection().getEventLoopHandle()
);
// ✅ static 함수, this 전달 없이 깔끔하게 사용
timerSource_ = addTimer(
event,
5 * 1'000'000, // 5초
[this](sd_event_source* source, uint64_t usec) {
std::cout << "[Timer] Fired!\n";
emitSubResult(42);
// 반복
sd_event_source_set_time(source, usec + 5 * 1'000'000);
sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
}
);
}