You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2022/03/30 12:57:22 UTC
[ignite] branch ignite-2.13 updated: IGNITE-16749 .NET: Fix EntryPointNotFoundException on Alpine Linux (#9916)
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch ignite-2.13
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/ignite-2.13 by this push:
new 5f7608f IGNITE-16749 .NET: Fix EntryPointNotFoundException on Alpine Linux (#9916)
5f7608f is described below
commit 5f7608f3424b9e0adf55d78ad3e9d2cb50d6ef1c
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Tue Mar 29 18:53:25 2022 +0300
IGNITE-16749 .NET: Fix EntryPointNotFoundException on Alpine Linux (#9916)
On some Linux distros we can load `dlopen` and `pthread_*` symbols from `libcoreclr.so` to avoid dependency on `libc-dev`. However, this does not work on Alpine.
Add exception handler: if `libcoreclr` approach does not work, fall back to `libpthread.so` and `libdl.so`.
* `DllLoader` is called only once to load jvm.dll, it is fine to handle the exception inline.
* `UnmanagedThread` is called many times: perform the check once and set up delegates.
Tested on Alpine 3.15, Ubuntu 20.04, macOs Catalina 2019, Windows 10.
(cherry picked from commit 6fa06587ca31083e1f714ba285e5b18fde88995d)
---
.../Impl/Unmanaged/Jni/DllLoader.cs | 23 +-
.../Impl/Unmanaged/UnmanagedThread.cs | 318 +++++++++++++++------
2 files changed, 246 insertions(+), 95 deletions(-)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/DllLoader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/DllLoader.cs
index e7c41aa..494600d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/DllLoader.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/DllLoader.cs
@@ -86,18 +86,21 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
: null);
}
- if (Os.IsNetCore)
+ // Depending on the Linux distro, dlopen is either present in libdl or in libcoreclr.
+ try
{
- var ptr = NativeMethodsCore.dlopen(dllPath, RtldGlobal | RtldLazy);
+ var ptr = NativeMethodsLinuxLibcoreclr.dlopen(dllPath, RtldGlobal | RtldLazy);
return new KeyValuePair<IntPtr, string>(ptr, ptr == IntPtr.Zero
- ? GetErrorText(NativeMethodsCore.dlerror())
+ ? GetErrorText(NativeMethodsLinuxLibcoreclr.dlerror())
+ : null);
+ }
+ catch (EntryPointNotFoundException)
+ {
+ var ptr = NativeMethodsLinuxLibdl.dlopen(dllPath, RtldGlobal | RtldLazy);
+ return new KeyValuePair<IntPtr, string>(ptr, ptr == IntPtr.Zero
+ ? GetErrorText(NativeMethodsLinuxLibdl.dlerror())
: null);
}
-
- var lptr = NativeMethodsLinux.dlopen(dllPath, RtldGlobal | RtldLazy);
- return new KeyValuePair<IntPtr, string>(lptr, lptr == IntPtr.Zero
- ? GetErrorText(NativeMethodsLinux.dlerror())
- : null);
}
throw new InvalidOperationException("Unsupported OS: " + Environment.OSVersion);
@@ -149,7 +152,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
/// <summary>
/// Linux.
/// </summary>
- private static class NativeMethodsLinux
+ private static class NativeMethodsLinuxLibdl
{
[SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
[DllImport("libdl.so", SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = false,
@@ -181,7 +184,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
/// <summary>
/// libdl.so depends on libc6-dev on Linux, use libcoreclr instead.
/// </summary>
- private static class NativeMethodsCore
+ private static class NativeMethodsLinuxLibcoreclr
{
[SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
[DllImport("libcoreclr.so", SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = false,
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedThread.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedThread.cs
index 67a1665..e84fe4a 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedThread.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/UnmanagedThread.cs
@@ -27,6 +27,15 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
/// </summary>
internal static class UnmanagedThread
{
+ /** */
+ private static readonly Func<IntPtr, int> SetThreadExitCallbackDelegate;
+
+ /** */
+ private static readonly Action<int> RemoveThreadExitCallbackDelegate;
+
+ /** */
+ private static readonly Action<int, IntPtr> EnableCurrentThreadExitEventDelegate;
+
/// <summary>
/// Delegate for <see cref="SetThreadExitCallback"/>.
/// </summary>
@@ -36,9 +45,61 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
/// <summary>
/// Initializes the <see cref="UnmanagedThread"/> class.
/// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")]
+ [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
static UnmanagedThread()
{
NativeLibraryUtils.SetDllImportResolvers();
+
+ if (Os.IsWindows)
+ {
+ SetThreadExitCallbackDelegate = SetThreadExitCallbackWindows;
+ RemoveThreadExitCallbackDelegate = RemoveThreadExitCallbackWindows;
+ EnableCurrentThreadExitEventDelegate = EnableCurrentThreadExitEventWindows;
+ }
+ else if (Os.IsMacOs)
+ {
+ SetThreadExitCallbackDelegate = SetThreadExitCallbackMacOs;
+ RemoveThreadExitCallbackDelegate = RemoveThreadExitCallbackMacOs;
+ EnableCurrentThreadExitEventDelegate = EnableCurrentThreadExitEventMacOs;
+ }
+ else if (Os.IsLinux)
+ {
+ if (Os.IsMono)
+ {
+ SetThreadExitCallbackDelegate = SetThreadExitCallbackMono;
+ RemoveThreadExitCallbackDelegate = RemoveThreadExitCallbackMono;
+ EnableCurrentThreadExitEventDelegate = EnableCurrentThreadExitEventMono;
+ }
+ else
+ {
+ unsafe
+ {
+ // Depending on the Linux distro, use either libcoreclr or libpthread.
+ try
+ {
+ int tlsIndex;
+
+ CheckResult(NativeMethodsLinuxLibcoreclr.pthread_key_create(new IntPtr(&tlsIndex), IntPtr.Zero));
+ CheckResult(NativeMethodsLinuxLibcoreclr.pthread_key_delete(tlsIndex));
+
+ SetThreadExitCallbackDelegate = SetThreadExitCallbackLibcoreclr;
+ RemoveThreadExitCallbackDelegate = RemoveThreadExitCallbackLibcoreclr;
+ EnableCurrentThreadExitEventDelegate = EnableCurrentThreadExitEventLibcoreclr;
+ }
+ catch (EntryPointNotFoundException)
+ {
+ SetThreadExitCallbackDelegate = SetThreadExitCallbackLibpthread;
+ RemoveThreadExitCallbackDelegate = RemoveThreadExitCallbackLibpthread;
+ EnableCurrentThreadExitEventDelegate = EnableCurrentThreadExitEventLibpthread;
+ }
+ }
+ }
+ }
+ else
+ {
+ throw new InvalidOperationException("Unsupported OS: " + Environment.OSVersion);
+ }
}
/// <summary>
@@ -47,115 +108,195 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
/// <param name="callbackPtr">
/// Pointer to a callback function that matches <see cref="ThreadExitCallback"/>.
/// </param>
- public static unsafe int SetThreadExitCallback(IntPtr callbackPtr)
+ public static int SetThreadExitCallback(IntPtr callbackPtr)
{
Debug.Assert(callbackPtr != IntPtr.Zero);
- if (Os.IsWindows)
- {
- var res = NativeMethodsWindows.FlsAlloc(callbackPtr);
+ return SetThreadExitCallbackDelegate(callbackPtr);
+ }
- if (res == NativeMethodsWindows.FLS_OUT_OF_INDEXES)
- {
- throw new InvalidOperationException("FlsAlloc failed: " + Marshal.GetLastWin32Error());
- }
+ /// <summary>
+ /// Removes thread exit callback that has been set with <see cref="SetThreadExitCallback"/>.
+ /// NOTE: callback may be called as a result of this method call on some platforms.
+ /// </summary>
+ /// <param name="callbackId">Callback id returned from <see cref="SetThreadExitCallback"/>.</param>
+ public static void RemoveThreadExitCallback(int callbackId)
+ {
+ RemoveThreadExitCallbackDelegate(callbackId);
+ }
- return res;
- }
+ /// <summary>
+ /// Enables thread exit event for current thread.
+ /// </summary>
+ public static void EnableCurrentThreadExitEvent(int callbackId, IntPtr threadLocalValue)
+ {
+ Debug.Assert(threadLocalValue != IntPtr.Zero);
- if (Os.IsMacOs)
- {
- int tlsIndex;
- var res = NativeMethodsMacOs.pthread_key_create(new IntPtr(&tlsIndex), callbackPtr);
+ EnableCurrentThreadExitEventDelegate(callbackId, threadLocalValue);
+ }
- NativeMethodsLinux.CheckResult(res);
+ /// <summary>
+ /// Sets the thread exit callback.
+ /// </summary>
+ private static unsafe int SetThreadExitCallbackMacOs(IntPtr callbackPtr)
+ {
+ int tlsIndex;
+ var res = NativeMethodsMacOs.pthread_key_create(new IntPtr(&tlsIndex), callbackPtr);
- return tlsIndex;
- }
+ CheckResult(res);
- if (Os.IsLinux)
- {
- int tlsIndex;
- var res = Os.IsMono
- ? NativeMethodsMono.pthread_key_create(new IntPtr(&tlsIndex), callbackPtr)
- : NativeMethodsLinux.pthread_key_create(new IntPtr(&tlsIndex), callbackPtr);
+ return tlsIndex;
+ }
- NativeMethodsLinux.CheckResult(res);
+ /// <summary>
+ /// Sets the thread exit callback.
+ /// </summary>
+ private static int SetThreadExitCallbackWindows(IntPtr callbackPtr)
+ {
+ var res = NativeMethodsWindows.FlsAlloc(callbackPtr);
- return tlsIndex;
+ if (res == NativeMethodsWindows.FLS_OUT_OF_INDEXES)
+ {
+ throw new InvalidOperationException("FlsAlloc failed: " + Marshal.GetLastWin32Error());
}
- throw new InvalidOperationException("Unsupported OS: " + Environment.OSVersion);
+ return res;
+ }
+
+ /// <summary>
+ /// Sets the thread exit callback.
+ /// </summary>
+ private static unsafe int SetThreadExitCallbackMono(IntPtr callbackPtr)
+ {
+ int tlsIndex;
+
+ CheckResult(NativeMethodsMono.pthread_key_create(new IntPtr(&tlsIndex), callbackPtr));
+
+ return tlsIndex;
+ }
+
+ /// <summary>
+ /// Sets the thread exit callback.
+ /// </summary>
+ private static unsafe int SetThreadExitCallbackLibcoreclr(IntPtr callbackPtr)
+ {
+ int tlsIndex;
+
+ CheckResult(NativeMethodsLinuxLibcoreclr.pthread_key_create(new IntPtr(&tlsIndex), callbackPtr));
+
+ return tlsIndex;
+ }
+
+ /// <summary>
+ /// Sets the thread exit callback.
+ /// </summary>
+ private static unsafe int SetThreadExitCallbackLibpthread(IntPtr callbackPtr)
+ {
+ int tlsIndex;
+
+ CheckResult(NativeMethodsLinuxLibpthread.pthread_key_create(new IntPtr(&tlsIndex), callbackPtr));
+
+ return tlsIndex;
}
/// <summary>
/// Removes thread exit callback that has been set with <see cref="SetThreadExitCallback"/>.
- /// NOTE: callback may be called as a result of this method call on some platforms.
/// </summary>
- /// <param name="callbackId">Callback id returned from <see cref="SetThreadExitCallback"/>.</param>
- public static void RemoveThreadExitCallback(int callbackId)
+ private static void RemoveThreadExitCallbackLibpthread(int callbackId)
{
- if (Os.IsWindows)
- {
- var res = NativeMethodsWindows.FlsFree(callbackId);
+ CheckResult(NativeMethodsLinuxLibpthread.pthread_key_delete(callbackId));
+ }
- if (!res)
- {
- throw new InvalidOperationException("FlsFree failed: " + Marshal.GetLastWin32Error());
- }
- }
- else if (Os.IsMacOs)
- {
- var res = NativeMethodsMacOs.pthread_key_delete(callbackId);
- NativeMethodsLinux.CheckResult(res);
- }
- else if (Os.IsLinux)
- {
- var res = Os.IsMono
- ? NativeMethodsMono.pthread_key_delete(callbackId)
- : NativeMethodsLinux.pthread_key_delete(callbackId);
+ /// <summary>
+ /// Removes thread exit callback that has been set with <see cref="SetThreadExitCallback"/>.
+ /// </summary>
+ private static void RemoveThreadExitCallbackLibcoreclr(int callbackId)
+ {
+ CheckResult(NativeMethodsLinuxLibcoreclr.pthread_key_delete(callbackId));
+ }
- NativeMethodsLinux.CheckResult(res);
- }
- else
+ /// <summary>
+ /// Removes thread exit callback that has been set with <see cref="SetThreadExitCallback"/>.
+ /// </summary>
+ private static void RemoveThreadExitCallbackMono(int callbackId)
+ {
+ CheckResult(NativeMethodsMono.pthread_key_delete(callbackId));
+ }
+
+ /// <summary>
+ /// Removes thread exit callback that has been set with <see cref="SetThreadExitCallback"/>.
+ /// </summary>
+ private static void RemoveThreadExitCallbackMacOs(int callbackId)
+ {
+ CheckResult(NativeMethodsMacOs.pthread_key_delete(callbackId));
+ }
+
+ /// <summary>
+ /// Removes thread exit callback that has been set with <see cref="SetThreadExitCallback"/>.
+ /// </summary>
+ private static void RemoveThreadExitCallbackWindows(int callbackId)
+ {
+ var res = NativeMethodsWindows.FlsFree(callbackId);
+
+ if (!res)
{
- throw new InvalidOperationException("Unsupported OS: " + Environment.OSVersion);
+ throw new InvalidOperationException("FlsFree failed: " + Marshal.GetLastWin32Error());
}
}
/// <summary>
/// Enables thread exit event for current thread.
/// </summary>
- public static void EnableCurrentThreadExitEvent(int callbackId, IntPtr threadLocalValue)
+ private static void EnableCurrentThreadExitEventLibpthread(int callbackId, IntPtr threadLocalValue)
{
- Debug.Assert(threadLocalValue != IntPtr.Zero);
+ CheckResult(NativeMethodsLinuxLibpthread.pthread_setspecific(callbackId, threadLocalValue));
+ }
- // Store any value so that destructor callback is fired.
- if (Os.IsWindows)
- {
- var res = NativeMethodsWindows.FlsSetValue(callbackId, threadLocalValue);
+ /// <summary>
+ /// Enables thread exit event for current thread.
+ /// </summary>
+ private static void EnableCurrentThreadExitEventLibcoreclr(int callbackId, IntPtr threadLocalValue)
+ {
+ CheckResult(NativeMethodsLinuxLibcoreclr.pthread_setspecific(callbackId, threadLocalValue));
+ }
- if (!res)
- {
- throw new InvalidOperationException("FlsSetValue failed: " + Marshal.GetLastWin32Error());
- }
- }
- else if (Os.IsMacOs)
+ /// <summary>
+ /// Enables thread exit event for current thread.
+ /// </summary>
+ private static void EnableCurrentThreadExitEventMono(int callbackId, IntPtr threadLocalValue)
+ {
+ CheckResult(NativeMethodsMono.pthread_setspecific(callbackId, threadLocalValue));
+ }
+
+ /// <summary>
+ /// Enables thread exit event for current thread.
+ /// </summary>
+ private static void EnableCurrentThreadExitEventMacOs(int callbackId, IntPtr threadLocalValue)
+ {
+ CheckResult(NativeMethodsMacOs.pthread_setspecific(callbackId, threadLocalValue));
+ }
+
+ /// <summary>
+ /// Enables thread exit event for current thread.
+ /// </summary>
+ private static void EnableCurrentThreadExitEventWindows(int callbackId, IntPtr threadLocalValue)
+ {
+ var res = NativeMethodsWindows.FlsSetValue(callbackId, threadLocalValue);
+
+ if (!res)
{
- var res = NativeMethodsMacOs.pthread_setspecific(callbackId, threadLocalValue);
- NativeMethodsLinux.CheckResult(res);
+ throw new InvalidOperationException("FlsSetValue failed: " + Marshal.GetLastWin32Error());
}
- else if (Os.IsLinux)
- {
- var res = Os.IsMono
- ? NativeMethodsMono.pthread_setspecific(callbackId, threadLocalValue)
- : NativeMethodsLinux.pthread_setspecific(callbackId, threadLocalValue);
+ }
- NativeMethodsLinux.CheckResult(res);
- }
- else
+ /// <summary>
+ /// Checks native call result.
+ /// </summary>
+ private static void CheckResult(int res)
+ {
+ if (res != 0)
{
- throw new InvalidOperationException("Unsupported OS: " + Environment.OSVersion);
+ throw new InvalidOperationException("Native call failed: " + res);
}
}
@@ -185,7 +326,7 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
/// <summary>
/// Linux imports.
/// </summary>
- private static class NativeMethodsLinux
+ private static class NativeMethodsLinuxLibcoreclr
{
[SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Reviewed.")]
[DllImport("libcoreclr.so")]
@@ -198,17 +339,24 @@ namespace Apache.Ignite.Core.Impl.Unmanaged
[SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Reviewed.")]
[DllImport("libcoreclr.so")]
public static extern int pthread_setspecific(int key, IntPtr value);
+ }
- /// <summary>
- /// Checks native call result.
- /// </summary>
- public static void CheckResult(int res)
- {
- if (res != 0)
- {
- throw new InvalidOperationException("Native call failed: " + res);
- }
- }
+ /// <summary>
+ /// Linux imports.
+ /// </summary>
+ private static class NativeMethodsLinuxLibpthread
+ {
+ [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Reviewed.")]
+ [DllImport("libpthread.so")]
+ public static extern int pthread_key_create(IntPtr key, IntPtr destructorCallback);
+
+ [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Reviewed.")]
+ [DllImport("libpthread.so")]
+ public static extern int pthread_key_delete(int key);
+
+ [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Reviewed.")]
+ [DllImport("libpthread.so")]
+ public static extern int pthread_setspecific(int key, IntPtr value);
}
/// <summary>