signalsdbusgdbus

gdbus signal block in method call


I am trying to send the signal on dbus (g_dbus_connection_emit_signal), inside the method call. I have a client program which communicates with server program using dbus. when I call a method on server side, it redirects to that method function and while executing that method, in between I'm sending signal. But I'm receiving signal after completion of that method, that's very late.

I am not getting why this is happening inside dbus method call.

the problem is both signals are hitting at the client end at the same time,

In the below code -> _apply_ota() is the method which is called by client.

**
 * Handle all method calls
 * */
static void handle_method_call (GDBusConnection *connection,
                    const gchar           *sender,
                    const gchar           *object_path,
                    const gchar           *interface_name,
                    const gchar           *method_name,
                    GVariant              *parameters,
                    GDBusMethodInvocation *invocation,
                    gpointer               user_data)
{
#if DBG_SRV_METHOD
    fprintf(stdout,"method_call - %s %s\r\n", method_name,g_variant_get_type_string (parameters));
#endif
    char *response = NULL;
    /*
     * Get Config
     * */
    if (g_strcmp0(method_name, "apply_ota") == 0) {
        cJSON * config = NULL;
        ERROR_CODE status = _apply_ota();
    }else{
        g_dbus_method_invocation_return_dbus_error (invocation,SERVICE_ERROR,"SNAP ! Unhandled Method ..");
    }

    /*Send the response*/
    if(response != NULL){
        g_dbus_method_invocation_return_value(invocation,g_variant_new("(s)", response));
        if (response != NULL)
            free(response);
    }else{
        g_dbus_method_invocation_return_dbus_error (invocation,SERVICE_ERROR,"NULL Response");
    }
}
/*
 * Echo Signal Handlers
 * */
static void service_signal_handler (GDBusConnection  *connection,
                                const gchar      *sender_name,
                                const gchar      *object_path,
                                const gchar      *interface_name,
                                const gchar      *signal_name,
                                GVariant         *parameters,
                                gpointer         user_data)
{
#if DBG_SRV_SIGNALS
    printf("ECHO Signal - %s%s\r\n",signal_name,g_variant_get_type_string(parameters));
#endif

    /*
     * You are hearing your own signals
     * Echo the signals to WEB Clients
     * */
    cJSON * msg = NULL;
    msg = cJSON_CreateObject();
    if (msg != NULL) {
        cJSON_AddItemToObject(msg, KEY_MSG_TYPE,
                cJSON_CreateString(VAL_SIGNAL));
        cJSON_AddItemToObject(msg, KEY_SERVICE_NAME,
                cJSON_CreateString(SERVICE_NAME));
        cJSON_AddItemToObject(msg, KEY_MESSAGE,
                cJSON_CreateString(signal_name));

        /*
         * Check if there are parameters
         * We only expect one parameter in signal
         * */
        if (g_strcmp0(g_variant_get_type_string(parameters), "(s)") == 0) {
            const gchar * param_json_str;
            g_variant_get(parameters, "(&s)", &param_json_str);
            if (param_json_str != NULL) {
                cJSON * param_json = NULL;
                param_json = cJSON_Parse(param_json_str);
                if (param_json != NULL) {
                    cJSON_AddItemToObject(msg, KEY_ARG, param_json);
                } else {
                    fprintf(stderr, "Failed to parse signal parameters \r\n");

                }
            }
        }


        /*Broadcast cast signal to WEB Clients*/
        if (!ws_send_brodcast(msg)) {
            fprintf(stderr, "Failed to send broadcast!\r\n");
        }
        cJSON_Delete(msg);
    }
}
bool service_emit_json_signal(char * signal_name, cJSON * msg_json){
#if DBG_SRV_SIGNALS
    printf("service_emit_json_signal - %s \r\n", signal_name);
    printf_json(msg_json,"msg = \r\n");
#endif
    bool status = false;
    GError *error = NULL;
    GDBusConnection *c = NULL;
    GVariant * parameters;
    char* msg = NULL;
    c = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
    if(c!= NULL){
        msg = cJSON_PrintUnformatted(msg_json);
        parameters = g_variant_new("(s)",msg);
        if (parameters != NULL){
            parameters = g_variant_ref_sink (parameters);
            if(parameters != NULL){
                /*Ready to send Signal*/
                status = g_dbus_connection_emit_signal(c,
                                                NULL,
                                                MAIN_INTERFACE,
                                                SERVICE_NAME,
                                                signal_name,
                                                parameters,
                                                NULL);
                if(!status){
                    fprintf(stderr,"Error Sending  Signal - %s\r\n", error->message);
                }
            }
        }
    }else {
        /*Failed to connect to the DBUS*/
        fprintf(stderr, "Error connecting to BUS - %s\r\n", error->message);
    }

    /*Clean UP*/
    if (error != NULL)
        g_error_free(error);
    if (msg != NULL)
        free(msg);
    if (parameters != NULL)
        g_variant_unref(parameters);
    if (c != NULL)
        g_object_unref(c);
    return status;
}

bool emit_ota_updating_signal(OTA_STATUS status){

    bool ret = false;
    cJSON * signal_json = cJSON_CreateObject();
    if(signal_json != NULL){
        cJSON_AddItemToObject(signal_json, KEY_OTA, cJSON_CreateNumber(status));
        ret = service_emit_json_signal(SIG_OTA_UPDATING,signal_json);
        cJSON_Delete(signal_json);
    }
    return ret;
}

ERROR_CODE _apply_ota(){

    
    if(emit_ota_downloading_signal(OTA_DOWNLOADING)){

        dcc_download_ota_file(ota->ota_file_url);
    }else{
        goto exit;
    }

    if(emit_ota_updating_signal(OTA_UPDATING)){

            dcc_run_ota_update_script();
    }else{
        goto exit;
    }




    return ret;
}

.


Solution

  • Your code sample is not a minimal reproducer (it’s very complex, and yet still missing various symbols so nobody but you can compile and test it).

    However, from what I can see, it looks like the code has a problem with blocking while handling the method call. It looks like _apply_ota() is likely to take several seconds — dcc_download_ota_file() and dcc_run_ota_update_script() look like they are blocking functions which will take several seconds (or longer) each to run. These are being executed in the same thread as is handling the incoming D-Bus method call. If they block that thread (preventing its main loop from iterating and handling inputs), they may block outgoing signal emissions.

    You may want to read these guides on how threading and asynchronous function calls are intended to work with GLib/GIO:

    And these guidelines on D-Bus API design:

    The standard way for code like this to work, where a method call triggers a long-running operation, is for the method call to return immediately, and leave the operation running in the background. When the operation is complete, it will emit a signal. This is what NetworkManager does, for example, with method calls which change network state.

    Choose this method if the state change which results from _apply_ota() being called should be announced to all peers of your D-Bus service. Perhaps via a “updated to new version” signal, if I’m interpreting ‘ota’ correctly as ‘over-the-air update’.

    Alternatively, the method call could return a new D-Bus object path pointing to a D-Bus object which represents the ongoing operation. The object could provide properties representing progress in the operation, or a method to cancel it. This is what the freedesktop portal API does, for example.

    Choose this method if the state change which results from _apply_ota() being called is only relevant to the caller of _apply_ota(), or if the caller needs to be able to know when the state change from that specific _apply_ota() call is complete. (A general signal emission, as in the first pattern, can’t be reliably matched back to a specific method call.)

    So I would suggest refactoring the code to follow one of those two design patterns more closely. However, this is based only on what I can see in your question, which is not the complete picture.