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)
{
NSRunLoop.Main.BeginInvokeOnMainThread (() => d (state));
NSRunLoop.Main.BeginInvokeOnMainThread (d, 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.Runtime.InteropServices;
using System.Threading;
using ObjCRuntime;
namespace Foundation {
#if !COREBUILD
// Use this for synchronous operations
[Register ("__MonoMac_NSActionDispatcher")]
internal sealed class NSActionDispatcher : NSObject {
internal abstract class NSDispatcher : NSObject
{
public const string SelectorName = "xamarinApplySelector";
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;
public NSActionDispatcher (Action action)
@ -43,40 +57,28 @@ namespace Foundation {
throw new ArgumentNullException ("action");
this.action = action;
IsDirectBinding = false;
}
[Export (SelectorName)]
[Preserve (Conditional = true)]
public void Apply ()
{
action ();
}
public override void Apply () => action ();
}
// Use this for synchronous operations
[Register ("__MonoMac_ActionDispatcher")]
internal sealed class ActionDispatcher : NSObject {
public const string SelectorName = "xamarinApplySelector";
public static readonly Selector Selector = new Selector (SelectorName);
[Register ("__MonoMac_NSSynchronizationContextDispatcher")]
internal sealed class NSSynchronizationContextDispatcher : NSDispatcher
{
readonly SendOrPostCallback d;
readonly object state;
readonly Action action;
public ActionDispatcher (Action action)
public NSSynchronizationContextDispatcher (SendOrPostCallback d, object state)
{
if (action == null)
throw new ArgumentNullException ("action");
if (d == null)
throw new ArgumentNullException (nameof (d));
this.action = action;
IsDirectBinding = false;
this.d = d;
this.state = state;
}
[Export (SelectorName)]
[Preserve (Conditional = true)]
public void Apply ()
{
action ();
}
public override void Apply () => d (state);
}
// 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
[Register ("__MonoMac_NSAsyncActionDispatcher")]
internal class NSAsyncActionDispatcher : NSObject {
GCHandle gch;
internal sealed class NSAsyncActionDispatcher : NSAsyncDispatcher {
Action action;
public NSAsyncActionDispatcher (Action action)
{
if (action == null)
throw new ArgumentNullException (nameof (action));
this.action = action;
gch = GCHandle.Alloc (this);
IsDirectBinding = false;
}
[Export (NSActionDispatcher.SelectorName)]
[Preserve (Conditional = true)]
public void Apply ()
public override void Apply ()
{
try {
action ();
} finally {
action = null; // this is a one-shot dispatcher
gch.Free ();
action = null;
base.Apply ();
}
}
}
//
// 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
[Register ("__MonoMac_NSAsyncSynchronizationContextDispatcher")]
internal sealed class NSAsyncSynchronizationContextDispatcher : NSAsyncDispatcher {
SendOrPostCallback d;
object state;
public NSAsyncSynchronizationContextDispatcher (SendOrPostCallback d, object state)
{
if (d == null)
throw new ArgumentNullException (nameof (d));
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);
#if MONOMAC
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDoneHandle,
NSActionDispatcher.Selector.Handle, d.Handle, false);
NSDispatcher.Selector.Handle, d.Handle, false);
#else
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
}
@ -588,14 +600,27 @@ namespace Foundation {
using (var d = new NSActionDispatcher (action)) {
#if MONOMAC
Messaging.void_objc_msgSend_IntPtr_IntPtr_bool (d.Handle, Selector.PerformSelectorOnMainThreadWithObjectWaitUntilDoneHandle,
NSActionDispatcher.Selector.Handle, d.Handle, true);
NSDispatcher.Selector.Handle, d.Handle, true);
#else
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
}
}
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)
{
if (obj == null)
@ -739,13 +764,13 @@ namespace Foundation {
public virtual void Invoke (Action action, double delay)
{
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)
{
var d = new NSAsyncActionDispatcher (action);
d.PerformSelector (NSActionDispatcher.Selector, null, delay.TotalSeconds);
d.PerformSelector (NSDispatcher.Selector, null, delay.TotalSeconds);
}
internal void ClearHandle ()

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

@ -21,12 +21,12 @@ namespace UIKit {
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)
{
NSRunLoop.Main.InvokeOnMainThread ( () => d (state) );
NSRunLoop.Main.InvokeOnMainThread (d, state);
}
}