Commit d14f8d43 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Simpler and safer event callbacks handling

parent c68490b3
...@@ -139,30 +139,12 @@ namespace VideoLAN.LibVLC ...@@ -139,30 +139,12 @@ namespace VideoLAN.LibVLC
*/ */
public abstract class EventingObject : BaseObject public abstract class EventingObject : BaseObject
{ {
/** private Dictionary<Delegate, IntPtr> events;
* @brief Managed to unmanaged event handler mapping
* @ingroup Internals
*
* The CLR cannot do reference counting for unmanaged callbacks.
* We keep track of handled events here instead.
*/
private class Event
{
public EventCallback managed;
public IntPtr unmanaged;
public Event (EventCallback managed, IntPtr unmanaged)
{
this.managed = managed;
this.unmanaged = unmanaged;
}
};
private Dictionary<EventType, Event> events;
/**< references to our unmanaged function pointers */ /**< references to our unmanaged function pointers */
internal EventingObject () : base () internal EventingObject () : base ()
{ {
events = new Dictionary<EventType, Event> (); events = new Dictionary<Delegate, IntPtr> ();
} }
/** /**
...@@ -187,19 +169,20 @@ namespace VideoLAN.LibVLC ...@@ -187,19 +169,20 @@ namespace VideoLAN.LibVLC
* @param callback callback to invoke when the event occurs * @param callback callback to invoke when the event occurs
* *
* @note * @note
* For simplicity, we only allow one handler per type. * For simplicity, we require distinct callbacks for each event type.
* Multicasting can be implemented higher up with managed code. * This is hardly an issue since most events have different formats.
*/ */
internal void Attach (EventType type, EventCallback callback) internal void Attach (EventType type, Delegate callback)
{ {
EventManagerHandle manager; EventManagerHandle manager;
IntPtr cb = Marshal.GetFunctionPointerForDelegate (callback); IntPtr cb = Marshal.GetFunctionPointerForDelegate (callback);
Event ev = new Event (callback, cb);
bool unref = false; bool unref = false;
if (events.ContainsKey (type)) /* If things go wrong, we will leak the callback thunk... until
throw new ArgumentException ("Duplicate event"); * this object is destroyed anyway. If we added the thunk _after_
* the critical section, the native code could try to jump to a
* non-existent address, which is much worse. */
events.Add (callback, cb);
try try
{ {
handle.DangerousAddRef (ref unref); handle.DangerousAddRef (ref unref);
...@@ -212,19 +195,19 @@ namespace VideoLAN.LibVLC ...@@ -212,19 +195,19 @@ namespace VideoLAN.LibVLC
handle.DangerousRelease (); handle.DangerousRelease ();
} }
Raise (); Raise ();
events.Add (type, ev);
} }
private void Detach (EventType type, IntPtr callback) internal void Detach (EventType type, Delegate callback)
{ {
EventManagerHandle manager; EventManagerHandle manager;
IntPtr cb = events[callback];
bool unref = false; bool unref = false;
try try
{ {
handle.DangerousAddRef (ref unref); handle.DangerousAddRef (ref unref);
manager = GetManager (); manager = GetManager ();
LibVLC.EventDetach (manager, type, callback, IntPtr.Zero, ex); LibVLC.EventDetach (manager, type, cb, IntPtr.Zero, ex);
} }
finally finally
{ {
...@@ -232,12 +215,7 @@ namespace VideoLAN.LibVLC ...@@ -232,12 +215,7 @@ namespace VideoLAN.LibVLC
handle.DangerousRelease (); handle.DangerousRelease ();
} }
Raise (); Raise ();
events.Remove (type); events.Remove (callback);
}
internal void Detach (EventType type)
{
Detach(type, events[type].unmanaged);
} }
}; };
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment