问题描述
我试图在方法调用内部的 dbus (g_dbus_connection_emit_signal) 上发送信号。我有一个使用 dbus 与服务器程序通信的客户端程序。当我在服务器端调用一个方法时,它重定向到该方法函数,并在执行该方法时,在两者之间发送信号。但是我在完成那个方法后收到信号,那很晚了。
我不明白为什么在 dbus 方法调用中会发生这种情况。
问题是两个信号同时到达客户端,
在下面的代码中 -> _apply_ota() 是客户端调用的方法。
**
* 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,"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)",¶m_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,MAIN_INTERFACE,SERVICE_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;
}
.
解决方法
您的代码示例不是 minimal reproducer(它非常复杂,但仍然缺少各种符号,因此只有您可以编译和测试它)。
但是,据我所知,代码在处理方法调用时似乎存在阻塞问题。看起来 _apply_ota()
可能需要几秒钟 - dcc_download_ota_file()
和 dcc_run_ota_update_script()
看起来像是阻塞函数,每个函数都需要几秒钟(或更长时间)才能运行。这些是在处理传入 D-Bus 方法调用的同一线程中执行的。如果它们阻塞该线程(阻止其主循环迭代和处理输入),它们可能会阻塞传出信号发射。
您可能想阅读这些关于线程和异步函数调用如何与 GLib/GIO 一起使用的指南:
- https://developer.gnome.org/programming-guidelines/stable/main-contexts.html.en
- https://developer.gnome.org/programming-guidelines/stable/async-programming.html.en
以及这些关于 D-Bus API 设计的指南:
这样的代码工作的标准方式(方法调用触发长时间运行的操作)是方法调用立即返回,并让操作在后台运行。当操作完成时,它会发出一个信号。例如,这是 what NetworkManager does,具有更改网络状态的方法调用。
如果应向 D-Bus 服务的所有对等方宣布由调用 _apply_ota()
导致的状态更改,请选择此方法。也许通过“更新到新版本”信号,如果我将“ota”正确解释为“无线更新”。
或者,方法调用可以返回一个新的 D-Bus 对象路径,该路径指向一个 D-Bus 对象,该对象代表正在进行的操作。该对象可以提供表示操作进度的属性,或取消它的方法。例如,这就是 freedesktop portal API 的作用。
如果 _apply_ota()
被调用导致的状态更改仅与 _apply_ota()
的调用者相关,或者调用者需要知道状态何时从该特定更改开始,请选择此方法_apply_ota()
调用完成。 (在第一种模式中,一般的信号发射无法可靠地匹配回特定的方法调用。)
所以我建议重构代码以更紧密地遵循这两种设计模式之一。但是,这仅基于我在您的问题中看到的内容,并不是完整的图片。