Skip to content
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

System.InvalidOperationException : A resolver is already set for the assembly. #1855

Closed
AArnott opened this issue Nov 24, 2020 · 3 comments
Closed

Comments

@AArnott
Copy link
Contributor

AArnott commented Nov 24, 2020

After updating Nerdbank.GitVersioning to libgit2sharp 0.27.0-preview-0096, a great many of my tests are failing with this exception. Is it expected that SetConfigSearchPaths become callable-once-only?

Test Name:	GitContextLibGit2Tests.InitialDefaultState
Test FullName:	NerdBank.GitVersioning.Tests (net5.0).GitContextLibGit2Tests.GitContextLibGit2Tests.InitialDefaultState
Test Source:	D:\git\Nerdbank.GitVersioning\src\NerdBank.GitVersioning.Tests\GitContextTests.cs : line 40
Test Outcome:	Failed
Test Duration:	0:00:00

Test Name:	GitContextLibGit2Tests.InitialDefaultState
Test Outcome:	Failed
Result StackTrace:	
at LibGit2Sharp.Core.NativeMethods.git_repository_init_ext(git_repository*& repository, FilePath path, GitRepositoryInitOptions options)
   at LibGit2Sharp.Core.Proxy.git_repository_init_ext(FilePath workdirPath, FilePath gitdirPath, Boolean isBare)
   at LibGit2Sharp.Repository.Init(String path, Boolean isBare)
   at LibGit2Sharp.Repository.Init(String path)
   at RepoTestBase.InitializeSourceControl(String repoPath, Boolean withInitialCommit) in D:\git\Nerdbank.GitVersioning\src\NerdBank.GitVersioning.Tests\RepoTestBase.cs:line 108
   at RepoTestBase.InitializeSourceControl(Boolean withInitialCommit) in D:\git\Nerdbank.GitVersioning\src\NerdBank.GitVersioning.Tests\RepoTestBase.cs:line 102
   at GitContextTests..ctor(ITestOutputHelper logger) in D:\git\Nerdbank.GitVersioning\src\NerdBank.GitVersioning.Tests\GitContextTests.cs:line 35
   at GitContextLibGit2Tests..ctor(ITestOutputHelper logger) in D:\git\Nerdbank.GitVersioning\src\NerdBank.GitVersioning.Tests\GitContextTests.cs:line 23
----- Inner Stack Trace -----
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at LibGit2Sharp.Core.NativeMethods.TryUseNativeLibrary()
   at LibGit2Sharp.Core.NativeMethods..cctor()
----- Inner Stack Trace -----
   at System.Runtime.InteropServices.NativeLibrary.SetDllImportResolver(Assembly assembly, DllImportResolver resolver)
Result Message:	
System.TypeInitializationException : The type initializer for 'LibGit2Sharp.Core.NativeMethods' threw an exception.
---- System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
-------- System.InvalidOperationException : A resolver is already set for the assembly.

Originally posted by @AArnott in #1618 (comment)

@AArnott
Copy link
Contributor Author

AArnott commented Nov 24, 2020

Where is this SetDllImportResolver method? And why is it called twice when it's called by a static constructor?

@AArnott
Copy link
Contributor Author

AArnott commented Nov 24, 2020

Ok, the callstack for the original exception is:

 	System.Private.CoreLib.dll!System.ThrowHelper.ThrowArgumentException(System.ExceptionResource resource) Line 200	C#
 	System.Private.CoreLib.dll!System.Runtime.CompilerServices.ConditionalWeakTable<System.__Canon, System.__Canon>.Add(System.__Canon key, System.__Canon value) Line 85	C#
 	System.Private.CoreLib.dll!System.Runtime.InteropServices.NativeLibrary.SetDllImportResolver(System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportResolver resolver) Line 225	C#
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
>	LibGit2Sharp.dll!LibGit2Sharp.Core.NativeMethods.TryUseNativeLibrary() Line 127	C#
 	LibGit2Sharp.dll!LibGit2Sharp.Core.NativeMethods.NativeMethods() Line 37	C#
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	LibGit2Sharp.dll!LibGit2Sharp.Core.Proxy.git_repository_init_ext(LibGit2Sharp.Core.FilePath workdirPath, LibGit2Sharp.Core.FilePath gitdirPath, bool isBare) Line 2487	C#
 	LibGit2Sharp.dll!LibGit2Sharp.Repository.Init(string path, bool isBare) Line 498	C#
 	LibGit2Sharp.dll!LibGit2Sharp.Repository.Init(string path) Line 485	C#
 	NerdBank.GitVersioning.Tests.dll!RepoTestBase.InitializeSourceControl(string repoPath, bool withInitialCommit) Line 108	C#

The LibGit2Sharp part of this stack includes:

// NativeMethods.SetDllImportResolver(typeof(NativeMethods).Assembly, ResolveDll);
object resolveDelegate = typeof(NativeMethods).GetMethod(nameof(ResolveDll), BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate(dllImportResolverType);
setDllImportResolver.Invoke(null, new object[] { typeof(NativeMethods).Assembly, resolveDelegate });

This stack includes a static constructor, so the guarantee should be that it only runs once. But maybe other code calls this NativeLibrary.SetDllImportResolver as well before the static constructor runs.

Also of note, this problem only manifests on .NET Core (3.1 or 5.0).

@AArnott
Copy link
Contributor Author

AArnott commented Nov 24, 2020

Ah yes, my own unit tests were calling that same NativeLibrary.SetDllImportResolver method. I suppose it was from before libgit2sharp did it on its own? Anyway, once I remove my code that calls it, the tests pass even with the libgit2sharp upgrade. 🥳

@AArnott AArnott closed this as completed Nov 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant