Browse Source

Fix iOS Dispatcher (#13942)

* Replace kCFRunLoopCommonModes with kCFRunLoopDefaultMode

* Replace delegates with function pointers in iOS backend

* Update DispatcherImpl.cs
pull/13976/head
Max Katz 3 years ago
committed by GitHub
parent
commit
447b9b93fe
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
  2. 57
      src/iOS/Avalonia.iOS/DispatcherImpl.cs
  3. 25
      src/iOS/Avalonia.iOS/Interop.cs

1
src/iOS/Avalonia.iOS/Avalonia.iOS.csproj

@ -3,6 +3,7 @@
<TargetFramework>net7.0-ios16.0</TargetFramework> <TargetFramework>net7.0-ios16.0</TargetFramework>
<SupportedOSPlatformVersion>13.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion>13.0</SupportedOSPlatformVersion>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver> <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />

57
src/iOS/Avalonia.iOS/DispatcherImpl.cs

@ -2,12 +2,11 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using Avalonia.Threading; using Avalonia.Threading;
using CoreFoundation; using CoreFoundation;
using Foundation; using Foundation;
using ObjCRuntime;
using CFIndex = System.IntPtr;
namespace Avalonia.iOS; namespace Avalonia.iOS;
@ -20,30 +19,28 @@ internal class DispatcherImpl : IDispatcherImplWithExplicitBackgroundProcessing
internal static readonly DispatcherImpl Instance = new(); internal static readonly DispatcherImpl Instance = new();
private readonly Stopwatch _clock = Stopwatch.StartNew(); private readonly Stopwatch _clock = Stopwatch.StartNew();
private readonly Action _checkSignaledAction; private readonly object _sync = new();
private readonly Action _wakeUpLoopAction;
private readonly IntPtr _timer; private readonly IntPtr _timer;
private readonly IntPtr _mainLoop;
private readonly IntPtr _mainQueue;
private Thread? _loopThread; private Thread? _loopThread;
private bool _backgroundProcessingRequested, _signaled; private bool _backgroundProcessingRequested, _signaled;
private DispatcherImpl() private unsafe DispatcherImpl()
{ {
_checkSignaledAction = CheckSignaled; _mainLoop = Interop.CFRunLoopGetMain();
_wakeUpLoopAction = () => _mainQueue = DispatchQueue.MainQueue.Handle.Handle;
{
// This is needed to wakeup the loop if we are called from inside of BeforeWait hook
};
var observer = Interop.CFRunLoopObserverCreate(IntPtr.Zero, var observer = Interop.CFRunLoopObserverCreate(IntPtr.Zero,
Interop.CFOptionFlags.kCFRunLoopAfterWaiting | Interop.CFOptionFlags.kCFRunLoopBeforeSources | Interop.CFOptionFlags.kCFRunLoopAfterWaiting | Interop.CFOptionFlags.kCFRunLoopBeforeSources |
Interop.CFOptionFlags.kCFRunLoopBeforeWaiting, Interop.CFOptionFlags.kCFRunLoopBeforeWaiting,
true, 0, ObserverCallback, IntPtr.Zero); 1, 0, &ObserverCallback, IntPtr.Zero);
Interop.CFRunLoopAddObserver(Interop.CFRunLoopGetMain(), observer, Interop.kCFRunLoopCommonModes); Interop.CFRunLoopAddObserver(_mainLoop, observer, Interop.kCFRunLoopDefaultMode);
_timer = Interop.CFRunLoopTimerCreate(IntPtr.Zero, _timer = Interop.CFRunLoopTimerCreate(IntPtr.Zero,
Interop.CFAbsoluteTimeGetCurrent() + DistantFutureInterval, Interop.CFAbsoluteTimeGetCurrent() + DistantFutureInterval,
DistantFutureInterval, 0, 0, TimerCallback, IntPtr.Zero); DistantFutureInterval, 0, 0, &TimerCallback, IntPtr.Zero);
Interop.CFRunLoopAddTimer(Interop.CFRunLoopGetMain(), _timer, Interop.kCFRunLoopCommonModes); Interop.CFRunLoopAddTimer(_mainLoop, _timer, Interop.kCFRunLoopDefaultMode);
} }
public event Action? Signaled; public event Action? Signaled;
@ -63,16 +60,16 @@ internal class DispatcherImpl : IDispatcherImplWithExplicitBackgroundProcessing
} }
} }
public void Signal() public unsafe void Signal()
{ {
lock (this) lock (_sync)
{ {
if (_signaled) if (_signaled)
return; return;
_signaled = true; _signaled = true;
DispatchQueue.MainQueue.DispatchAsync(_checkSignaledAction); Interop.dispatch_async_f(_mainQueue, IntPtr.Zero, &CheckSignaled);
CFRunLoop.Main.WakeUp(); Interop.CFRunLoopWakeUp(_mainLoop);
} }
} }
@ -86,18 +83,18 @@ internal class DispatcherImpl : IDispatcherImplWithExplicitBackgroundProcessing
public long Now => _clock.ElapsedMilliseconds; public long Now => _clock.ElapsedMilliseconds;
public void RequestBackgroundProcessing() public unsafe void RequestBackgroundProcessing()
{ {
if (_backgroundProcessingRequested) if (_backgroundProcessingRequested)
return; return;
_backgroundProcessingRequested = true; _backgroundProcessingRequested = true;
DispatchQueue.MainQueue.DispatchAsync(_wakeUpLoopAction); Interop.dispatch_async_f(_mainQueue, IntPtr.Zero, &WakeUpCallback);
} }
private void CheckSignaled() private void CheckSignaled()
{ {
bool signaled; bool signaled;
lock (this) lock (_sync)
{ {
signaled = _signaled; signaled = _signaled;
_signaled = false; _signaled = false;
@ -109,13 +106,25 @@ internal class DispatcherImpl : IDispatcherImplWithExplicitBackgroundProcessing
} }
} }
[MonoPInvokeCallback(typeof(Interop.CFRunLoopObserverCallback))] [UnmanagedCallersOnly]
private static void CheckSignaled(IntPtr context)
{
Instance.CheckSignaled();
}
[UnmanagedCallersOnly]
private static void WakeUpCallback(IntPtr context)
{
}
[UnmanagedCallersOnly]
private static void ObserverCallback(IntPtr observer, Interop.CFOptionFlags activity, IntPtr info) private static void ObserverCallback(IntPtr observer, Interop.CFOptionFlags activity, IntPtr info)
{ {
if (activity == Interop.CFOptionFlags.kCFRunLoopBeforeWaiting) if (activity == Interop.CFOptionFlags.kCFRunLoopBeforeWaiting)
{ {
bool triggerProcessing; bool triggerProcessing;
lock (Instance) lock (Instance._sync)
{ {
triggerProcessing = Instance._backgroundProcessingRequested; triggerProcessing = Instance._backgroundProcessingRequested;
Instance._backgroundProcessingRequested = false; Instance._backgroundProcessingRequested = false;
@ -127,7 +136,7 @@ internal class DispatcherImpl : IDispatcherImplWithExplicitBackgroundProcessing
Instance.CheckSignaled(); Instance.CheckSignaled();
} }
[MonoPInvokeCallback(typeof(Interop.CFRunLoopTimerCallback))] [UnmanagedCallersOnly]
private static void TimerCallback(IntPtr timer, IntPtr info) private static void TimerCallback(IntPtr timer, IntPtr info)
{ {
Instance.Timer?.Invoke(); Instance.Timer?.Invoke();

25
src/iOS/Avalonia.iOS/Interop.cs

@ -7,11 +7,11 @@ using ObjCRuntime;
namespace Avalonia.iOS; namespace Avalonia.iOS;
// TODO: use LibraryImport in NET7 internal unsafe class Interop
internal class Interop
{ {
internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
internal static NativeHandle kCFRunLoopCommonModes = CFString.CreateNative("kCFRunLoopCommonModes"); internal const string libcLibrary = "/usr/lib/libc.dylib";
internal static NativeHandle kCFRunLoopDefaultMode = CFString.CreateNative("kCFRunLoopDefaultMode");
[Flags] [Flags]
internal enum CFOptionFlags : ulong internal enum CFOptionFlags : ulong
@ -20,26 +20,29 @@ internal class Interop
kCFRunLoopAfterWaiting = (1UL << 6), kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopBeforeWaiting = (1UL << 5) kCFRunLoopBeforeWaiting = (1UL << 5)
} }
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate void CFRunLoopObserverCallback(IntPtr observer, CFOptionFlags activity, IntPtr info);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate void CFRunLoopTimerCallback(IntPtr timer, IntPtr info);
[DllImport(libcLibrary)]
internal static extern void dispatch_async_f(IntPtr queue, IntPtr context, delegate* unmanaged<IntPtr, void> dispatch);
[DllImport(CoreFoundationLibrary)] [DllImport(CoreFoundationLibrary)]
internal static extern IntPtr CFRunLoopGetMain(); internal static extern IntPtr CFRunLoopGetMain();
[DllImport(CoreFoundationLibrary)]
internal static extern IntPtr CFRunLoopGetCurrent();
[DllImport (CoreFoundationLibrary)]
internal static extern void CFRunLoopWakeUp(IntPtr rl);
[DllImport(CoreFoundationLibrary)] [DllImport(CoreFoundationLibrary)]
internal static extern IntPtr CFRunLoopObserverCreate(IntPtr allocator, CFOptionFlags activities, internal static extern IntPtr CFRunLoopObserverCreate(IntPtr allocator, CFOptionFlags activities,
bool repeats, int index, CFRunLoopObserverCallback callout, IntPtr context); int repeats, int index, delegate* unmanaged<IntPtr, CFOptionFlags, IntPtr, void> callout, IntPtr context);
[DllImport(CoreFoundationLibrary)] [DllImport(CoreFoundationLibrary)]
internal static extern IntPtr CFRunLoopAddObserver(IntPtr loop, IntPtr observer, IntPtr mode); internal static extern IntPtr CFRunLoopAddObserver(IntPtr loop, IntPtr observer, IntPtr mode);
[DllImport(CoreFoundationLibrary)] [DllImport(CoreFoundationLibrary)]
internal static extern IntPtr CFRunLoopTimerCreate(IntPtr allocator, double firstDate, double interval, internal static extern IntPtr CFRunLoopTimerCreate(IntPtr allocator, double firstDate, double interval,
CFOptionFlags flags, int order, CFRunLoopTimerCallback callout, IntPtr context); CFOptionFlags flags, int order, delegate* unmanaged<IntPtr, IntPtr, void> callout, IntPtr context);
[DllImport(CoreFoundationLibrary)] [DllImport(CoreFoundationLibrary)]
internal static extern void CFRunLoopTimerSetTolerance(IntPtr timer, double tolerance); internal static extern void CFRunLoopTimerSetTolerance(IntPtr timer, double tolerance);

Loading…
Cancel
Save