반응형

 
 

예제 1. g_dbus_interface_skeleton_unexport 및 g_signal_handler_disconnect 이용

일반적으로는 g_dbus_interface_skeleton_unexport()를 호출하면 GObject의 참조 카운팅 시스템이 자동으로 정리를 처리하지만, 명시적으로 시그널 연결을 해제하는 것이 더 안전하다.
 
왜 명시적으로 해제해야 하나?

  • 메모리 누수 방지: 순환 참조가 있을 경우 GObject가 자동으로 해제되지 않을 수 있음
  • 명확한 리소스 관리: 코드의 의도가 명확해지고 디버깅이 쉬워짐
  • 베스트 프랙티스: GLib/GObject 프로그래밍에서 권장되는 방식
  • 예측 가능한 동작: 종료 시퀀스를 명확하게 제어 가능

실제로 많은 경우 GObject의 참조 카운팅이 자동으로 정리하지만, 복잡한 애플리케이션에서는 명시적인 해제가 더 안전하다.
 

#include <gio/gio.h>
#include <glib.h>

// D-Bus 인터페이스 XML 정의
static const gchar introspection_xml[] =
  "<node>"
  "  <interface name='org.example.Calculator'>"
  "    <method name='Add'>"
  "      <arg type='i' name='a' direction='in'/>"
  "      <arg type='i' name='b' direction='in'/>"
  "      <arg type='i' name='result' direction='out'/>"
  "    </method>"
  "    <method name='Sub'>"
  "      <arg type='i' name='a' direction='in'/>"
  "      <arg type='i' name='b' direction='in'/>"
  "      <arg type='i' name='result' direction='out'/>"
  "    </method>"
  "  </interface>"
  "</node>";

// Add 메서드 핸들러
static gboolean
on_handle_add(GDBusInterfaceSkeleton *skeleton,
              GDBusMethodInvocation *invocation,
              gint a,
              gint b,
              gpointer user_data)
{
    gint result = a + b;
    g_print("Add called: %d + %d = %d\n", a, b, result);
    
    // 결과 반환
    g_dbus_method_invocation_return_value(invocation,
        g_variant_new("(i)", result));
    
    return TRUE;
}

// Sub 메서드 핸들러
static gboolean
on_handle_sub(GDBusInterfaceSkeleton *skeleton,
              GDBusMethodInvocation *invocation,
              gint a,
              gint b,
              gpointer user_data)
{
    gint result = a - b;
    g_print("Sub called: %d - %d = %d\n", a, b, result);
    
    // 결과 반환
    g_dbus_method_invocation_return_value(invocation,
        g_variant_new("(i)", result));
    
    return TRUE;
}

// 시그널 핸들러 ID를 저장할 구조체
typedef struct {
    gulong add_handler_id;
    gulong sub_handler_id;
} SignalHandlers;

// 버스 획득 콜백
static void
on_bus_acquired(GDBusConnection *connection,
                const gchar *name,
                gpointer user_data)
{
    GDBusInterfaceSkeleton *skeleton;
    GDBusNodeInfo *node_info;
    SignalHandlers *handlers;
    GError *error = NULL;
    
    g_print("Bus acquired: %s\n", name);
    
    // introspection 데이터 파싱
    node_info = g_dbus_node_info_new_for_xml(introspection_xml, &error);
    if (error) {
        g_printerr("Error parsing introspection XML: %s\n", error->message);
        g_error_free(error);
        return;
    }
    
    // 인터페이스 스켈레톤 생성
    skeleton = g_dbus_interface_skeleton_new(
        g_dbus_node_info_lookup_interface(node_info, "org.example.Calculator"));
    
    // 시그널 핸들러 구조체 생성
    handlers = g_new0(SignalHandlers, 1);
    
    // 시그널 연결 및 핸들러 ID 저장
    handlers->add_handler_id = g_signal_connect(skeleton, "handle-add",
                                                 G_CALLBACK(on_handle_add), NULL);
    handlers->sub_handler_id = g_signal_connect(skeleton, "handle-sub",
                                                 G_CALLBACK(on_handle_sub), NULL);
    
    // 인터페이스 export
    if (!g_dbus_interface_skeleton_export(skeleton,
                                          connection,
                                          "/org/example/Calculator",
                                          &error)) {
        g_printerr("Error exporting interface: %s\n", error->message);
        g_error_free(error);
        g_object_unref(skeleton);
        g_free(handlers);
        g_dbus_node_info_unref(node_info);
        return;
    }
    
    g_print("Interface exported at /org/example/Calculator\n");
    
    // user_data에 skeleton과 handlers 저장
    g_object_set_data_full(G_OBJECT(connection), "skeleton",
                          skeleton, g_object_unref);
    g_object_set_data_full(G_OBJECT(connection), "handlers",
                          handlers, g_free);
    
    g_dbus_node_info_unref(node_info);
}

// 이름 획득 콜백
static void
on_name_acquired(GDBusConnection *connection,
                 const gchar *name,
                 gpointer user_data)
{
    g_print("Name acquired: %s\n", name);
}

// 이름 손실 콜백
static void
on_name_lost(GDBusConnection *connection,
             const gchar *name,
             gpointer user_data)
{
    g_print("Name lost: %s\n", name);
    
    if (connection) {
        GDBusInterfaceSkeleton *skeleton;
        SignalHandlers *handlers;
        
        skeleton = g_object_get_data(G_OBJECT(connection), "skeleton");
        handlers = g_object_get_data(G_OBJECT(connection), "handlers");
        
        if (skeleton && handlers) {
            g_print("Disconnecting signal handlers...\n");
            
            // 시그널 핸들러 연결 해제
            g_signal_handler_disconnect(skeleton, handlers->add_handler_id);
            g_signal_handler_disconnect(skeleton, handlers->sub_handler_id);
            
            g_print("Unexporting interface...\n");
            g_dbus_interface_skeleton_unexport(skeleton);
        }
    }
    
    // 메인 루프 종료
    GMainLoop *loop = (GMainLoop *)user_data;
    if (loop) {
        g_main_loop_quit(loop);
    }
}

// SIGINT 핸들러
static gboolean
on_signal(gpointer user_data)
{
    g_print("\nReceived signal, shutting down...\n");
    GMainLoop *loop = (GMainLoop *)user_data;
    g_main_loop_quit(loop);
    return FALSE;
}

int main(int argc, char *argv[])
{
    GMainLoop *loop;
    guint owner_id;
    
    loop = g_main_loop_new(NULL, FALSE);
    
    // SIGINT 핸들러 설정
    g_unix_signal_add(SIGINT, on_signal, loop);
    
    // D-Bus 이름 소유
    owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
                              "org.example.Calculator",
                              G_BUS_NAME_OWNER_FLAGS_NONE,
                              on_bus_acquired,
                              on_name_acquired,
                              on_name_lost,
                              loop,
                              NULL);
    
    g_print("Calculator server started. Press Ctrl+C to exit.\n");
    
    // 메인 루프 실행
    g_main_loop_run(loop);
    
    // 정리
    g_bus_unown_name(owner_id);
    g_main_loop_unref(loop);
    
    g_print("Server stopped.\n");
    
    return 0;
}

 
 
 

예제 2. g_signal_handlers_disconnect_by_func 이용

g_signal_handlers_disconnect_by_func() 동작 방식
이 함수는 다음 조건이 모두 일치하는 시그널 핸들러를 찾아 연결을 해제한다.
 

  • 콜백 함수: G_CALLBACK(on_handle_add)
  • user_data: NULL (연결 시 전달한 값과 일치해야 함)

 
만약 같은 함수를 여러 번 연결했다면 모두 해제되므로, 특정 핸들러만 해제하려면 g_signal_handler_disconnect()와 핸들러 ID를 사용해야 한다. 하지만 이 경우처럼 각 시그널에 하나의 핸들러만 연결했다면 g_signal_handlers_disconnect_by_func()가 더 깔끔한 선택이다.
 
 

#include <gio/gio.h>
#include <glib.h>

// D-Bus 인터페이스 XML 정의
static const gchar introspection_xml[] =
  "<node>"
  "  <interface name='org.example.Calculator'>"
  "    <method name='Add'>"
  "      <arg type='i' name='a' direction='in'/>"
  "      <arg type='i' name='b' direction='in'/>"
  "      <arg type='i' name='result' direction='out'/>"
  "    </method>"
  "    <method name='Sub'>"
  "      <arg type='i' name='a' direction='in'/>"
  "      <arg type='i' name='b' direction='in'/>"
  "      <arg type='i' name='result' direction='out'/>"
  "    </method>"
  "  </interface>"
  "</node>";

// Add 메서드 핸들러
static gboolean
on_handle_add(GDBusInterfaceSkeleton *skeleton,
              GDBusMethodInvocation *invocation,
              gint a,
              gint b,
              gpointer user_data)
{
    gint result = a + b;
    g_print("Add called: %d + %d = %d\n", a, b, result);
    
    // 결과 반환
    g_dbus_method_invocation_return_value(invocation,
        g_variant_new("(i)", result));
    
    return TRUE;
}

// Sub 메서드 핸들러
static gboolean
on_handle_sub(GDBusInterfaceSkeleton *skeleton,
              GDBusMethodInvocation *invocation,
              gint a,
              gint b,
              gpointer user_data)
{
    gint result = a - b;
    g_print("Sub called: %d - %d = %d\n", a, b, result);
    
    // 결과 반환
    g_dbus_method_invocation_return_value(invocation,
        g_variant_new("(i)", result));
    
    return TRUE;
}

// 버스 획득 콜백
static void
on_bus_acquired(GDBusConnection *connection,
                const gchar *name,
                gpointer user_data)
{
    GDBusInterfaceSkeleton *skeleton;
    GDBusNodeInfo *node_info;
    GError *error = NULL;
    
    g_print("Bus acquired: %s\n", name);
    
    // introspection 데이터 파싱
    node_info = g_dbus_node_info_new_for_xml(introspection_xml, &error);
    if (error) {
        g_printerr("Error parsing introspection XML: %s\n", error->message);
        g_error_free(error);
        return;
    }
    
    // 인터페이스 스켈레톤 생성
    skeleton = g_dbus_interface_skeleton_new(
        g_dbus_node_info_lookup_interface(node_info, "org.example.Calculator"));
    
    // 시그널 연결
    g_signal_connect(skeleton, "handle-add",
                     G_CALLBACK(on_handle_add), NULL);
    g_signal_connect(skeleton, "handle-sub",
                     G_CALLBACK(on_handle_sub), NULL);
    
    // 인터페이스 export
    if (!g_dbus_interface_skeleton_export(skeleton,
                                          connection,
                                          "/org/example/Calculator",
                                          &error)) {
        g_printerr("Error exporting interface: %s\n", error->message);
        g_error_free(error);
        g_object_unref(skeleton);
        g_dbus_node_info_unref(node_info);
        return;
    }
    
    g_print("Interface exported at /org/example/Calculator\n");
    
    // user_data에 skeleton 저장 (나중에 unexport하기 위해)
    g_object_set_data_full(G_OBJECT(connection), "skeleton",
                          skeleton, g_object_unref);
    
    g_dbus_node_info_unref(node_info);
}

// 이름 획득 콜백
static void
on_name_acquired(GDBusConnection *connection,
                 const gchar *name,
                 gpointer user_data)
{
    g_print("Name acquired: %s\n", name);
}

// 이름 손실 콜백
static void
on_name_lost(GDBusConnection *connection,
             const gchar *name,
             gpointer user_data)
{
    g_print("Name lost: %s\n", name);
    
    if (connection) {
        GDBusInterfaceSkeleton *skeleton;
        
        skeleton = g_object_get_data(G_OBJECT(connection), "skeleton");
        
        if (skeleton) {
            g_print("Disconnecting signal handlers...\n");
            
            // 함수 포인터로 시그널 핸들러 연결 해제
            g_signal_handlers_disconnect_by_func(skeleton,
                                                G_CALLBACK(on_handle_add),
                                                NULL);
            g_signal_handlers_disconnect_by_func(skeleton,
                                                G_CALLBACK(on_handle_sub),
                                                NULL);
            
            g_print("Unexporting interface...\n");
            g_dbus_interface_skeleton_unexport(skeleton);
        }
    }
    
    // 메인 루프 종료
    GMainLoop *loop = (GMainLoop *)user_data;
    if (loop) {
        g_main_loop_quit(loop);
    }
}

// SIGINT 핸들러
static gboolean
on_signal(gpointer user_data)
{
    g_print("\nReceived signal, shutting down...\n");
    GMainLoop *loop = (GMainLoop *)user_data;
    g_main_loop_quit(loop);
    return FALSE;
}

int main(int argc, char *argv[])
{
    GMainLoop *loop;
    guint owner_id;
    
    loop = g_main_loop_new(NULL, FALSE);
    
    // SIGINT 핸들러 설정
    g_unix_signal_add(SIGINT, on_signal, loop);
    
    // D-Bus 이름 소유
    owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
                              "org.example.Calculator",
                              G_BUS_NAME_OWNER_FLAGS_NONE,
                              on_bus_acquired,
                              on_name_acquired,
                              on_name_lost,
                              loop,
                              NULL);
    
    g_print("Calculator server started. Press Ctrl+C to exit.\n");
    
    // 메인 루프 실행
    g_main_loop_run(loop);
    
    // 정리
    g_bus_unown_name(owner_id);
    g_main_loop_unref(loop);
    
    g_print("Server stopped.\n");
    
    return 0;
}

반응형