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

Make plugins unloadable #16

Closed
natemcmaster opened this issue Oct 25, 2018 · 11 comments
Closed

Make plugins unloadable #16

natemcmaster opened this issue Oct 25, 2018 · 11 comments
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@natemcmaster
Copy link
Owner

natemcmaster commented Oct 25, 2018

This will require .NET Core 3.0 and up, which has added libraries to make assembly load contexts unloadable.

See https://github.com/dotnet/coreclr/issues/552, dotnet/coreclr#18476, https://github.com/dotnet/corefx/issues/14724, and https://github.com/dotnet/coreclr/projects/9.


Update

Merged #31 which adds API when using .NET Core 3.0 to allow unloading.

Usage

Unloadable plugins is supported in .NET Core 3.0 Preview 2 and newer. To unload a plugin, make sure isUnloadable = true and dispose the loader to trigger the unload.

var loader = PluginLoader.CreateFromAssemblyFile(
                    pluginDll,
                    sharedTypes: new [] { typeof(IPlugin) },
                    isUnloadable: true);
// do stuff

loader.Dispose(); // this triggers an unload

In order for this to work, you must make sure you have not left behind any variables (static, local, etc) or manually allocated memory. Once GC has collected everything, the assembly will be unloaded. See dotnet/coreclr#22221

@per-samuelsson
Copy link
Contributor

Should be pretty straightforward to implement as well as it seem: sample.

Only 3.0 need to become available. 😎

@natemcmaster natemcmaster added the enhancement New feature or request label Nov 2, 2018
@FrankDoersam
Copy link

FrankDoersam commented Nov 5, 2018

Thank you for the info. However, after unloading, the DLL could not be deleted from the directory? Have I possibly ignored something?

Thanks in advance.
Yours sincerely
Frank Dörsam

Code:

 class SimpleUnloadableAssemblyLoadContext : AssemblyLoadContext
    {
        public SimpleUnloadableAssemblyLoadContext()
           : base(isCollectible: true)
        {
        }

        protected override Assembly Load(AssemblyName assemblyName) => null;
    }

    class Program
    {
        private static void ExecuteAssembly(Assembly assembly)
        {
            MethodInfo entry = assembly.EntryPoint;
            foreach (Type type in assembly.GetTypes())
            {
                Console.WriteLine(type.FullName);
            }
            Console.WriteLine("ok");

        }

        static void Main(string[] args)
        {
            AssemblyLoadContext tt = new SimpleUnloadableAssemblyLoadContext();
            tt.LoadFromAssemblyPath(@"C:\Lokale Daten\AppDomainUnload\ConsoleApp1\bin\Debug\netcoreapp3.0\Plugin\Plugin1.dll");

            tt.Unload();
            Console.ReadKey();
        }

        private static void Context_Unloading(AssemblyLoadContext obj)
        {
            Console.WriteLine("Unloading");
        }
    }

@natemcmaster
Copy link
Owner Author

If you are not able to delete a dll after unloading its load context, it is probably a bug in .NET Core 3. Checkout https://github.com/dotnet/coreclr/projects/9 to see if this is a known issue. If not, open a new bug on dotnet/coreclr.

@HamedFathi
Copy link

@natemcmaster,

Based on .NET Core 3 preview 1

image

Is this issue possible to solve now or need the next versions?

@natemcmaster
Copy link
Owner Author

Yes. 3.0 preview 1 should have AssemblyLoadContext.Unload in it.

I don't think there is any use for MetadataLoadContext in this project. That API is more about "reflection-only" loading. It's similar to AssemblyLoadContext, but not exactly the same.

@natemcmaster
Copy link
Owner Author

Initial implementation #31.

Usage

Unloadable plugins is supported in .NET Core 3.0 Preview 2 and newer. To unload a plugin, dispose the loader.

var loader = PluginLoader.CreateFromAssemblyFile(
                    pluginDll,
                    sharedTypes: new [] { typeof(IPlugin) });
// do stuff

loader.Dispose(); // this triggers an unload

@dazinator
Copy link
Contributor

dazinator commented Feb 6, 2019

Any ideas whether this would allow asp.net core application parts to be unloaded (then reloaded from new assemblies) ? I asked / raised an issue but the response was its not on asp.net core roadmap: dotnet/aspnetcore#7219 - I wonder if you have had any thoughts around this?

@natemcmaster
Copy link
Owner Author

It might. Unloading is very new and I haven't played with it enough to try this out. Calling .Dispose() on a plugin doesn't guarantee it will be immediately unloaded. If managed memory still has pointers to something from the plugin, the plugin won't be cleaned up by garbage collection.

@natemcmaster
Copy link
Owner Author

Merged #31 which adds API when using .NET Core 3.0 to allow unloading.

Usage

Unloadable plugins is supported in .NET Core 3.0 Preview 2 and newer. To unload a plugin, make sure isUnloadable = true and dispose the loader to trigger the unload.

var loader = PluginLoader.CreateFromAssemblyFile(
                    pluginDll,
                    sharedTypes: new [] { typeof(IPlugin) },
                    isUnloadable: true);
// do stuff

loader.Dispose(); // this triggers an unload

In order for this to work, you must make sure you have not left behind any variables (static, local, etc) or manually allocated memory. Once GC has collected everything, the assembly will be unloaded. See dotnet/coreclr#22221

@natemcmaster natemcmaster self-assigned this Mar 26, 2019
@dazinator
Copy link
Contributor

Cool stuff. Is there a way to tell if a particular assembly has been unloaded? For example some kind of log output or something?

@natemcmaster
Copy link
Owner Author

There are some events triggered and you can also use a debugger. See https://docs.microsoft.com/en-us/dotnet/standard/assembly/unloadability-howto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants