dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.16k stars 4.71k forks source link

Unable to unload AOT compiled library of a highly multithreaded .net library using dlcose() #103028

Closed mjacobz closed 4 months ago

mjacobz commented 4 months ago

Encountering an issue with our .net 8 multithreaded library compiled to Native AOT . The client app cannot successfully unload the library using dlclose(), resulting in crashes. This issue occurs specifically when the application is running multiple threads and when the threads are alive.

The code where this issue can be reproduced is listed here in the following repo.

https://github.com/mjacobz/dotnet8aot/tree/main

The AOT compilation can be done using the docker file in the repo and can be tested with the cpp client code.

filipnavara commented 4 months ago

NativeAOT libraries don't support unloading.

dotnet-policy-service[bot] commented 4 months ago

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas See info in area-owners.md if you want to be subscribed.

jkotas commented 4 months ago

.NET runtime (including native AOT form factor) does not support unloading.

dotnet-policy-service[bot] commented 4 months ago

Tagging subscribers to this area: @vitek-karas, @agocke, @vsadov See info in area-owners.md if you want to be subscribed.

jkotas commented 4 months ago

Is this mentioned in the docs? I am not able to find it.

neon-sunset commented 4 months ago

(The author might want to clarify that this is for unloading a dynamically linked NAOT library itself, via dlclose, rather than assembly unloading from within .NET)

mjacobz commented 4 months ago

This is for unloading a dynamically linked NAOT library itself, via dlclose

agocke commented 4 months ago

Native AOT doesn't unload assemblies and Native AOT programs compiled to native shared libraries cannot be unloaded via dlclose(). I will add a line to the documentation about this.

teo-tsirpanis commented 4 months ago

I wonder how difficult it would be to support it.

anoop331 commented 4 months ago

And why is it not supported today. We see that for a simple AOT compiled library, you can indeed unload the library using dlclose(), as long as there are for instance no threads running. We have this AOT compiled library running inside a systemd service on an embedded arm64 SOC, and we cannot keep the library open as it takes resources. But, trying to unload it, using dlcose(), kills the calling systemd executable. We would like to know if there is a plan to fix this in the future (net9 for instance). Or if there are any work arounds.

MichalStrehovsky commented 4 months ago

There is some discussion here: https://github.com/dotnet/corert/pull/7011

TL;DR: the .NET base class libraries are not designed to be unloadable and the runtime is not unloadable either.

jkotas commented 4 months ago

We see that for a simple AOT compiled library, you can indeed unload the library using dlclose(), as long as there are for instance no threads running.

This does not work. If you run dlclose on native AOT library and you do not see it crash badly, you are just lucky. There may be background threads running in pretty much all situations (e.g. the finalizer thread). dlclose won't release all memory allocated by the native AOT library, so you will see a significant memory leak.

TL;DR: the .NET base class libraries are not designed to be unloadable and the runtime is not unloadable either.

Right, clean shutdown of the base class libraries is the main problem. The core runtime (ie GC) can be made unloadable, but it is not interesting without having a clean shutdown path for BCL. We have no plans to fix it.

anoop331 commented 4 months ago

That’s good to know, we will then try to adapt our solution to live with this limitation. The challenge for us then is to dispose the resources when the lib is done with its stuffs - we face a challenge there as well, on arm64 the even a forced garbage collection doesn’t really release the unused memory, what the next best thing to do if you can’t unload the library and you have a long running process ?

teo-tsirpanis commented 4 months ago

The next best thing to do is to use a separate process (which contradicts the "long running process" requirement).

jkotas commented 4 months ago

what the next best thing to do

You may be looking for an API like what's proposed in https://github.com/dotnet/runtime/issues/30885