This is an automated email from the ASF dual-hosted git repository.

ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 6fa0658  IGNITE-16749 .NET: Fix EntryPointNotFoundException on Alpine 
Linux (#9916)
6fa0658 is described below

commit 6fa06587ca31083e1f714ba285e5b18fde88995d
Author: Pavel Tupitsyn <[email protected]>
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.
---
 .../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>

Reply via email to