diff --git a/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj b/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj index 49a122b8e12..eb4938c13ff 100644 --- a/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj +++ b/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj @@ -3,6 +3,7 @@ net7.0-ios16.0 13.0 true + true diff --git a/src/iOS/Avalonia.iOS/DispatcherImpl.cs b/src/iOS/Avalonia.iOS/DispatcherImpl.cs index 8b3c747b5a1..9933126133e 100644 --- a/src/iOS/Avalonia.iOS/DispatcherImpl.cs +++ b/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 @@ public bool CurrentThreadIsLoopThread } } - 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 @@ public void UpdateTimer(long? dueTimeInMs) 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 @@ private void CheckSignaled() } } - [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 @@ private static void ObserverCallback(IntPtr observer, Interop.CFOptionFlags acti Instance.CheckSignaled(); } - [MonoPInvokeCallback(typeof(Interop.CFRunLoopTimerCallback))] + [UnmanagedCallersOnly] private static void TimerCallback(IntPtr timer, IntPtr info) { Instance.Timer?.Invoke(); diff --git a/src/iOS/Avalonia.iOS/Interop.cs b/src/iOS/Avalonia.iOS/Interop.cs index c0b3506936f..b05ddafc9ad 100644 --- a/src/iOS/Avalonia.iOS/Interop.cs +++ b/src/iOS/Avalonia.iOS/Interop.cs @@ -7,11 +7,11 @@ 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 enum CFOptionFlags : ulong 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 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 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 callout, IntPtr context); [DllImport(CoreFoundationLibrary)] internal static extern void CFRunLoopTimerSetTolerance(IntPtr timer, double tolerance);