signals - 方法调用中的 gdbus 信号块
问题描述
我正在尝试在方法调用内的 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,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)", ¶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,
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;
}
.
解决方案
您的代码示例不是最小的复制器(它非常复杂,但仍然缺少各种符号,所以除了您之外没有人可以编译和测试它)。
但是,据我所见,代码在处理方法调用时似乎存在阻塞问题。看起来_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 设计的指南:
像这样的代码工作的标准方式是,方法调用触发长时间运行的操作,方法调用立即返回,并让操作在后台运行。当操作完成时,它会发出一个信号。这就是NetworkManager 所做的,例如,改变网络状态的方法调用。
如果调用导致的状态变化_apply_ota()
应该向 D-Bus 服务的所有对等方公布,请选择此方法。如果我将“ota”正确解释为“无线更新”,也许是通过“更新到新版本”信号。
或者,方法调用可以返回一个新的 D-Bus 对象路径,该路径指向一个代表正在进行的操作的 D-Bus 对象。该对象可以提供表示操作进度的属性或取消操作的方法。例如,这就是freedesktop 门户 API所做的。
如果调用导致的状态变化_apply_ota()
仅与调用者相关_apply_ota()
,或者调用者需要能够知道该特定_apply_ota()
调用的状态变化何时完成,则选择此方法。(与第一种模式一样,一般的信号发射不能可靠地匹配回特定的方法调用。)
所以我建议重构代码以更紧密地遵循这两种设计模式之一。但是,这仅基于我在您的问题中可以看到的内容,这不是完整的情况。
推荐阅读
- maven - 我在 Checkstyle 配置文件中的哪里放置模块元素?
- javascript - Javascript:用链调用 new 实例化一个对象
- c++ - 选择不同的枢轴
- javascript - 将方法应用于 javascript 对象中匹配条件的所有键
- java - 删除视图的子视图然后读取它后,视图将无法正确显示
- python - 如何将 1 行 x 列向量重塑为(900、1600、4)?
- powershell - GNU Make 和 Windows Powershell:“Remove-Item -ErrorAction Ignore”不起作用
- functional-programming - 更新函数内的变量
- python - TypeError:无法从“列表”解析
- sql-server - 如何在同一张表的SQL中合并列