Optimize NSActionDispatcher usage (#4162)

* [NSActionDispatcher] Remove unused class

* [NSActionDispatcher] Move selectorname and selector variable into each class

* [NSActionDispatcher] Add specialized versions of NSActionDispatcher that use SynchronizationContext parameters

* [NSObject] Add specialized overloads for *InvokeOnMainThread which use SynchronizationContext parameters

* [AppKit,UIKit] Use the synchronization context specialized versions of *InvokeOnMainThread

This finishes the PR adding the following value:

1. There is no Action wrapper being constructed on async continuations,
thus on every await call we gain: 1 less allocation (lambda capture),
1 less indirected call to the actual continuation, and possibly
Action being removed by the linker, as its no longer used.

2. NSActionDispatcher* classes might now be linked out, due to
the static selector no longer being used everywhere

3. One unused class removed

* PR feedback

* Fix build

* PR feedback 2

* Seal this class

* Fix some renames not followed because they were undef ifdef'd code
This commit is contained in:
Marius Ungureanu 2018-06-01 08:27:43 +03:00 коммит произвёл Rolf Bjarne Kvinge
Родитель d5263edd69
Коммит 0dd3dece90
4 изменённых файлов: 123 добавлений и 57 удалений

Просмотреть файл

@ -14,12 +14,12 @@ namespace AppKit {
public override void Post (SendOrPostCallback d, object state) public override void Post (SendOrPostCallback d, object state)
{ {
NSRunLoop.Main.BeginInvokeOnMainThread (() => d (state)); NSRunLoop.Main.BeginInvokeOnMainThread (d, state);
} }
public override void Send (SendOrPostCallback d, object state) public override void Send (SendOrPostCallback d, object state)
{ {
NSRunLoop.Main.InvokeOnMainThread (() => d (state)); NSRunLoop.Main.InvokeOnMainThread (d, state);
} }
} }
} }

Просмотреть файл

@ -24,17 +24,31 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using ObjCRuntime; using ObjCRuntime;
namespace Foundation { namespace Foundation {
#if !COREBUILD #if !COREBUILD
// Use this for synchronous operations // Use this for synchronous operations
[Register ("__MonoMac_NSActionDispatcher")] internal abstract class NSDispatcher : NSObject
internal sealed class NSActionDispatcher : NSObject { {
public const string SelectorName = "xamarinApplySelector"; public const string SelectorName = "xamarinApplySelector";
public static readonly Selector Selector = new Selector (SelectorName); public static readonly Selector Selector = new Selector (SelectorName);
protected NSDispatcher ()
{
IsDirectBinding = false;
}
[Export (SelectorName)]
[Preserve (Conditional = true)]
public abstract void Apply ();
}
// Use this for synchronous operations
[Register ("__MonoMac_NSActionDispatcher")]
internal sealed class NSActionDispatcher : NSDispatcher {
readonly Action action; readonly Action action;
public NSActionDispatcher (Action action) public NSActionDispatcher (Action action)
@ -43,40 +57,28 @@ namespace Foundation {
throw new ArgumentNullException ("action"); throw new ArgumentNullException ("action");
this.action = action; this.action = action;
IsDirectBinding = false;
} }
[Export (SelectorName)] public override void Apply () => action ();
[Preserve (Conditional = true)]
public void Apply ()
{
action ();
}
} }
// Use this for synchronous operations // Use this for synchronous operations
[Register ("__MonoMac_ActionDispatcher")] [Register ("__MonoMac_NSSynchronizationContextDispatcher")]
internal sealed class ActionDispatcher : NSObject { internal sealed class NSSynchronizationContextDispatcher : NSDispatcher
public const string SelectorName = "xamarinApplySelector"; {
public static readonly Selector Selector = new Selector (SelectorName); readonly SendOrPostCallback d;
readonly object state;
readonly Action action; public NSSynchronizationContextDispatcher (SendOrPostCallback d, object state)
public ActionDispatcher (Action action)
{ {
if (action == null) if (d == null)
throw new ArgumentNullException ("action"); throw new ArgumentNullException (nameof (d));
this.action = action; this.d = d;
IsDirectBinding = false; this.state = state;
} }
[Export (SelectorName)] public override void Apply () => d (state);
[Preserve (Conditional = true)]
public void Apply ()
{
action ();
}
} }
// Used this for NSTimer support // Used this for NSTimer support
@ -104,41 +106,80 @@ namespace Foundation {
} }
} }
abstract class NSAsyncDispatcher : NSDispatcher {
readonly GCHandle gch;
protected NSAsyncDispatcher ()
{
gch = GCHandle.Alloc (this);
}
public override void Apply ()
{
gch.Free ();
//
// Although I would like to call Dispose here, to
// reduce the load on the GC, we have some useful diagnostic
// code in our runtime that is useful to track down
// problems, so we are removing the Dispose and letting
// the GC and our pipeline do their job.
//
#if MONOTOUCH
// MonoTouch has fixed the above problems, and we can call
// Dispose here.
Dispose ();
#endif
}
}
// Use this for asynchronous operations // Use this for asynchronous operations
[Register ("__MonoMac_NSAsyncActionDispatcher")] [Register ("__MonoMac_NSAsyncActionDispatcher")]
internal class NSAsyncActionDispatcher : NSObject { internal sealed class NSAsyncActionDispatcher : NSAsyncDispatcher {
GCHandle gch;
Action action; Action action;
public NSAsyncActionDispatcher (Action action) public NSAsyncActionDispatcher (Action action)
{ {
if (action == null)
throw new ArgumentNullException (nameof (action));
this.action = action; this.action = action;
gch = GCHandle.Alloc (this);
IsDirectBinding = false;
} }
[Export (NSActionDispatcher.SelectorName)] public override void Apply ()
[Preserve (Conditional = true)]
public void Apply ()
{ {
try { try {
action (); action ();
} finally { } finally {
action = null; // this is a one-shot dispatcher action = null;
gch.Free (); base.Apply ();
}
}
}
// // Use this for asynchronous operations
// Although I would like to call Dispose here, to [Register ("__MonoMac_NSAsyncSynchronizationContextDispatcher")]
// reduce the load on the GC, we have some useful diagnostic internal sealed class NSAsyncSynchronizationContextDispatcher : NSAsyncDispatcher {
// code in our runtime that is useful to track down SendOrPostCallback d;
// problems, so we are removing the Dispose and letting object state;
// the GC and our pipeline do their job.
// public NSAsyncSynchronizationContextDispatcher (SendOrPostCallback d, object state)
#if MONOTOUCH {
// MonoTouch has fixed the above problems, and we can call if (d == null)
// Dispose here. throw new ArgumentNullException (nameof (d));
Dispose ();
#endif this.d = d;
this.state = state;
}
public override void Apply ()
{
try {
d (state);
} finally {
d = null; // this is a one-shot dispatcher
state = null;
base.Apply ();
} }
} }
} }

Просмотреть файл

@ -576,10 +576,22 @@ namespace Foundation {
var d = new NSAsyncActionDispatcher (action); var d = new NSAsyncActionDispatcher (action);
#if MONOMAC #if MONOMAC
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDoneHandle, Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDoneHandle,
NSActionDispatcher.Selector.Handle, d.Handle, false); NSDispatcher.Selector.Handle, d.Handle, false);
#else #else
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.GetHandle (Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDone), Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.GetHandle (Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDone),
Selector.GetHandle (NSActionDispatcher.SelectorName), d.Handle, false); Selector.GetHandle (NSDispatcher.SelectorName), d.Handle, false);
#endif
}
internal void BeginInvokeOnMainThread (System.Threading.SendOrPostCallback cb, object state)
{
var d = new NSAsyncSynchronizationContextDispatcher (cb, state);
#if MONOMAC
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDoneHandle,
NSDispatcher.Selector.Handle, d.Handle, false);
#else
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.GetHandle (Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDone),
Selector.GetHandle (NSDispatcher.SelectorName), d.Handle, false);
#endif #endif
} }
@ -588,14 +600,27 @@ namespace Foundation {
using (var d = new NSActionDispatcher (action)) { using (var d = new NSActionDispatcher (action)) {
#if MONOMAC #if MONOMAC
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDoneHandle, Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDoneHandle,
NSActionDispatcher.Selector.Handle, d.Handle, true); NSDispatcher.Selector.Handle, d.Handle, true);
#else #else
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.GetHandle (Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDone), Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.GetHandle (Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDone),
Selector.GetHandle (NSActionDispatcher.SelectorName), d.Handle, true); Selector.GetHandle (NSDispatcher.SelectorName), d.Handle, true);
#endif #endif
} }
} }
internal void InvokeOnMainThread (System.Threading.SendOrPostCallback cb, object state)
{
using (var d = new NSSynchronizationContextDispatcher (cb, state)) {
#if MONOMAC
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDoneHandle,
NSDispatcher.Selector.Handle, d.Handle, true);
#else
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.GetHandle (Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDone),
Selector.GetHandle (NSDispatcher.SelectorName), d.Handle, true);
#endif
}
}
public static NSObject FromObject (object obj) public static NSObject FromObject (object obj)
{ {
if (obj == null) if (obj == null)
@ -739,13 +764,13 @@ namespace Foundation {
public virtual void Invoke (Action action, double delay) public virtual void Invoke (Action action, double delay)
{ {
var d = new NSAsyncActionDispatcher (action); var d = new NSAsyncActionDispatcher (action);
d.PerformSelector (NSActionDispatcher.Selector, null, delay); d.PerformSelector (NSDispatcher.Selector, null, delay);
} }
public virtual void Invoke (Action action, TimeSpan delay) public virtual void Invoke (Action action, TimeSpan delay)
{ {
var d = new NSAsyncActionDispatcher (action); var d = new NSAsyncActionDispatcher (action);
d.PerformSelector (NSActionDispatcher.Selector, null, delay.TotalSeconds); d.PerformSelector (NSDispatcher.Selector, null, delay.TotalSeconds);
} }
internal void ClearHandle () internal void ClearHandle ()

Просмотреть файл

@ -21,12 +21,12 @@ namespace UIKit {
public override void Post (SendOrPostCallback d, object state) public override void Post (SendOrPostCallback d, object state)
{ {
NSRunLoop.Main.BeginInvokeOnMainThread ( () => d (state) ); NSRunLoop.Main.BeginInvokeOnMainThread (d, state);
} }
public override void Send (SendOrPostCallback d, object state) public override void Send (SendOrPostCallback d, object state)
{ {
NSRunLoop.Main.InvokeOnMainThread ( () => d (state) ); NSRunLoop.Main.InvokeOnMainThread (d, state);
} }
} }