Skip to content

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

Description

@AArnott

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)

Activity

  1. AArnott commented on Nov 24, 2020

    @AArnott
    ContributorAuthor

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

  2. AArnott commented on Nov 24, 2020

    @AArnott
    ContributorAuthor

    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).

  3. AArnott commented on Nov 24, 2020

    @AArnott
    ContributorAuthor

    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. 🥳

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions