方法调用中的 gdbus 信号块
gdbus signal block in method call
我正尝试在方法调用内通过 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;
}
.
您的代码示例不是 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 设计的指南:
像这样的代码工作的标准方式,其中一个方法调用触发一个长 运行ning 操作,是立即对 return 的方法调用,然后离开操作 运行宁在后台。当操作完成时,它会发出一个信号。这是 what NetworkManager does,例如,使用改变网络状态的方法调用。
如果应向 D-Bus 服务的所有对等方宣布因调用 _apply_ota()
而导致的状态更改,请选择此方法。也许通过“更新到新版本”信号,如果我将“ota”正确解释为“无线更新”。
或者,方法调用可以 return 指向表示正在进行的操作的 D-Bus 对象的新 D-Bus 对象路径。该对象可以提供表示操作进度的属性或取消它的方法。例如,这就是 freedesktop portal API 所做的。
如果调用 _apply_ota()
导致的状态变化仅与 _apply_ota()
的调用者相关,或者如果调用者需要能够知道状态何时从该特定 _apply_ota()
调用已完成。 (一般信号发射,如第一种模式,无法可靠地匹配回特定方法调用。)
所以我建议重构代码以更紧密地遵循这两种设计模式中的一种。但是,这仅基于我在您的问题中看到的内容,并非完整图片。
我正尝试在方法调用内通过 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;
}
.
您的代码示例不是 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 设计的指南:
像这样的代码工作的标准方式,其中一个方法调用触发一个长 运行ning 操作,是立即对 return 的方法调用,然后离开操作 运行宁在后台。当操作完成时,它会发出一个信号。这是 what NetworkManager does,例如,使用改变网络状态的方法调用。
如果应向 D-Bus 服务的所有对等方宣布因调用 _apply_ota()
而导致的状态更改,请选择此方法。也许通过“更新到新版本”信号,如果我将“ota”正确解释为“无线更新”。
或者,方法调用可以 return 指向表示正在进行的操作的 D-Bus 对象的新 D-Bus 对象路径。该对象可以提供表示操作进度的属性或取消它的方法。例如,这就是 freedesktop portal API 所做的。
如果调用 _apply_ota()
导致的状态变化仅与 _apply_ota()
的调用者相关,或者如果调用者需要能够知道状态何时从该特定 _apply_ota()
调用已完成。 (一般信号发射,如第一种模式,无法可靠地匹配回特定方法调用。)
所以我建议重构代码以更紧密地遵循这两种设计模式中的一种。但是,这仅基于我在您的问题中看到的内容,并非完整图片。