skiasharp/binding/SkiaSharp/PlatformLock.cs
Max Katz 9cbf9c80b7
Function pointers and LibraryImport (#2917)
* Update generator to emit function pointers

* Regenerate interop files

* Enable USE_LIBRARY_IMPORT in HarfBuzzSharp

* Enable USE_LIBRARY_IMPORT on SkiaSharp

* Set DisableRuntimeMarshalling on HarfBuzzSharp

* Replace remaining DllImports with LibraryImport on SkiaSharp

* Set DisableRuntimeMarshalling on SkiaSharp as well

* Fix missed proxy definition

* Regenerate skia api with a correct submodule version

* Collections literals are not supported on the CI .NET SDK

* An attempt to fix Tizen build

* Forgot about partial

* Set UnmanagedType.LPStr on evas_gl_proc_address_get instead

* Set USE_LIBRARY_IMPORT on remaining projects too

* Update generator tool to generate DelegateProxy as well

* Regenerate HarfBuzz and SkiaSharp with new DelegateProxy source gen

* Regenerate other projects as well

* Add `protected internal` to test classes too, since this project has InternalsVisibleTo configured

* Disable DelegateTypesAreValid and DelegateTypesHaveAttributes tests on .NET 7+ build, see comments

* Reduce warnings noise

* Update binding/SkiaSharp/GRGlInterface.cs

Co-authored-by: Filip Navara <filip.navara@gmail.com>

* Add missing USE_LIBRARY_IMPORT defines

* Update binding/SkiaSharp/GRGlInterface.cs

* Also needs USE_LIBRARY_IMPORT

---------

Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
Co-authored-by: Filip Navara <filip.navara@gmail.com>
2024-08-21 23:16:05 +08:00

174 lines
5.1 KiB
C#

#nullable disable
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
/*
* This is a fix for issue #1383.
*
* https://github.com/mono/SkiaSharp/issues/1383
*
* On Windows, .NET locks are alertable when using the STA threading model and can
* cause the Windows message loop to be dispatched (typically on WM_PAINT messages).
* This can lead to re-entrancy and a deadlock on the HandleDictionary lock.
*
* This fix replaces the ReaderWriteLockSlim instance on Windows with a native Win32
* CRITICAL_SECTION.
*/
namespace SkiaSharp.Internals
{
/// <summary>
/// Abstracts a platform dependant lock implementation
/// </summary>
public interface IPlatformLock
{
void EnterReadLock ();
void ExitReadLock ();
void EnterWriteLock ();
void ExitWriteLock ();
void EnterUpgradeableReadLock ();
void ExitUpgradeableReadLock ();
}
/// <summary>
/// Helper class to create a IPlatformLock instance, by default according to the current platform
/// but also client toolkits can plugin their own implementation.
/// </summary>
public static partial class PlatformLock
{
/// <summary>
/// Creates a platform lock
/// </summary>
/// <returns></returns>
public static IPlatformLock Create ()
{
// Just call the factory
return Factory ();
}
/// <summary>
/// The factory for creating platform locks
/// </summary>
/// <remarks>
/// Use this to plugin your own lock implementation. Must be set
/// before using other SkiaSharp functionality that causes the lock
/// to be created (currently only used by SkiaSharps internal
/// HandleDictionary).
/// </remarks>
public static Func<IPlatformLock> Factory { get; set; } = DefaultFactory;
/// <summary>
/// Default platform lock factory
/// </summary>
/// <returns>A reference to a new platform lock implementation</returns>
public static IPlatformLock DefaultFactory ()
{
if (PlatformConfiguration.IsWindows)
return new NonAlertableWin32Lock ();
else
return new ReadWriteLock ();
}
/// <summary>
/// Non-Windows platform lock uses ReaderWriteLockSlim
/// </summary>
class ReadWriteLock : IPlatformLock
{
public void EnterReadLock () => _lock.EnterReadLock ();
public void ExitReadLock () => _lock.ExitReadLock ();
public void EnterWriteLock () => _lock.EnterWriteLock ();
public void ExitWriteLock () => _lock.ExitWriteLock ();
public void EnterUpgradeableReadLock () => _lock.EnterUpgradeableReadLock ();
public void ExitUpgradeableReadLock () => _lock.ExitUpgradeableReadLock ();
ReaderWriterLockSlim _lock = new ReaderWriterLockSlim ();
}
/// <summary>
/// Windows platform lock uses Win32 CRITICAL_SECTION
/// </summary>
partial class NonAlertableWin32Lock : IPlatformLock
{
public NonAlertableWin32Lock ()
{
_cs = Marshal.AllocHGlobal (Unsafe.SizeOf<CRITICAL_SECTION>());
if (_cs == IntPtr.Zero)
throw new OutOfMemoryException ("Failed to allocate memory for critical section");
InitializeCriticalSectionEx (_cs, 4000, 0);
}
~NonAlertableWin32Lock ()
{
if (_cs != IntPtr.Zero) {
DeleteCriticalSection (_cs);
Marshal.FreeHGlobal (_cs);
_cs = IntPtr.Zero;
}
}
IntPtr _cs;
void Enter ()
{
if (_cs != IntPtr.Zero) {
EnterCriticalSection(_cs);
}
}
void Leave ()
{
if (_cs != IntPtr.Zero) {
LeaveCriticalSection(_cs);
}
}
public void EnterReadLock () { Enter (); }
public void ExitReadLock () { Leave (); }
public void EnterWriteLock () { Enter (); }
public void ExitWriteLock () { Leave (); }
public void EnterUpgradeableReadLock () { Enter (); }
public void ExitUpgradeableReadLock () { Leave (); }
[StructLayout (LayoutKind.Sequential)]
public struct CRITICAL_SECTION
{
public IntPtr DebugInfo;
public int LockCount;
public int RecursionCount;
public IntPtr OwningThread;
public IntPtr LockSemaphore;
public UIntPtr SpinCount;
}
#if USE_LIBRARY_IMPORT
[LibraryImport ("Kernel32.dll", SetLastError = true)]
[return: MarshalAs (UnmanagedType.Bool)]
private static partial bool InitializeCriticalSectionEx (IntPtr lpCriticalSection, uint dwSpinCount, uint Flags);
[LibraryImport ("Kernel32.dll")]
private static partial void DeleteCriticalSection (IntPtr lpCriticalSection);
[LibraryImport ("Kernel32.dll")]
private static partial void EnterCriticalSection (IntPtr lpCriticalSection);
[LibraryImport ("Kernel32.dll")]
private static partial void LeaveCriticalSection (IntPtr lpCriticalSection);
#else
[DllImport ("Kernel32.dll", SetLastError = true)]
[return: MarshalAs (UnmanagedType.Bool)]
static extern bool InitializeCriticalSectionEx (IntPtr lpCriticalSection, uint dwSpinCount, uint Flags);
[DllImport ("Kernel32.dll")]
static extern void DeleteCriticalSection (IntPtr lpCriticalSection);
[DllImport ("Kernel32.dll")]
static extern void EnterCriticalSection (IntPtr lpCriticalSection);
[DllImport ("Kernel32.dll")]
static extern void LeaveCriticalSection (IntPtr lpCriticalSection);
#endif
}
}
}