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 2 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>
<SupportedOSPlatformVersion>13.0</SupportedOSPlatformVersion>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />

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

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

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

@ -7,11 +7,11 @@ using ObjCRuntime;
namespace Avalonia.iOS;
// TODO: use LibraryImport in NET7
internal class Interop
internal unsafe class Interop
{
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]
internal enum CFOptionFlags : ulong
@ -20,26 +20,29 @@ internal class Interop
kCFRunLoopAfterWaiting = (1UL << 6),
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)]
internal static extern IntPtr CFRunLoopGetMain();
[DllImport(CoreFoundationLibrary)]
internal static extern IntPtr CFRunLoopGetCurrent();
[DllImport (CoreFoundationLibrary)]
internal static extern void CFRunLoopWakeUp(IntPtr rl);
[DllImport(CoreFoundationLibrary)]
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)]
internal static extern IntPtr CFRunLoopAddObserver(IntPtr loop, IntPtr observer, IntPtr mode);
[DllImport(CoreFoundationLibrary)]
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)]
internal static extern void CFRunLoopTimerSetTolerance(IntPtr timer, double tolerance);

Loading…
Cancel
Save