Как сделать вызов на стороне сервера D-Bus асинхронным?

Для моего проекта я использую DBUS в качестве IPC для взаимодействия между приложением QT (на стороне клиента) и моим сервисным демоном (на стороне сервера - GIO / GDBUS). На стороне клиента методы вызываются асинхронно с использованием QDBusPendingCallWatcher.

Однако на стороне сервера, как сделать вызов метода асинхронным?, Насколько я понимаю, "g_dbus_method_invocation_return_value" будет возвращать ответ с выходными параметрами, вызывая вызов метода как синхронизацию.

Один из способов, который я могу придумать, - это вернуть промежуточный ответ, используя g_dbus_method_invocation_return_value, а затем, как только будет получен окончательный ответ, испустить окончательный ответ в качестве сигнала.

Образец кода:-

//Method invocation      

static void handle_method_call(GDBusConnection *conn,
                               const gchar *sender,
                               const gchar *object_path,
                               const gchar *interface_name,
                               const gchar *method_name,
                               GVariant *parameters,
                               GDBusMethodInvocation *invocation,
                               gpointer user_data)

{

    if (!g_strcmp0(method_name, "Scan")) {
        guint8 radiotype = 0;
        guint8temp_resp = 0 ;
        g_variant_get(parameters, "(y)", radiotype);
        // Async Function Call and takes very 
        // long time to return final response as needs to scan whole radio band 
        temp_resp = RadioScan(radiotype); 

        g_dbus_method_invocation_return_value(invocation, g_variant_new("(y", temp_resp)); // return intermediate response to client  and when final response is received then  emit the signal
        g_free(response);
    }
}
// Final scan response callback function
static gboolean on_scanfinalresponse_cb (gpointer user_data)
{

    GDBusConnection *connection = G_DBUS_CONNECTION (user_data);

    GVariantBuilder *builder;

    GVariantBuilder *invalidated_builder;

    GError *error;
    g_dbus_connection_emit_signal (connection,
                                   NULL,
                                   "/org/example/test",
                                   "org.example.test",
                                   "ScanFinalResponse",
                                   g_variant_new ("(s)",
                                                  builder),
                                   &error);
    g_assert_no_error (error);
    return TRUE;
}

Пожалуйста, дайте мне знать, правильный ли это подход или есть ли лучший способ добиться асинхронного вызова для вышеуказанного случая?

1 ответ

Однако на стороне сервера, как сделать вызов метода асинхронным?

Есть две концепции, на которые вы можете ссылаться с помощью "асинхронности", и D-Bus (или GDBus) не ограничивают вас ни одной из них.

  1. Дизайн API: Если вы можете изменить действительный API, вы, конечно, можете создать метод, который возвращает немедленно, а затем "возвращает значения" через изменения свойств или сигналы. Это может быть хорошей идеей для конкретных случаев, таких как вызов сканирования Wi-Fi.

  2. Реализация метода: ваш API может иметь метод, который занимает много времени перед возвратом, и он может быть реализован "асинхронно" в том смысле, что ваш сервис не блокируется, пока метод не возвращен - другие методы могут быть вызваны, и сигналы и изменения свойств могут произойти в течение этого времени. g_dbus_method_invocation_return_* функции могут быть использованы для реализации этого. Создание долго работающих методов D-Bus не является проблемой, если они задокументированы как таковые: клиенты могут обрабатывать вызовы асинхронно и даже увеличивать время ожидания вызова метода по умолчанию, если это необходимо.

В контексте примера кода, который вы опубликовали, первое, что вам нужно сделать, это сделать вызов RadioScan() асинхронным или выполнить вызов в другом потоке: это гарантирует, что ваша служба будет отвечать на запросы во время вызова.

После того, как ваш RadioScan станет асинхронным, будет легко реализовать любое решение. Если метод RadioScan() имеет четко определенное возвращаемое значение (и вы не хотите возвращать промежуточные результаты раньше), я бы выбрал обычный вызов метода, который просто занимает больше времени:

static void handle_method_call(GDBusConnection *conn, ...)
{
    if (!g_strcmp0(method_name, "Scan")) {
        // start the async scan (maybe using another thread): this should 
        // return immediately and call the callback when scan is done
        start_radio_scan(..., radio_scan_finished_cb); 
        // do not return the invocation here, just store a pointer to it          
    }
}

static void radio_scan_finished_cb (...)
{
    // return the D-Bbus method call with the invocation that was stored earlier
    g_dbus_method_invocation_return_value(invocation, ...)
}

Если ваши результаты сканирования действительно приходят со временем (например, первый результат через 1 секунду, больше результатов через 3 секунды), может иметь смысл возвращать результаты в виде сигналов, когда они доступны, а затем просто возвращать вызов метода как признак того, что сканирование наконец закончено

Наличие единственного сигнала "ScanFinalResponse", безусловно, возможно, но я не думаю, что в этом есть какое-то преимущество перед вызовом метода, который просто занимает больше времени.

Другие вопросы по тегам