mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2025-12-22 02:00:10 +01:00
Fix a bunch of lifecycle and memory management problems in the mono bindings. * mono/Arguments.cs (Arguments): Implement IDisposable * mono/Bus.cs (Bus): Don't allow public instantiation. This is strictly a static class. * mono/Connection.cs: Move the DBusObjectPathVTable and associated delegates into this file. (Connection): Implement IDisposable. (Dispose): Disconnect the connection and set the raw connection pointer to IntPtr.Zero. (~Connection): Call Dispose(). (RegisterObjectPath): Added. Manages the registration of object paths so we can cleanly disconnect them at dispose/finalize time. (UnregisterObjectPath): Ditto. (set_RawConnection): Unregister all of the object paths when changing the underlying DBusConnection. Add them back onto the new connection, if any. * mono/Handler.cs: Don't implement IDisposable; it doesn't use any more unmanaged resources anymore, so it's not necessary. Move all the DBusObjectPathVTable stuff out of here. (Handler): Save references to our delegates so that they don't get finalized. Call Connection.RegisterObjectPath() instead of dbus_connection_register_object_path() directly. (Message_Called): Dispose the message after we're finished with it. * mono/Message.cs (Message): Implement IDisposable. (Dispose): Dispose the Arguments, and set the RawMessage to IntPtr.Zero. (SendWithReplyAndBlock): We own the ref to the reply that comes back from dbus_connection_send_with_reply_and_block() so add a comment about that and unref it after we've constructed a managed MethodReturn class around it. Fixes a big, big leak. * mono/ProxyBuilder.cs: Reflect into Message to get the Dispose method. (BuildSignalHandler): After we've sent the Signal message, dispose of it. (BuildMethod): Dispose of the method call and reply messages after we've sent the message and extracted the data we want from the reply. * mono/Service.cs (UnregisterObject): Don't call handler.Dispose() anymore. (Service_FilterCalled): Dispose of the message after we're finished with it.
437 lines
11 KiB
C#
437 lines
11 KiB
C#
namespace DBus
|
|
{
|
|
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Diagnostics;
|
|
using System.Collections;
|
|
|
|
public class Message : IDisposable
|
|
{
|
|
private static Stack stack = new Stack ();
|
|
|
|
static public Message Current {
|
|
get
|
|
{
|
|
return stack.Count > 0 ? (Message) stack.Peek () : null;
|
|
}
|
|
}
|
|
|
|
static internal void Push (Message message)
|
|
{
|
|
stack.Push (message);
|
|
}
|
|
|
|
static internal void Pop ()
|
|
{
|
|
stack.Pop ();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// A pointer to the underlying Message structure
|
|
/// </summary>
|
|
private IntPtr rawMessage;
|
|
|
|
/// <summary>
|
|
/// The current slot number
|
|
/// </summary>
|
|
private static int slot = -1;
|
|
|
|
// Keep in sync with C
|
|
public enum MessageType
|
|
{
|
|
Invalid = 0,
|
|
MethodCall = 1,
|
|
MethodReturn = 2,
|
|
Error = 3,
|
|
Signal = 4
|
|
}
|
|
|
|
private Arguments arguments = null;
|
|
|
|
protected Service service = null;
|
|
protected string pathName = null;
|
|
protected string interfaceName = null;
|
|
protected string name = null;
|
|
private string key= null;
|
|
|
|
protected Message()
|
|
{
|
|
// An empty constructor for the sake of sub-classes which know how to construct theirselves.
|
|
}
|
|
|
|
protected Message(IntPtr rawMessage, Service service)
|
|
{
|
|
RawMessage = rawMessage;
|
|
this.service = service;
|
|
}
|
|
|
|
protected Message(MessageType messageType)
|
|
{
|
|
// the assignment bumps the refcount
|
|
RawMessage = dbus_message_new((int) messageType);
|
|
|
|
if (RawMessage == IntPtr.Zero) {
|
|
throw new OutOfMemoryException();
|
|
}
|
|
|
|
dbus_message_unref(RawMessage);
|
|
}
|
|
|
|
protected Message(MessageType messageType, Service service) : this(messageType)
|
|
{
|
|
this.service = service;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public void Dispose (bool disposing)
|
|
{
|
|
if (disposing) {
|
|
if (this.arguments != null)
|
|
this.arguments.Dispose ();
|
|
}
|
|
|
|
RawMessage = IntPtr.Zero; // free the native object
|
|
}
|
|
|
|
~Message()
|
|
{
|
|
Dispose (false);
|
|
}
|
|
|
|
public static Message Wrap(IntPtr rawMessage, Service service)
|
|
{
|
|
if (slot > -1) {
|
|
// If we already have a Message object associated with this rawMessage then return it
|
|
IntPtr rawThis = dbus_message_get_data(rawMessage, slot);
|
|
if (rawThis != IntPtr.Zero)
|
|
return (DBus.Message) ((GCHandle)rawThis).Target;
|
|
}
|
|
// If it doesn't exist then create a new Message around it
|
|
Message message = null;
|
|
MessageType messageType = (MessageType) dbus_message_get_type(rawMessage);
|
|
|
|
switch (messageType) {
|
|
case MessageType.Signal:
|
|
message = new Signal(rawMessage, service);
|
|
break;
|
|
case MessageType.MethodCall:
|
|
message = new MethodCall(rawMessage, service);
|
|
break;
|
|
case MessageType.MethodReturn:
|
|
message = new MethodReturn(rawMessage, service);
|
|
break;
|
|
case MessageType.Error:
|
|
message = new ErrorMessage(rawMessage, service);
|
|
break;
|
|
default:
|
|
throw new ApplicationException("Unknown message type to wrap: " + messageType);
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
internal IntPtr RawMessage
|
|
{
|
|
get
|
|
{
|
|
return rawMessage;
|
|
}
|
|
set
|
|
{
|
|
if (value == rawMessage)
|
|
return;
|
|
|
|
if (rawMessage != IntPtr.Zero)
|
|
{
|
|
// Get the reference to this
|
|
IntPtr rawThis = dbus_message_get_data(rawMessage, Slot);
|
|
Debug.Assert (rawThis != IntPtr.Zero);
|
|
|
|
// Blank over the reference
|
|
dbus_message_set_data(rawMessage, Slot, IntPtr.Zero, IntPtr.Zero);
|
|
|
|
// Free the reference
|
|
((GCHandle) rawThis).Free();
|
|
|
|
// Unref the connection
|
|
dbus_message_unref(rawMessage);
|
|
}
|
|
|
|
this.rawMessage = value;
|
|
|
|
if (rawMessage != IntPtr.Zero)
|
|
{
|
|
GCHandle rawThis;
|
|
|
|
dbus_message_ref(rawMessage);
|
|
|
|
// We store a weak reference to the C# object on the C object
|
|
rawThis = GCHandle.Alloc(this, GCHandleType.WeakTrackResurrection);
|
|
|
|
dbus_message_set_data(rawMessage, Slot, (IntPtr) rawThis, IntPtr.Zero);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Send(ref int serial)
|
|
{
|
|
if (!dbus_connection_send (Service.Connection.RawConnection, RawMessage, ref serial))
|
|
throw new OutOfMemoryException ();
|
|
|
|
Service.Connection.Flush();
|
|
}
|
|
|
|
public void Send()
|
|
{
|
|
int ignored = 0;
|
|
Send(ref ignored);
|
|
}
|
|
|
|
public void SendWithReply()
|
|
{
|
|
IntPtr rawPendingCall = IntPtr.Zero;
|
|
|
|
if (!dbus_connection_send_with_reply (Service.Connection.RawConnection, RawMessage, rawPendingCall, Service.Connection.Timeout))
|
|
throw new OutOfMemoryException();
|
|
}
|
|
|
|
public MethodReturn SendWithReplyAndBlock()
|
|
{
|
|
Error error = new Error();
|
|
error.Init();
|
|
|
|
IntPtr rawMessage = dbus_connection_send_with_reply_and_block(Service.Connection.RawConnection,
|
|
RawMessage,
|
|
Service.Connection.Timeout,
|
|
ref error);
|
|
|
|
if (rawMessage != IntPtr.Zero) {
|
|
MethodReturn methodReturn = new MethodReturn(rawMessage, Service);
|
|
// Ownership of a ref is passed onto us from
|
|
// dbus_connection_send_with_reply_and_block(). It gets reffed as
|
|
// a result of being passed into the MethodReturn ctor, so unref
|
|
// the extra one here.
|
|
dbus_message_unref (rawMessage);
|
|
|
|
return methodReturn;
|
|
} else {
|
|
throw new DBusException(error);
|
|
}
|
|
}
|
|
|
|
public MessageType Type
|
|
{
|
|
get
|
|
{
|
|
return (MessageType) dbus_message_get_type(RawMessage);
|
|
}
|
|
}
|
|
|
|
public Service Service
|
|
{
|
|
set
|
|
{
|
|
if (this.service != null && (value.Name != this.service.Name)) {
|
|
if (!dbus_message_set_destination(RawMessage, value.Name)) {
|
|
throw new OutOfMemoryException();
|
|
}
|
|
}
|
|
|
|
this.service = value;
|
|
}
|
|
get
|
|
{
|
|
return this.service;
|
|
}
|
|
}
|
|
|
|
protected virtual string PathName
|
|
{
|
|
set
|
|
{
|
|
if (value != this.pathName)
|
|
{
|
|
if (!dbus_message_set_path(RawMessage, value)) {
|
|
throw new OutOfMemoryException();
|
|
}
|
|
|
|
this.pathName = value;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
if (this.pathName == null) {
|
|
this.pathName = Marshal.PtrToStringAnsi(dbus_message_get_path(RawMessage));
|
|
}
|
|
|
|
return this.pathName;
|
|
}
|
|
}
|
|
|
|
protected virtual string InterfaceName
|
|
{
|
|
set
|
|
{
|
|
if (value != this.interfaceName)
|
|
{
|
|
dbus_message_set_interface (RawMessage, value);
|
|
this.interfaceName = value;
|
|
}
|
|
}
|
|
get
|
|
{
|
|
if (this.interfaceName == null) {
|
|
this.interfaceName = Marshal.PtrToStringAnsi(dbus_message_get_interface(RawMessage));
|
|
}
|
|
|
|
return this.interfaceName;
|
|
}
|
|
}
|
|
|
|
protected virtual string Name
|
|
{
|
|
set {
|
|
if (value != this.name) {
|
|
dbus_message_set_member(RawMessage, value);
|
|
this.name = value;
|
|
}
|
|
}
|
|
get {
|
|
if (this.name == null) {
|
|
this.name = Marshal.PtrToStringAnsi(dbus_message_get_member(RawMessage));
|
|
}
|
|
|
|
return this.name;
|
|
}
|
|
}
|
|
|
|
public string Key
|
|
{
|
|
get {
|
|
if (this.key == null) {
|
|
this.key = Name + " " + Arguments;
|
|
}
|
|
|
|
return this.key;
|
|
}
|
|
}
|
|
|
|
public Arguments Arguments
|
|
{
|
|
get
|
|
{
|
|
if (this.arguments == null) {
|
|
this.arguments = new Arguments(this);
|
|
}
|
|
|
|
return this.arguments;
|
|
}
|
|
}
|
|
|
|
public string Sender
|
|
{
|
|
get
|
|
{
|
|
return Marshal.PtrToStringAnsi(dbus_message_get_sender(RawMessage));
|
|
}
|
|
}
|
|
|
|
public string Destination
|
|
{
|
|
get
|
|
{
|
|
return Marshal.PtrToStringAnsi(dbus_message_get_destination(RawMessage));
|
|
}
|
|
}
|
|
|
|
protected int Slot
|
|
{
|
|
get
|
|
{
|
|
if (slot == -1)
|
|
{
|
|
// We need to initialize the slot
|
|
if (!dbus_message_allocate_data_slot (ref slot))
|
|
throw new OutOfMemoryException ();
|
|
|
|
Debug.Assert (slot >= 0);
|
|
}
|
|
|
|
return slot;
|
|
}
|
|
}
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_message_new")]
|
|
protected extern static IntPtr dbus_message_new (int messageType);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_message_unref")]
|
|
protected extern static void dbus_message_unref (IntPtr ptr);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_message_ref")]
|
|
protected extern static void dbus_message_ref (IntPtr ptr);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_message_allocate_data_slot")]
|
|
protected extern static bool dbus_message_allocate_data_slot (ref int slot);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_message_free_data_slot")]
|
|
protected extern static void dbus_message_free_data_slot (ref int slot);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_message_set_data")]
|
|
protected extern static bool dbus_message_set_data (IntPtr ptr,
|
|
int slot,
|
|
IntPtr data,
|
|
IntPtr free_data_func);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_message_get_data")]
|
|
protected extern static IntPtr dbus_message_get_data (IntPtr ptr,
|
|
int slot);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_connection_send")]
|
|
private extern static bool dbus_connection_send (IntPtr ptr,
|
|
IntPtr message,
|
|
ref int client_serial);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_connection_send_with_reply")]
|
|
private extern static bool dbus_connection_send_with_reply (IntPtr rawConnection, IntPtr rawMessage, IntPtr rawPendingCall, int timeout);
|
|
|
|
[DllImport ("dbus-1", EntryPoint="dbus_connection_send_with_reply_and_block")]
|
|
private extern static IntPtr dbus_connection_send_with_reply_and_block (IntPtr rawConnection, IntPtr message, int timeout, ref Error error);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static int dbus_message_get_type(IntPtr rawMessage);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static bool dbus_message_set_path(IntPtr rawMessage, string pathName);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static IntPtr dbus_message_get_path(IntPtr rawMessage);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static bool dbus_message_set_interface (IntPtr rawMessage, string interfaceName);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static IntPtr dbus_message_get_interface(IntPtr rawMessage);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static bool dbus_message_set_member(IntPtr rawMessage, string name);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static IntPtr dbus_message_get_member(IntPtr rawMessage);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static bool dbus_message_set_destination(IntPtr rawMessage, string serviceName);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static IntPtr dbus_message_get_destination(IntPtr rawMessage);
|
|
|
|
[DllImport("dbus-1")]
|
|
private extern static IntPtr dbus_message_get_sender(IntPtr rawMessage);
|
|
}
|
|
}
|