Maemo Linux手机平台系列分析:10 Maemo平台开发之 使用Glib/D-Bus做异步操作

 
 
使用 Glib/D-Bus 做异步操作
 
本部分内容:
  • D-Bus客户端(client)的不同步性
  • (server)服务器端的缓慢运行
  • 使用桩(stub)做异步方法调用
  • 不同步性所引发的问题
  • 使用Glib的封装做异步方法调用
 
D-Bus 客户端 (client) 的不同步性
到目前为止,我们所实现的 RPC 调用都是比较快的,原因在于没有访问运行较慢的服务或者外部的资源。不过,在现实生活中,经常会遇到这种情况。这就需要我们在完成方法调用之前,需要等待外部的服务程序完成之后,才算真正的调用结束。
Glib 封装提供了一种方法调用的形式:调用可以被立即启动,函数调用返回时激发注册的回调函数。
当你的程序需要更新一些状态或者对用户做出响应,使用这种异步操作是非常必要的。否则程序可能由于需要等待 RPC 的返回而被阻塞住,或者不能及时刷新屏幕。另外一种不太推荐的方法:使用多线程。多线程有两个弊端:一难调试;二难同步。这里我们不采用多线程的方式。
为了模拟现实中遇到的执行慢的情况,我们在 server 侧的实现代码中加个延时。通过这个试验,我们就会发现异步 RPC 机制是非常重要的。因为信号也是一种异步机制,为了排除它的干扰,我们把 client 侧的信号去掉,保留 server 侧的信号。
 
使 server 侧程序缓慢执行
Server 侧的代码唯一变动之处:在几个 RPC 函数里面增加延时操作:
/* How many microseconds to delay between each client operation. */
#define SERVER_DELAY_USEC (5*1000000UL)
 
 /*... Listing cut for brevity ...*/
 
gboolean value_object_setvalue1(ValueObject* obj, gint valueIn,
                                                  GError** error) {
 
 dbg("Called (valueIn=%d)", valueIn);
 g_assert(obj != NULL);
 
 dbg("Delaying operation");
 g_usleep(SERVER_DELAY_USEC); //延时操作
 
 /* Compare the current value against old one. If they're the same,
     we don't need to do anything (except return success). */
 if (obj->value1 != valueIn) {
还与前面一样编译 server 侧代码,不过运行时我们就可以注意到 RPC 的延迟现象:
[sbox-CHINOOK_X86: ~/glib-dbus-async] > run-standalone.sh ./server &
server:main Connecting to the Session D-Bus.
server:main Registering the well-known name (org.maemo.Platdev_ex)
server:main RequestName returned 1.
server:main Creating one Value object.
server:value_object_class_init: Called
server:value_object_class_init: Creating signals
server:value_object_class_init: Binding to GLib/D-Bus
server:value_object_class_init: Done
server:value_object_init: Called
server:main Registering it on the D-Bus.
server:main Ready to serve requests (daemonizing).
server: Not daemonizing (built with NO_DAEMON-build define)
[sbox-CHINOOK_X86: ~/glib-dbus-async] > time run-standalone.sh dbus-send /
 --type=method_call --print-reply --dest=org.maemo.Platdev_ex /
 /GlobalValue org.maemo.Value.getvalue1
server:value_object_getvalue1: Called (internal value1 is 0)
server:value_object_getvalue1: Delaying operation
method return sender=:1.54 -> dest=:1.56
 int32 0
 
real    0m5.066s
user    0m0.004s
sys     0m0.056s
上面,我们使用 ”time” 命令来测量走表时间(也称实际时间)、代码运行时间以及系统调用时间。这里,我们对 “real” 时间比较感兴趣,这里可以看出程序的确延迟了 5 秒!当然,这里的延迟不是很精确。
下面我们做另外一个试验:当前已经有函数正在运行,而另外一个客户端程序也要调用这个函数,该怎么处理这种情况呢?一个比较简单的测试:运行两次命令,前一次在后台运行:
[sbox-CHINOOK_X86: ~/glib-dbus-async] > time run-standalone.sh dbus-send /
 --type=method_call --print-reply --dest=org.maemo.Platdev_ex /
 /GlobalValue org.maemo.Value.getvalue1 & //后台运行,运行后处于延迟状态,下面马上再执行一次该命令
[2] 17010
server:value_object_getvalue1: Called (internal value1 is 0)
server:value_object_getvalue1: Delaying operation
[sbox-CHINOOK_X86: ~/glib-dbus-async] > time run-standalone.sh dbus-send /
 --type=method_call --print-reply --dest=org.maemo.Platdev_ex /
 /GlobalValue org.maemo.Value.getvalue1 //再运行一次
method return sender=:1.54 -> dest=:1.57
 int32 0
 
real    0m5.176s
user    0m0.008s
sys     0m0.092s
server:value_object_getvalue1: Called (internal value1 is 0)
server:value_object_getvalue1: Delaying operation
method return sender=:1.54 -> dest=:1.58
 int32 0
 
real    0m9.852s
user    0m0.004s
sys     0m0.052s
从上面的输出,我们可以看到第一个客户程序被延迟了 5 秒才得以执行,第二个被延迟了更长的时间,这说明 server 侧的程序一次只能处理一个 client 的调用请求,后面的请求将会排队执行,正是这种排队操作,导致延迟会累加。
后面我们将会碰到 server 侧的并发问题,不过到目前为止,我们希望 client 侧的程序在等待 server 侧响应的同时,能够继续 正常 的工作。我们这个例子程序,没有用户界面,因此,所谓的 正常 就是:能够继续等待响应。不过对于图形界面程序,异步操作使得程序能够及时响应用户的输入。 D-Bus 本身并不支持直接取消 server 侧已经运行的方法调用。如果你要增加这种中途取消调用的功能,你需要自行在 server 增加一个单独的函数调用。不幸的是: server 侧一次只能处理一个操作,所以目前 server 根本不能支持中途取消方法调用的操作。
 
使用 D-Bus 工具生成的 桩函数 来实现异步方法调用
使用 glib-bindings-tool , XML 文件生成 stub 头文件时,这个工具已经替你生成了异步方法调用的接口。剩下的工作就是实现回调函数、处理返回错误和启动方法调用了。
/* Pull in the client stubs that were generated with
   dbus-binding-tool */
#include "value-client-stub.h" //直接使用工具生成的桩函数原型
我们实现具体的回调函数:
/**
 * 这个回调函数会在三种情况下被调用:1:正常结束;2:超时;3:失败
 * 程序运行一段时间后,你可以看到这三种情况。
 *
 * This function will be called when the async setvalue1 will either
 * complete, timeout or fail (our server however does not signal
 * errors, but the client D-Bus library might). When this example
 * program is left running for a while, you will see all three cases.
 *
 * The prototype must match the one generated by the dbus-binding-tool
 * (org_maemo_Value_setvalue1_reply).
 *
 * Since there is no return value from the RPC, the only useful
 * parameter that we get is the error object, which we'll check.
 * If error is NULL, that means no error. Otherwise the RPC call
 * failed and we should check what the cause was.
 */
static void setValue1Completed(DBusGProxy* proxy, GError *error,
                                                  gpointer userData) {
 
 g_print(PROGNAME ":%s:setValue1Completed/n", timestamp());
 if (error != NULL) {
    g_printerr(PROGNAME "       ERROR: %s/n", error->message);
    /* We need to release the error object since the stub code does
       not do it automatically. */
    g_error_free(error);
 } else {
    g_print(PROGNAME "       SUCCESS/n");
 }
}
由于这里的方法调用并没有返回任何数据,所以传递给回调函数的参数也最少(一般三个)。对于 error 的处理必须放在回调函数里面,因为 server 侧的延时可能会导致在启动方法调用时不能立即得到 error 请注意:这里的回调函数并没有在发生错误时结束程序,故意这么做的,主要是为了验证下面出现的异步问题。函数 ”timestamp” 用于获取程序开始后的秒数(对于查看异步事件的顺序很有用)。
/**
 * This function will be called repeatedly from within the mainloop
 * timer launch code.
 *
 * It will launch asynchronous RPC method to set value1 with ever
 * increasing argument.
 */
static gboolean timerCallback(DBusGProxy* remoteobj) {
 
 /* Local value that we'll start updating to the remote object. */
 static gint localValue1 = -80;
 
 /* Start the RPC.
     This is done by calling the stub function that will take the new
     value and the callback function to call on reply getting back.
 
     The stub returns a DBusGProxyCall object, but we don't need it
     so we'll ignore the return value. The return value could be used
     to cancel a pending request (from client side) with
     dbus_g_proxy_cancel_call. We could also pass a pointer to
     user-data (last parameter), but we don't need one in this example.
     It would normally be used to "carry around" the application state.
     */
 g_print(PROGNAME ":%s:timerCallback launching setvalue1/n",
          timestamp());
 org_maemo_Value_setvalue1_async(remoteobj, localValue1,
                                  setValue1Completed, NULL);
 g_print(PROGNAME ":%s:timerCallback setvalue1 launched/n",
          timestamp());
 
 /* Step the local value forward. */
 localValue1 += 10;
 
 /* Repeat timer later. */
 return TRUE;
}
使用 stub 代码非常简单,对于每一个同步函数调用,都有一个对应的异步操作函数。
主函数还是与前一个例子类似,每隔 1 秒调用一次异步方法调用。
 
不同步性所引发的问题
编译后运行,开始似乎一切正常,但是一段时间后出现了问题:
[sbox-CHINOOK_X86: ~/glib-dbus-async] > make client-stubs
dbus-binding-tool --prefix=value_object --mode=glib-client /
 value-dbus-interface.xml > value-client-stub.h
cc -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include /
 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -g -Wall /
 -DG_DISABLE_DEPRECATED -DNO_DAEMON -DPROGNAME=/"client-stubs/" /
 -c client-stubs.c -o client-stubs.o
cc client-stubs.o -o client-stubs -ldbus-glib-1 -ldbus-1 -lgobject-2.0 -lglib-2.0
[sbox-CHINOOK_X86: ~/glib-dbus-async] > run-standalone.sh ./client-stubs
client-stubs:main Connecting to Session D-Bus.
client-stubs:main Creating a GLib proxy object for Value.
client-stubs: 0.00:main Starting main loop (first timer in 1s).
client-stubs: 1.00:timerCallback launching setvalue1
client-stubs: 1.00:timerCallback setvalue1 launched
server:value_object_setvalue1: Called (valueIn=-80)
server:value_object_setvalue1: Delaying operation
client-stubs: 2.00:timerCallback launching setvalue1
client-stubs: 2.00:timerCallback setvalue1 launched
client-stubs: 3.01:timerCallback launching setvalue1
client-stubs: 3.01:timerCallback setvalue1 launched
client-stubs: 4.01:timerCallback launching setvalue1
client-stubs: 4.01:timerCallback setvalue1 launched
client-stubs: 5.02:timerCallback launching setvalue1
client-stubs: 5.02:timerCallback setvalue1 launched
server:value_object_emitSignal: Emitting signal id 0, with message 'value1'
server:value_object_setvalue1: Called (valueIn=-70)
server:value_object_setvalue1: Delaying operation
client-stubs: 6.01:setValue1Completed
client-stubs       SUCCESS //正常
client-stubs: 6.02:timerCallback launching setvalue1
client-stubs: 6.02:timerCallback setvalue1 launched
client-stubs: 7.02:timerCallback launching setvalue1
client-stubs: 7.02:timerCallback setvalue1 launched
...
client-stubs:25.04:timerCallback launching setvalue1
client-stubs:25.04:timerCallback setvalue1 launched
server:value_object_emitSignal: Emitting signal id 0, with message 'value1'
server:value_object_setvalue1: Called (valueIn=-30)
server:value_object_setvalue1: Delaying operation
client-stubs:26.03:setValue1Completed
client-stubs       SUCCESS //正常
client-stubs:26.05:timerCallback launching setvalue1
client-stubs:26.05:timerCallback setvalue1 launched
client-stubs:27.05:timerCallback launching setvalue1
client-stubs:27.05:timerCallback setvalue1 launched
client-stubs:28.05:timerCallback launching setvalue1
client-stubs:28.05:timerCallback setvalue1 launched
client-stubs:29.05:timerCallback launching setvalue1
client-stubs:29.05:timerCallback setvalue1 launched
client-stubs:30.05:timerCallback launching setvalue1
client-stubs:30.05:timerCallback setvalue1 launched
client-stubs:31.02:setValue1Completed //出现问题了
client-stubs       ERROR: Did not receive a reply. Possible causes include:
 the remote application did not send a reply, the message bus security policy
 blocked the reply, the reply timeout expired, or the network connection was
 broken.
server:value_object_emitSignal: Emitting signal id 0, with message 'value1'
server:value_object_setvalue1: Called (valueIn=-20)
server:value_object_setvalue1: Delaying operation
client-stubs:31.05:timerCallback launching setvalue1
client-stubs:31.05:timerCallback setvalue1 launched
client-stubs:32.03:setValue1Completed
client-stubs       ERROR: Did not receive a reply. Possible causes include:
 the remote application did not send a reply, the message bus security policy
 blocked the reply, the reply timeout expired, or the network connection was
 broken.
client-stubs:32.05:timerCallback launching setvalue1
client-stubs:32.05:timerCallback setvalue1 launched
client-stubs:33.03:setValue1Completed
client-stubs       ERROR: Did not receive a reply. Possible causes include:
 the remote application did not send a reply, the message bus security policy
 blocked the reply, the reply timeout expired, or the network connection was
 broken.
client-stubs:33.05:timerCallback launching setvalue1
client-stubs:33.05:timerCallback setvalue1 launched
client-stubs:34.03:setValue1Completed
client-stubs       ERROR: Did not receive a reply. Possible causes include:
 the remote application did not send a reply, the message bus security policy
 blocked the reply, the reply timeout expired, or the network connection was
 broken.
client-stubs:34.06:timerCallback launching setvalue1
client-stubs:34.06:timerCallback setvalue1 launched
client-stubs:35.03:setValue1Completed
client-stubs       ERROR: Did not receive a reply. Possible causes include:
 the remote application did not send a reply, the message bus security policy
 blocked the reply, the reply timeout expired, or the network connection was
 broken.
client-stubs:35.05:timerCallback launching setvalue1
client-stubs:35.05:timerCallback setvalue1 launched
client-stubs:36.04:setValue1Completed
client-stubs       ERROR: Did not receive a reply. Possible causes include:
 the remote application did not send a reply, the message bus security policy
 blocked the reply, the reply timeout expired, or the network connection was
 broken.
server:value_object_emitSignal: Emitting signal id 0, with message 'value1'
server:value_object_setvalue1: Called (valueIn=-10)
server:value_object_setvalue1: Delaying operation
client-stubs:36.06:timerCallback launching setvalue1
client-stubs:36.06:timerCallback setvalue1 launched
client-stubs:37.04:setValue1Completed
client-stubs       ERROR: Did not receive a reply. Possible causes include:
 the remote application did not send a reply, the message bus security policy
 blocked the reply, the reply timeout expired, or the network connection was
 broken.
[Ctrl+c]
[sbox-CHINOOK_X86: ~/glib-dbus-async] >
server:value_object_emitSignal: Emitting signal id 0, with message 'value1'
server:value_object_setvalue1: Called (valueIn=30)
server:value_object_setvalue1: Delaying operation
server:value_object_emitSignal: Emitting signal id 0, with message 'value1'
server:value_object_setvalue1: Called (valueIn=40)
server:value_object_setvalue1: Delaying operation
server:value_object_emitSignal: Emitting signal id 0, with message 'value1'
server:value_object_setvalue1: Called (valueIn=50)
server:value_object_setvalue1: Delaying operation
server:value_object_emitSignal: Emitting signal id 0, with message 'value1'
...
上面发生的异常对时间非常敏感。 Client 侧的定时器回调函数会每隔一秒调用一次异步调用。而 server 侧对每个异步调用都有 5 秒的延时,因此:开始几秒内我们没有看到 server 侧打印什么东西,大概 6 秒后, client 侧收到了第一个响应。此时, server 侧实际上收到了 5 个异步调用的请求,还有 4 个没有处理。由于延时和排队的效应, server 侧不能够及时响应每个请求。
大约 30 秒之后,我看到回调 setValue1Completed 被激发了,但是这次方法调用失败了!并且,从这个时间往后,在 server 累积的方法调用都会在 client 侧反映失败的结果。这是为什么呢? D-Bus 调用超时!!!
即使我们把 client 侧程序结束掉, server 侧照样自顾自地继续处理累积的调用申请,而不管 client 侧是否处理调用的结果。
上面的测试结果告诉我们:你需要合理的设计你的服务程序,以免发生函数调用延时的情况,从而导致调用超时 ( 失败 ) 。同时,你也需要设计一个通知机制:告诉 client 侧,什么事情完成了,而不是采用超时的方式。使用 D-Bus 信号是一种方法(通知机制)。当长时间的操作完成后可以发信号通知 client 侧。
另外, client 也不要闲着,尽量确保在给定时间段内只有一个方法调用,而不是盲目的去启动 RPC 方法调用:如果前一个调用的响应尚未返回,则推迟启动下一个。 Client 侧和 server 共同努力,尽量避免调用失败的情况。
如果 client 侧确实需要启动多个同样的方法调用,或者有多个 client 程序并行启动同一个 method call, server 侧要有并行处理多个请求的能力。如果具备这种并行处理能力,后面会给些提示。
 
//D-Bus设置的几个默认超时值
 
BusConfigParser*
bus_config_parser_new (const DBusString     *basedir,
                       dbus_bool_t            is_toplevel,
                       const BusConfigParser *parent)
{
    // ......
    {
 
      /* Make up some numbers! woot! */
      parser->limits.max_incoming_bytes = _DBUS_ONE_MEGABYTE * 63;
      parser->limits.max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 63;
      parser->limits.max_message_size = _DBUS_ONE_MEGABYTE * 32;
     
      /* Making this long means the user has to wait longer for an error
       * message if something screws up, but making it too short means
       * they might see a false failure.
       */
      parser->limits.activation_timeout = 25000; /* 25 seconds */
 
      /* Making this long risks making a DOS attack easier, but too short
       * and legitimate auth will fail. If interactive auth (ask user for
       * password) is allowed, then potentially it has to be quite long.
       */
      parser->limits.auth_timeout = 30000; /* 30 seconds */
     
      parser->limits.max_incomplete_connections = 32;
      parser->limits.max_connections_per_user = 128;
     
      /* Note that max_completed_connections / max_connections_per_user
       * is the number of users that would have to work together to
       * DOS all the other users.
       */
      parser->limits.max_completed_connections = 1024;
     
      parser->limits.max_pending_activations = 256;
      parser->limits.max_services_per_connection = 256;
     
      parser->limits.max_match_rules_per_connection = 512;
     
      parser->limits.reply_timeout = 5 * 60 * 1000; /* 5 minutes */
      parser->limits.max_replies_per_connection = 32;
    }
     
 parser->refcount = 1;
     
 return parser;
}
 
D-Bus 配置的参数看,前面的 30 秒超时好像和 auth_timeout 一致,但是这又不合逻辑:此时的 connection 已经建立起来了,鉴权已经完成了。后面的 reply_timeout 又是 5 分钟,和 30 秒也不吻合。值得进一步的研究。
原因在这里:
//D-Bus设置的等待消息回应的默认值是 25秒,如果25秒内没有回应,则超时,这就解释了为什么上面会在30秒处失败:25秒等待+5秒延迟打印 = 30秒
 
/** default timeout value when waiting for a message reply, 25 seconds */
#define _DBUS_DEFAULT_TIMEOUT_VALUE (25 * 1000)
 
DBusPendingCall*
_dbus_pending_call_new (DBusConnection    *connection,
                        int                timeout_milliseconds,
                        DBusTimeoutHandler timeout_handler)
{
 DBusPendingCall *pending;
 DBusTimeout *timeout;
 
 _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
 
 if (timeout_milliseconds == -1)
    timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
 
 //…
}
 
使用 Glib 的封装实现异步方法调用
有时候, XML 文件不存在,你没有办法使用 dbus-bindings-tool 来生成 stub 代码。这时候你可以直接使用 Glib 来解决。
直接使用生成的 stub 代码会非常方便,如果我们能在它的基础上做些修改,不是可以减少工作量吗?是的。在一个可以工作的模板基础上修改,好处多多。
好,下面我们看看由工具生成的代码是什么样子:
typedef void (*org_maemo_Value_setvalue1_reply) (DBusGProxy *proxy,
                                                 GError *error,
                                                 gpointer userdata);
 
static void
org_maemo_Value_setvalue1_async_callback ( DBusGProxy *proxy,
                                          DBusGProxyCall *call,
                                          void *user_data)
{
 DBusGAsyncData *data = user_data;
 GError *error = NULL;
  dbus_g_proxy_end_call ( proxy, call, &error, G_TYPE_INVALID);
 (*(org_maemo_Value_setvalue1_reply)data->cb) (proxy, error,
                                                data->userdata);
 return;
}
 
static
#ifdef G_HAVE_INLINE
inline
#endif
DBusGProxyCall*
org_maemo_Value_setvalue1_async ( DBusGProxy *proxy,
                                 const gint IN_new_value,
                             org_maemo_Value_setvalue1_reply callback,
                                 gpointer userdata)
 
{
 DBusGAsyncData *stuff;
 stuff = g_new (DBusGAsyncData, 1);
 stuff->cb = G_CALLBACK (callback);
 stuff->userdata = userdata;
 return dbus_g_proxy_begin_call (
    proxy, "setvalue1", org_maemo_Value_setvalue1_async_callback,
    stuff, g_free, G_TYPE_INT, IN_new_value, G_TYPE_INVALID);
}
我们注意到,在 org_maemo_Value_setvalue1_async 中,首先创建一个临时结构变量 DBusGAsyncData *stuff; 这个变量保存一个指向回调函数的指针和 userdata 指针的一份拷贝,今后就用这两个指针来引用回调函数和一些数据。这个临时的结构变量传递给 dbus_g_proxy_begin_call ,同时也把回调函数 (org_maemo_Value_setvalue1_async_callback) 的地址传入。
RPC 调用一完成,回调将会触发。接着就通过那个指向回调函数的指针去引用用户实际传入的回调函数。
下面我们仿照上面的办法搞一个异步调用:在定时器函数中,启动异步操作,同时注册回调函数:
/**
 * This function will be called repeatedly from within the mainloop
 * timer launch code.
 *
 * It will launch asynchronous RPC method to set value1 with ever
 * increasing argument.
 */
static gboolean timerCallback(DBusGProxy* remoteobj) {
 
 /* Local value that we'll start updating to the remote object. */
 static gint localValue1 = -80;
 
 /* Start the first RPC.
     The call using GLib/D-Bus is only slightly more complex than the
     stubs. The overall operation is the same. */
 g_print(PROGNAME ":timerCallback launching setvalue1/n");
 dbus_g_proxy_begin_call(remoteobj,
                          /* Method name. */
                          "setvalue1",
                          /* Callback to call on "completion". */
                          setValue1Completed,
                          /* User-data to pass to callback. */
                          NULL,
                          /* Function to call to free userData after
                             callback returns. */
                          NULL,
                          /* First argument GType. */
                          G_TYPE_INT,
                          /* First argument value (passed by value) */
                          localValue1,
                          /* Terminate argument list. */
                          G_TYPE_INVALID);
 g_print(PROGNAME ":timerCallback setvalue1 launched/n");
 
 /* Step the local value forward. */
 localValue1 += 10;
 
 /* Repeat timer later. */
 return TRUE;
}
 
当异步调用完成或者超时或者失败时,回调将被激活,在回调里面我们结束异步调用:
/**
 * This function will be called when the async setvalue1 will either
 * complete, timeout or fail (same as before). The main difference in
 * using GLib/D-Bus wrappers is that we need to "collect" the return
 * value (or error). This is done with the _end_call function.
 *
 * Note that all callbacks that are to be registered for RPC async
 * notifications using dbus_g_proxy_begin_call must follow the
 * following prototype: DBusGProxyCallNotify .
 */
static void setValue1Completed(DBusGProxy* proxy,
                               DBusGProxyCall* call,
                               gpointer userData) {
 
 /* This will hold the GError object (if any). */
 GError* error = NULL;
 
 g_print(PROGNAME ":setValue1Completed/n");
 
 /* We next need to collect the results from the RPC call.
     The function returns FALSE on errors (which we check), although
     we could also check whether error-ptr is still NULL. */
 if (!dbus_g_proxy_end_call(proxy,
                             /* The call that we're collecting. */
                             call,
                             /* Where to store the error (if any). */
                             &error,
                             /* Next we list the GType codes for all
                                the arguments we expect back. In our
                                case there are none, so set to
                                invalid. */
                             G_TYPE_INVALID)) {
    /* Some error occurred while collecting the result. */
    g_printerr(PROGNAME " ERROR: %s/n", error->message);
    g_error_free(error);
 } else {
    g_print(PROGNAME " SUCCESS/n");
 }
}
编译:
client-glib: client-glib.o
      $(CC) $^ -o $@ $(LDFLAGS)
# Note that the GLib client doesn't need the stub code.
client-glib.o: client-glib.c common-defs.h
      $(CC) $(CFLAGS) -DPROGNAME=/"$(basename $@)/" -c $< -o $@
 
测试的结果和使用生成的异步机制没有什么两样。
 
 
注:
D-Bus 内部函数中, pending reply( 等侯回应 ) 是用的比较多的概念,这个概念对于前面的超时有莫大的关系。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值