-
Notifications
You must be signed in to change notification settings - Fork 899
Avoid reading and writing global state when loading native library #1563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b01c549
0e2665c
8a10035
748e596
671d058
2c1e96d
fc7b5b3
fe71caa
9aadf0b
896eb24
53e1fea
e618156
d9496ba
497f91f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Text; | ||
|
||
namespace LibGit2Sharp.Tests | ||
{ | ||
public static class ProcessHelper | ||
{ | ||
public static (string, int) RunProcess(string fileName, string arguments, string workingDirectory = null) | ||
{ | ||
var process = new Process | ||
{ | ||
StartInfo = new ProcessStartInfo(fileName, arguments) | ||
{ | ||
RedirectStandardError = true, | ||
RedirectStandardOutput = true, | ||
CreateNoWindow = true, | ||
UseShellExecute = false, | ||
WorkingDirectory = workingDirectory ?? string.Empty | ||
} | ||
}; | ||
|
||
var output = new StringBuilder(); | ||
|
||
process.OutputDataReceived += (_, e) => output.AppendLine(e.Data); | ||
process.ErrorDataReceived += (_, e) => output.AppendLine(e.Data); | ||
|
||
process.Start(); | ||
|
||
process.WaitForExit(); | ||
|
||
return (output.ToString(), process.ExitCode); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,15 @@ | ||
using System; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.ConstrainedExecution; | ||
using System.Runtime.InteropServices; | ||
using LibGit2Sharp.Core.Handles; | ||
|
||
// Restrict the set of directories where the native library is loaded from to safe directories. | ||
[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.ApplicationDirectory | DllImportSearchPath.SafeDirectories)] | ||
|
||
#pragma warning disable IDE1006 // Naming Styles | ||
|
||
// ReSharper disable InconsistentNaming | ||
namespace LibGit2Sharp.Core | ||
{ | ||
|
@@ -17,41 +22,68 @@ internal static class NativeMethods | |
// This will handle initialization and shutdown of the underlying | ||
// native library. | ||
#pragma warning disable 0414 | ||
private static readonly NativeShutdownObject shutdownObject; | ||
#pragma warning restore 0414 | ||
private static NativeShutdownObject shutdownObject; | ||
#pragma warning restore 0414 | ||
|
||
static NativeMethods() | ||
{ | ||
if (Platform.OperatingSystem == OperatingSystemType.Windows) | ||
if (Platform.IsRunningOnNetFramework() || Platform.IsRunningOnNetCore()) | ||
{ | ||
string nativeLibraryPath = GlobalSettings.GetAndLockNativeLibraryPath(); | ||
|
||
string path = Path.Combine(nativeLibraryPath, Platform.ProcessorArchitecture); | ||
|
||
const string pathEnvVariable = "PATH"; | ||
Environment.SetEnvironmentVariable(pathEnvVariable, | ||
String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", path, Path.PathSeparator, Environment.GetEnvironmentVariable(pathEnvVariable))); | ||
string nativeLibraryDir = GlobalSettings.GetAndLockNativeLibraryPath(); | ||
if (nativeLibraryDir != null) | ||
{ | ||
string nativeLibraryPath = Path.Combine(nativeLibraryDir, libgit2 + Platform.GetNativeLibraryExtension()); | ||
|
||
// Try to load the .dll from the path explicitly. | ||
// If this call succeeds further DllImports will find the library loaded and not attempt to load it again. | ||
// If it fails the next DllImport will load the library from safe directories. | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
{ | ||
LoadWindowsLibrary(nativeLibraryPath); | ||
} | ||
else | ||
{ | ||
LoadUnixLibrary(nativeLibraryPath, RTLD_NOW); | ||
} | ||
} | ||
} | ||
|
||
LoadNativeLibrary(); | ||
shutdownObject = new NativeShutdownObject(); | ||
InitializeNativeLibrary(); | ||
} | ||
|
||
public const int RTLD_NOW = 0x002; | ||
|
||
[DllImport("libdl", EntryPoint = "dlopen")] | ||
private static extern IntPtr LoadUnixLibrary(string path, int flags); | ||
|
||
[DllImport("kernel32", EntryPoint = "LoadLibrary")] | ||
private static extern IntPtr LoadWindowsLibrary(string path); | ||
|
||
// Avoid inlining this method because otherwise mono's JITter may try | ||
// to load the library _before_ we've configured the path. | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static void LoadNativeLibrary() | ||
private static void InitializeNativeLibrary() | ||
{ | ||
// Configure the OpenSSL locking on the true initialization | ||
// of the library. | ||
if (git_libgit2_init() == 1) | ||
int initCounter; | ||
try | ||
{ | ||
} | ||
finally // avoid thread aborts | ||
{ | ||
// Initialization can be called multiple times as long as there is a corresponding shutdown to each initialization. | ||
initCounter = git_libgit2_init(); | ||
shutdownObject = new NativeShutdownObject(); | ||
} | ||
|
||
// Configure the OpenSSL locking on the first initialization of the library in the current process. | ||
if (initCounter == 1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Help me better understand this construct? What happens if a thread aborts immediately before the If that analysis is correct then we need to protect against this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case The only purpose of the try-finally block is to make these two lines uninterruptible by thread abort: initCounter = git_libgit2_init();
shutdownObject = new NativeShutdownObject(); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, duh. You're of course right. I forgot that was the entire purpose of |
||
{ | ||
git_openssl_set_locking(); | ||
} | ||
} | ||
|
||
// Shutdown the native library in a finalizer. | ||
private sealed class NativeShutdownObject | ||
private sealed class NativeShutdownObject : CriticalFinalizerObject | ||
{ | ||
~NativeShutdownObject() | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sad to see this, but I can't think of a better option. Thankfully, every modern system that I looked at defines this to the same value.