mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 830290: Setup asyncronous DBus messages in DBus thread r=bent,qdot
This patch finally fixes bug 827888 were a message's reply was received before the respective handler function could be installed. The patch adds the class DBusConnectionSendWithReplyTask, which asyncronously sends a DBus message and installs the reply's handler function. The DBus utility functions for asyncronous messages create an instance of this class and dispatch it to the DBus thread. This intercepts the DBusPollTask, so no DBus replies can be received until the dispatched DBusConnectionSendWithReplyTask has finished.
This commit is contained in:
parent
c3746ccd8e
commit
1853aa5f3f
@ -17,6 +17,9 @@
|
||||
*/
|
||||
|
||||
#include "DBusUtils.h"
|
||||
#include "DBusThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
@ -53,14 +56,12 @@ typedef struct {
|
||||
void *user;
|
||||
} dbus_async_call_t;
|
||||
|
||||
void dbus_func_args_async_callback(DBusPendingCall *call, void *data) {
|
||||
void dbus_func_args_async_callback(DBusPendingCall *call, void *data)
|
||||
{
|
||||
nsAutoPtr<dbus_async_call_t> req(static_cast<dbus_async_call_t*>(data));
|
||||
|
||||
dbus_async_call_t *req = (dbus_async_call_t *)data;
|
||||
DBusMessage *msg;
|
||||
|
||||
/* This is guaranteed to be non-NULL, because this function is called only
|
||||
when once the remote method invokation returns. */
|
||||
msg = dbus_pending_call_steal_reply(call);
|
||||
// This is NULL if the timeout has been reached.
|
||||
DBusMessage *msg = dbus_pending_call_steal_reply(call);
|
||||
|
||||
if (msg) {
|
||||
if (req->user_cb) {
|
||||
@ -73,60 +74,90 @@ void dbus_func_args_async_callback(DBusPendingCall *call, void *data) {
|
||||
//dbus_message_unref(req->method);
|
||||
dbus_pending_call_cancel(call);
|
||||
dbus_pending_call_unref(call);
|
||||
free(req);
|
||||
}
|
||||
|
||||
class DBusConnectionSendWithReplyTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DBusConnectionSendWithReplyTask(DBusConnection* aConnection,
|
||||
DBusMessage* aMessage,
|
||||
int aTimeout,
|
||||
void (*aCallback)(DBusMessage*, void*),
|
||||
void* aData)
|
||||
: mConnection(aConnection),
|
||||
mMessage(aMessage),
|
||||
mTimeout(aTimeout),
|
||||
mCallback(aCallback),
|
||||
mData(aData)
|
||||
{ }
|
||||
|
||||
NS_METHOD Run()
|
||||
{
|
||||
// Freed at end of dbus_func_args_async_callback (becomes "req")
|
||||
nsAutoPtr<dbus_async_call_t> pending(new dbus_async_call_t);
|
||||
NS_ENSURE_TRUE(pending, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
pending->user_cb = mCallback;
|
||||
pending->user = mData;
|
||||
|
||||
DBusPendingCall *call;
|
||||
|
||||
dbus_bool_t success = dbus_connection_send_with_reply(mConnection,
|
||||
mMessage,
|
||||
&call,
|
||||
mTimeout);
|
||||
NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE);
|
||||
|
||||
success = dbus_pending_call_set_notify(call,
|
||||
dbus_func_args_async_callback,
|
||||
pending,
|
||||
NULL);
|
||||
NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE);
|
||||
|
||||
pending.forget();
|
||||
dbus_message_unref(mMessage);
|
||||
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
protected:
|
||||
~DBusConnectionSendWithReplyTask()
|
||||
{ }
|
||||
|
||||
private:
|
||||
DBusConnection* mConnection;
|
||||
DBusMessage* mMessage;
|
||||
int mTimeout;
|
||||
void (*mCallback)(DBusMessage*, void*);
|
||||
void* mData;
|
||||
};
|
||||
|
||||
dbus_bool_t dbus_func_send_async(DBusConnection *conn,
|
||||
DBusMessage *msg,
|
||||
int timeout_ms,
|
||||
void (*user_cb)(DBusMessage*,
|
||||
void*),
|
||||
void *user) {
|
||||
dbus_async_call_t *pending;
|
||||
dbus_bool_t reply = FALSE;
|
||||
|
||||
// Freed at end of dbus_func_args_async_callback (becomes "req")
|
||||
pending = (dbus_async_call_t *)malloc(sizeof(dbus_async_call_t));
|
||||
DBusPendingCall *call;
|
||||
|
||||
pending->user_cb = user_cb;
|
||||
pending->user = user;
|
||||
|
||||
reply = dbus_connection_send_with_reply(conn, msg,
|
||||
&call,
|
||||
timeout_ms);
|
||||
/**
|
||||
* dbus_connection_send_with_reply() may return TRUE but leave *pending_return
|
||||
* as NULL, we'd better to check both reply value and return DBusPendingCall.
|
||||
*/
|
||||
if (!reply || !call) {
|
||||
goto done;
|
||||
void *user)
|
||||
{
|
||||
nsRefPtr<nsIRunnable> t(
|
||||
new DBusConnectionSendWithReplyTask(conn, msg, timeout_ms, user_cb, user));
|
||||
if (!t) {
|
||||
if (msg) {
|
||||
dbus_message_unref(msg);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Workaround bug 827888
|
||||
*
|
||||
* When we set the notify callback, the call might have already been
|
||||
* completed. In this case the call never gets handled. To workaround
|
||||
* the problem, we test if the call has been completed and if so, run
|
||||
* the notify handler explicitly.
|
||||
*
|
||||
* To fix bug 827888, we'd need to do this atomically; or make dbus
|
||||
* run the notifier function automatically if the call has been
|
||||
* completed meanwhile.
|
||||
*/
|
||||
if (dbus_pending_call_get_completed(call)) {
|
||||
dbus_func_args_async_callback(call, pending);
|
||||
} else {
|
||||
dbus_pending_call_set_notify(call,
|
||||
dbus_func_args_async_callback,
|
||||
pending,
|
||||
NULL);
|
||||
nsresult rv = DispatchToDBusThread(t);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
if (msg) {
|
||||
dbus_message_unref(msg);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
done:
|
||||
if (msg) dbus_message_unref(msg);
|
||||
return reply;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static dbus_bool_t dbus_func_args_async_valist(DBusConnection *conn,
|
||||
|
Loading…
Reference in New Issue
Block a user