Bug 830290: Execute DBus send operation in DBus thread r=bent,qdot
The DBus send operation simply sends a DBus message without further
processing of replies. If the sender is interested in a reply, the
respective serial number can be returned. In this case, the sending
operation (and only the sending) is serialized with the calling
thread.
--- a/ipc/dbus/DBusUtils.cpp
+++ b/ipc/dbus/DBusUtils.cpp
@@ -14,16 +14,17 @@
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include "DBusUtils.h"
#include "DBusThread.h"
#include "nsThreadUtils.h"
+#include "mozilla/Monitor.h"
#include "nsAutoPtr.h"
#include <cstdio>
#include <cstring>
#undef LOG
#if defined(MOZ_WIDGET_GONK)
#include <android/log.h>
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args);
@@ -65,16 +66,174 @@ protected:
virtual ~DBusConnectionSendRunnableBase()
{ }
DBusConnection* mConnection;
DBusMessageRefPtr mMessage;
};
+class DBusConnectionSendSyncRunnable : public DBusConnectionSendRunnableBase
+{
+public:
+ bool WaitForCompletion()
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ MonitorAutoLock autoLock(mCompletedMonitor);
+ while (!mCompleted) {
+ mCompletedMonitor.Wait();
+ }
+ return mSuccess;
+ }
+
+protected:
+ DBusConnectionSendSyncRunnable(DBusConnection* aConnection,
+ DBusMessage* aMessage)
+ : DBusConnectionSendRunnableBase(aConnection, aMessage),
+ mCompletedMonitor("DBusConnectionSendSyncRunnable.mCompleted"),
+ mCompleted(false),
+ mSuccess(false)
+ { }
+
+ virtual ~DBusConnectionSendSyncRunnable()
+ { }
+
+ // Call this function at the end of Run() to notify waiting
+ // threads.
+ void Completed(bool aSuccess)
+ {
+ MonitorAutoLock autoLock(mCompletedMonitor);
+ MOZ_ASSERT(!mCompleted);
+ mSuccess = aSuccess;
+ mCompleted = true;
+ mCompletedMonitor.Notify();
+ }
+
+private:
+ Monitor mCompletedMonitor;
+ bool mCompleted;
+ bool mSuccess;
+};
+
+//
+// Sends a message and returns the message's serial number to the
+// disaptching thread. Only run it in DBus thread.
+//
+class DBusConnectionSendRunnable : public DBusConnectionSendSyncRunnable
+{
+public:
+ DBusConnectionSendRunnable(DBusConnection* aConnection,
+ DBusMessage* aMessage,
+ dbus_uint32_t* aSerial)
+ : DBusConnectionSendSyncRunnable(aConnection, aMessage),
+ mSerial(aSerial)
+ { }
+
+ NS_IMETHOD Run()
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ dbus_bool_t success = dbus_connection_send(mConnection, mMessage, mSerial);
+ Completed(success == TRUE);
+
+ NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE);
+
+ return NS_OK;
+ }
+
+protected:
+ ~DBusConnectionSendRunnable()
+ { }
+
+private:
+ dbus_uint32_t* mSerial;
+};
+
+dbus_bool_t
+dbus_func_send(DBusConnection* aConnection, dbus_uint32_t* aSerial,
+ DBusMessage* aMessage)
+{
+ nsRefPtr<DBusConnectionSendRunnable> t(
+ new DBusConnectionSendRunnable(aConnection, aMessage, aSerial));
+ MOZ_ASSERT(t);
+
+ nsresult rv = DispatchToDBusThread(t);
+
+ if (NS_FAILED(rv)) {
+ if (aMessage) {
+ dbus_message_unref(aMessage);
+ }
+ return FALSE;
+ }
+
+ if (aSerial && !t->WaitForCompletion()) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+dbus_func_args_send_valist(DBusConnection* aConnection,
+ dbus_uint32_t* aSerial,
+ const char* aPath,
+ const char* aInterface,
+ const char* aFunction,
+ int aFirstArgType,
+ va_list aArgs)
+{
+ // Compose the command...
+ DBusMessage* message = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
+ aPath,
+ aInterface,
+ aFunction);
+ if (!message) {
+ LOG("Could not allocate D-Bus message object!");
+ goto done;
+ }
+
+ // ... and append arguments.
+ if (!dbus_message_append_args_valist(message, aFirstArgType, aArgs)) {
+ LOG("Could not append argument to method call!");
+ goto done;
+ }
+
+ return dbus_func_send(aConnection, aSerial, message);
+
+done:
+ if (message) {
+ dbus_message_unref(message);
+ }
+ return FALSE;
+}
+
+dbus_bool_t
+dbus_func_args_send(DBusConnection* aConnection,
+ dbus_uint32_t* aSerial,
+ const char* aPath,
+ const char* aInterface,
+ const char* aFunction,
+ int aFirstArgType, ...)
+{
+ va_list args;
+ va_start(args, aFirstArgType);
+
+ dbus_bool_t success = dbus_func_args_send_valist(aConnection,
+ aSerial,
+ aPath,
+ aInterface,
+ aFunction,
+ aFirstArgType,
+ args);
+ va_end(args);
+
+ return success;
+}
+
//
// Sends a message and executes a callback function for the reply. Only
// run it in DBus thread.
//
class DBusConnectionSendWithReplyRunnable : public DBusConnectionSendRunnableBase
{
private:
class NotifyData
--- a/ipc/dbus/DBusUtils.h
+++ b/ipc/dbus/DBusUtils.h
@@ -52,16 +52,27 @@ private:
typedef void (*DBusCallback)(DBusMessage *, void *);
void log_and_free_dbus_error(DBusError* err,
const char* function,
DBusMessage* msg = NULL);
+dbus_bool_t dbus_func_send(DBusConnection *aConnection,
+ dbus_uint32_t *aSerial,
+ DBusMessage *aMessage);
+
+dbus_bool_t dbus_func_args_send(DBusConnection *aConnection,
+ dbus_uint32_t *aSerial,
+ const char *aPath,
+ const char *aInterface,
+ const char *aFunction,
+ int aFirstArgType, ...);
+
dbus_bool_t dbus_func_send_async(DBusConnection* conn,
DBusMessage* msg,
int timeout_ms,
DBusCallback user_cb,
void* user);
dbus_bool_t dbus_func_args_async(DBusConnection* conn,
int timeout_ms,