Skip to content

Commit b59741c

Browse files
committed
Enabled inlining for calls to IDispatcherQueueHandler.Release()
1 parent f5e2d27 commit b59741c

File tree

5 files changed

+26
-32
lines changed

5 files changed

+26
-32
lines changed

CommunityToolkit.WinUI/Extensions/DispatcherQueueExtensions{T}.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ public static unsafe bool TryEnqueue<T1, T2>(this DispatcherQueue dispatcherQueu
111111
/// <param name="dispatcherQueueHandler">The input callback to enqueue.</param>
112112
/// <returns>Whether or not the task was added to the queue.</returns>
113113
/// <exception cref="Exception">Thrown when the enqueue operation fails.</exception>
114-
private static unsafe bool TryEnqueue(DispatcherQueue dispatcherQueue, DispatcherQueuePriority? priority, IDispatcherQueueHandler* dispatcherQueueHandler)
114+
private static unsafe bool TryEnqueue<THandler>(DispatcherQueue dispatcherQueue, DispatcherQueuePriority? priority, THandler* dispatcherQueueHandler)
115+
where THandler : unmanaged, IDispatcherQueueHandler
115116
{
116117
bool success;
117118
int hResult;

CommunityToolkit.WinUI/Extensions/Interop/DispatcherQueueProxyHandler1.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace CommunityToolkit.WinUI.Interop
1919
/// A custom <c>IDispatcherQueueHandler</c> object, that internally stores a captured <see cref="DispatcherQueueHandler{TState}"/> instance
2020
/// and the input captured state. This allows consumers to enqueue a state and a cached stateless delegate without any managed allocations.
2121
/// </summary>
22-
internal unsafe struct DispatcherQueueProxyHandler1
22+
internal unsafe struct DispatcherQueueProxyHandler1 : IDispatcherQueueHandler
2323
{
2424
/// <summary>
2525
/// The shared vtable pointer for <see cref="DispatcherQueueProxyHandler1"/> instances.
@@ -68,12 +68,12 @@ internal unsafe struct DispatcherQueueProxyHandler1
6868
private volatile uint referenceCount;
6969

7070
/// <summary>
71-
/// Creates a new <see cref="IDispatcherQueueHandler"/> instance for the input callback and state.
71+
/// Creates a new <see cref="DispatcherQueueProxyHandler1"/> instance for the input callback and state.
7272
/// </summary>
7373
/// <param name="handler">The input <see cref="DispatcherQueueHandler{TState}"/> callback to enqueue.</param>
7474
/// <param name="state">The input state to capture and pass to the callback.</param>
75-
/// <returns>A pointer to the newly initialized <see cref="IDispatcherQueueHandler"/> instance.</returns>
76-
public static IDispatcherQueueHandler* Create(object handler, object state)
75+
/// <returns>A pointer to the newly initialized <see cref="DispatcherQueueProxyHandler1"/> instance.</returns>
76+
public static DispatcherQueueProxyHandler1* Create(object handler, object state)
7777
{
7878
DispatcherQueueProxyHandler1* @this = (DispatcherQueueProxyHandler1*)Marshal.AllocHGlobal(sizeof(DispatcherQueueProxyHandler1));
7979

@@ -82,7 +82,7 @@ internal unsafe struct DispatcherQueueProxyHandler1
8282
@this->stateHandle = GCHandle.Alloc(state);
8383
@this->referenceCount = 1;
8484

85-
return (IDispatcherQueueHandler*)@this;
85+
return @this;
8686
}
8787

8888
/// <summary>

CommunityToolkit.WinUI/Extensions/Interop/DispatcherQueueProxyHandler2.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace CommunityToolkit.WinUI.Interop
1919
/// A custom <c>IDispatcherQueueHandler</c> object, that internally stores a captured <see cref="DispatcherQueueHandler{T1,T2}"/> instance
2020
/// and the input captured state. This allows consumers to enqueue a state and a cached stateless delegate without any managed allocations.
2121
/// </summary>
22-
internal unsafe struct DispatcherQueueProxyHandler2
22+
internal unsafe struct DispatcherQueueProxyHandler2 : IDispatcherQueueHandler
2323
{
2424
/// <summary>
2525
/// The shared vtable pointer for <see cref="DispatcherQueueProxyHandler2"/> instances.
@@ -73,13 +73,13 @@ internal unsafe struct DispatcherQueueProxyHandler2
7373
private volatile uint referenceCount;
7474

7575
/// <summary>
76-
/// Creates a new <see cref="IDispatcherQueueHandler"/> instance for the input callback and state.
76+
/// Creates a new <see cref="DispatcherQueueProxyHandler2"/> instance for the input callback and state.
7777
/// </summary>
7878
/// <param name="handler">The input <see cref="DispatcherQueueHandler{T1,T2}"/> callback to enqueue.</param>
7979
/// <param name="state1">The first input state to capture and pass to the callback.</param>
8080
/// <param name="state2">The second input state to capture and pass to the callback.</param>
81-
/// <returns>A pointer to the newly initialized <see cref="IDispatcherQueueHandler"/> instance.</returns>
82-
public static IDispatcherQueueHandler* Create(object handler, object state1, object state2)
81+
/// <returns>A pointer to the newly initialized <see cref="DispatcherQueueProxyHandler2"/> instance.</returns>
82+
public static DispatcherQueueProxyHandler2* Create(object handler, object state1, object state2)
8383
{
8484
DispatcherQueueProxyHandler2* @this = (DispatcherQueueProxyHandler2*)Marshal.AllocHGlobal(sizeof(DispatcherQueueProxyHandler2));
8585

@@ -89,7 +89,7 @@ internal unsafe struct DispatcherQueueProxyHandler2
8989
@this->state2Handle = GCHandle.Alloc(state2);
9090
@this->referenceCount = 1;
9191

92-
return (IDispatcherQueueHandler*)@this;
92+
return @this;
9393
}
9494

9595
/// <summary>

CommunityToolkit.WinUI/Extensions/Interop/IDispatcherQueue.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@ internal unsafe struct IDispatcherQueue
2525
/// <param name="callback">A pointer to an <see cref="IDispatcherQueueHandler"/> object.</param>
2626
/// <param name="result">The result of the operation (the <see cref="bool"/> WinRT retval).</param>
2727
/// <returns>The HRESULT for the operation.</returns>
28+
/// <remarks>
29+
/// The <paramref name="callback"/> parameter is assumed to be a pointer to an <see cref="IDispatcherQueueHandler"/> object, but it
30+
/// is just typed as a <see cref="void"/> to avoid unnecessary generic instantiations for this method (as it just needs to pass a
31+
/// pointer to the native side anyway). The <see cref="IDispatcherQueueHandler"/> is an actual C# interface and not a C++ one as it
32+
/// is only used internally to constrain type parameters and allow calls to <see cref="IDispatcherQueueHandler.Release"/> to be inlined.
33+
/// </remarks>
2834
[MethodImpl(MethodImplOptions.AggressiveInlining)]
29-
public int TryEnqueue(IDispatcherQueueHandler* callback, byte* result)
35+
public int TryEnqueue(void* callback, byte* result)
3036
{
31-
return ((delegate* unmanaged<IDispatcherQueue*, IDispatcherQueueHandler*, byte*, int>)lpVtbl[7])((IDispatcherQueue*)Unsafe.AsPointer(ref this), callback, result);
37+
return ((delegate* unmanaged<IDispatcherQueue*, void*, byte*, int>)lpVtbl[7])((IDispatcherQueue*)Unsafe.AsPointer(ref this), callback, result);
3238
}
3339

3440
/// <summary>
@@ -39,9 +45,9 @@ public int TryEnqueue(IDispatcherQueueHandler* callback, byte* result)
3945
/// <param name="result">The result of the operation (the <see cref="bool"/> WinRT retval).</param>
4046
/// <returns>The HRESULT for the operation.</returns>
4147
[MethodImpl(MethodImplOptions.AggressiveInlining)]
42-
public int TryEnqueueWithPriority(DispatcherQueuePriority priority, IDispatcherQueueHandler* callback, byte* result)
48+
public int TryEnqueueWithPriority(DispatcherQueuePriority priority, void* callback, byte* result)
4349
{
44-
return ((delegate* unmanaged<IDispatcherQueue*, DispatcherQueuePriority, IDispatcherQueueHandler*, byte*, int>)lpVtbl[8])((IDispatcherQueue*)Unsafe.AsPointer(ref this), priority, callback, result);
50+
return ((delegate* unmanaged<IDispatcherQueue*, DispatcherQueuePriority, void*, byte*, int>)lpVtbl[8])((IDispatcherQueue*)Unsafe.AsPointer(ref this), priority, callback, result);
4551
}
4652
}
4753
}

CommunityToolkit.WinUI/Extensions/Interop/IDispatcherQueueHandler.cs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,17 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System.Runtime.CompilerServices;
6-
7-
#pragma warning disable CS0649, SA1023
8-
95
namespace CommunityToolkit.WinUI.Interop
106
{
117
/// <summary>
12-
/// A struct mapping the native WinRT <c>IDispatcherQueueHandler</c> interface.
8+
/// An interface mapping the native WinRT <c>IDispatcherQueueHandler</c> interface.
139
/// </summary>
14-
internal unsafe struct IDispatcherQueueHandler
10+
internal interface IDispatcherQueueHandler
1511
{
1612
/// <summary>
17-
/// The vtable pointer for the current instance.
18-
/// </summary>
19-
private readonly void** lpVtbl;
20-
21-
/// <summary>
22-
/// Native API for <c>IUnknown.Release()</c>.
13+
/// Implements <c>IUnknown.Release()</c>.
2314
/// </summary>
2415
/// <returns>The updated reference count for the current instance.</returns>
25-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
26-
public uint Release()
27-
{
28-
return ((delegate* unmanaged<IDispatcherQueueHandler*, uint>)lpVtbl[2])((IDispatcherQueueHandler*)Unsafe.AsPointer(ref this));
29-
}
16+
uint Release();
3017
}
3118
}

0 commit comments

Comments
 (0)