.Net CLR Unloading vs shutdown? - c#

What Causes Finalize Methods to Be Called ?
2 answers( of 4 ) to this question are :
The CLR is unloading an AppDomain When an AppDomain unloads, the CLR considers
nothing in the AppDomain to be a root, and a garbage collection consisting of
all generations is performed.
The CLR is shutting down The CLR shuts down when a process terminates normally
(as opposed to an external shutdown via Task Manager, for example).
I assume that The CLR is unloading an AppDomain is when the program (console [exe] for example) is being closed ( by pressing close / normal end of program)
What about The CLR is shutting down ? continuing the [Exe] program analogy above :
what does it mean ? How can I ShutDown CLR ...?
[in the IIS world] does it mean IIS reset ?
please, can I have small explanation ?

The AppDomain is a more granular unit than the Process. A Process can can have multiple AppDomain instances, each of which can be unloaded separately.
The CLR shutdown is the process terminating.
The AppDomain unloading is each AppDomain separately.
(although I would not personally guarantee, without checking the documentation, that all finalizers etc are executed in either case)
As an example, I use multiple AppDomain instances in a long-running self-updating windows service; when new versions are detected it pulls down the new binaries, spawns a new AppDomain, gets it running, switches future operations to the new AppDomain, and unloads the old AppDomain (when running operations have completed).

Related

How to handle blocking synchronous external DLL methods

Recently I worked with an external dll library where I have no influence on it.
Under some special circumstances, a method of this third party dll is blocking and never returning.
I tried to work around this issue by executing this method in a new AppDomain. After a custom timeout, I wanted to Unload the AppDomain and kill all this crap ;)
Unfortunately, it does not work - as someone would expect.
After some time it throws CannotUnloadAppDomainException since the blocking method does not allow aborting the thread gracefully.
I depend on using this library and it does not seem that there will be an update soon.
So can I work around this issue, even if it's not best practice?
Any bad hack appreciated :)
An AppDomain cannot typically solve that problem, it's only good to throw away the state of your program. The real issue is that your thread is stuck. In cases like these, calling Thread.Abort() is unlikely to work, it will just get stuck as well. A thread can only be aborted if it is a "alertable wait state", blocking on a CLR synchronization object. Or executing managed code. In a state that the CLR knows how to safely clean up. Most 3rd party code falls over like this when executing unmanaged code, no way to ever clean that up in a safe way. A decisive hint that this is the case is AppDomain.Unload failing to get the job done, it can only unload the AppDomain when it can abort the threads that are executing code in the domain.
The only good alternative is to run that code in a separate process. Which you can kill with Process.Kill(). Windows do the cleanup. You'd use a .NET interop mechanism to talk to that code. Like named pipes, sockets, remoting or WCF. Plus the considerable hassle of having to write the code that can detect the timeout, kills the process, starts it back up and recovers internal state since you now restart with an uninitialized instance of that 3rd party code.
Do not forget about the real fix. Create a small repro project that reproduces the problem. When it hangs, create a minidump of the process. Send both to the 3rd party support group.
after reading this (scroll down the end to Blocking Issues) I think your only solution is to run the method in a different process - this might involve quite a bit of refactoring and/or a 'host' project (eg Console application) that loads the method in question and makes it easy to call (eg reading args from command line) when launching the new process using the Process class
You can always use background worker, no need to create a new appdomain. This will ensure that you have complete control over the execution of the thread.
However, there is no way to ensure that you can gracefully abort the thread. As the dll is unmanaged, chances are there that it may cause memory leaks. However, spawning a new thread will ensure that your application does not crash when the Dll does not respond.

unload AppDomain

A DLL process is running in an isolated App. Domain which was runned from a 3rd application.
I would like to unload AppDomain
If I use Enviroment.Exit(), it also kills the father application (app which lauches the DLL process)
edit
Doing as it follows neither:
AppDomain domainObj = AppDomain.CurrentDomain;
AppDomain.Unload(domainObj);
If a function from the DLL is running as a thread in a separate AppDomain in your process, then you can try calling AppDomain.Unload. According to the documentation,
When a thread calls Unload, the target domain is marked for unloading. The dedicated thread attempts to unload the domain, and all threads in the domain are aborted. If a thread does not abort, for example because it is executing unmanaged code, or because it is executing a finally block, then after a period of time a CannotUnloadAppDomainException is thrown in the thread that originally called Unload. If the thread that could not be aborted eventually ends, the target domain is not unloaded. Thus, in the .NET Framework version 2.0 domain is not guaranteed to unload, because it might not be possible to terminate executing threads.
So if you are unable to successfully unload this other appdomain - you may have to find other alternatives.

Does the System.Diagnostics.Process.Exited Event Always Fire?

I have an application that I am writting that communicates with a third-party program through a COM connection. I was tasked to strengthen the resilency of our application, in order to do that I wait until the third-party application is ready. I do this by waiting until the return value on a method that returns true only when the third-party application has been fully loaded.
In an effort to avoid the condition where the application crashes during this procedure, and my application just sits there waiting throwing COM errors( caught of course ) I was wondering if the Exited event on the Process itself, is guaranteed to always happen?
I am more then willing to provide additional details when its possible. I am also for the time being limited to .NET Framework 2.0 with this current build.
It depends on the sort of crash - some crashes leave the "process" in memory (for example just stuck in some never-ending loop OR that COM object opened some modal error dialog and waits for user)... these cases are not covered by any method mentioned in your description...
In my experience to be really resilient you need a combination:
hook the Exited event (if you started the application via Process) OR find the process ID and monitor that
catch any exceptions from that COM object
implement a timeout + abort logic for calls to that COM object/application
"monitor" the COM object/app
I usually start a separate thread before calling such a COM object... that thread monitors the COM object/application for example if it open any window it is not supposed or memory consumption goes through the roof when it is not supposed to etc. - the things monitored and the reaction to each of them is rather specific to that COM object/application...

Load DLL create Instance and Unload

I have a Application which copies a DLL (UserControlLibrary) to its own Debug/Release folder and loads it using this Code:
AppDomain appDomain = AppDomain.CreateDomain("MyDomain");
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "DLLs (*.dll)|*.dll";
if (dialog.ShowDialog().Value)
{
string newLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + dialog.SafeFileName;
File.Copy(dialog.FileName, newLocation, true);
Assembly assembly = appDomain.Load(AssemblyName.GetAssemblyName(newLocation));
UserControl userControl = (UserControl) assembly.CreateInstance("WpfControlLibrary1.UserControl1");
}
I now add that UserControl to a Grid using:
grid.Children.Add(userControl);
Works fine. Now I try to unload the DLL using:
AppDomain.Unload(appDomain);
grid.Children.Clear();
If I now try to load the DLL (because it changed meanwhile) again using the above code I get an error telling me the file is in use (File.Copy).
I've read a lot of things and my guess is that I'm not allowed to use the UserControl like I did (because that loads into the Main AppDomain). How do I have to change the Code to make it work?
I've also read a lot of things about using MarshalByRefObject but unfortunately I was unable to implement it in this project. A example or modification of the code above would be nice.
EDIT:
From the comments I've read so far (especially from svick) it looks like I have to use "AppDomain.CreateInstanceAndUnwrap" instead of "AppDomain.Load".
I've seen that approach already while searching for a solution earlier but as svick mentioned that doesn't work because a UserControl can't inherit from MarshalByRefObject.
Does anyone know another way?
Read the documentation for AppDomain.Load(). It specifically says that this method loads the assembly both into the assembly you are calling the method on and into the current assembly. So, even when you unload the app domain, the assembly still stays loaded in the current assembly.
I'm not sure there is a way around this as you can't make UserControl inherit from MarhshalByRefObject.
When you call unload, the AppDomain is not immediately unloaded (see MSDN for the full article):
When a thread calls Unload, the target domain is marked for unloading.
The dedicated thread attempts to unload the domain, and all threads in
the domain are aborted. If a thread does not abort, for example
because it is executing unmanaged code, or because it is executing a
finally block, then after a period of time a
CannotUnloadAppDomainException is thrown...
Perhaps this quote from the MSDN article for AppDomain.Unload could explain things:
In the .NET Framework version 2.0 there is a thread dedicated to unloading application domains. This improves reliability, especially when the .NET Framework is hosted. When a thread calls Unload, the target domain is marked for unloading. The dedicated thread attempts to unload the domain, and all threads in the domain are aborted. If a thread does not abort, for example because it is executing unmanaged code, or because it is executing a finally block, then after a period of time a CannotUnloadAppDomainException is thrown in the thread that originally called Unload. If the thread that could not be aborted eventually ends, the target domain is not unloaded. Thus, in the .NET Framework version 2.0 domain is not guaranteed to unload, because it might not be possible to terminate executing threads.
Maybe there are some rogue threads that are keeping the app domain alive and therefore also keeping your file locked.
As a workaround, you could copy the DLLs to random file names instead.

How to properly unload an AppDomain using C#?

I have an application that loads external assemblies which I have no control over (similar to a plugin model where other people create and develop assemblies that are used by the main application). It loads them by creating new AppDomains for these assemblies and then when the assemblies are done being used, the main AppDomain unloads them.
Currently, it simplistically unloads these assemblies by
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch(Exception exception)
{
// log exception
}
However, on occasion, exceptions are thrown during the unloading process specifically CannotUnloadAppDomainException. From what I understand, this can be expected since a thread in the children AppDomains cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally block:
When a thread calls Unload, the target
domain is marked for unloading. The
dedicated thread attempts to unload
the domain, and all threads in the
domain are aborted. If a thread does
not abort, for example because it is
executing unmanaged code, or because
it is executing a finally block, then
after a period of time a
CannotUnloadAppDomainException is
thrown in the thread that originally
called Unload. If the thread that
could not be aborted eventually ends,
the target domain is not unloaded.
Thus, in the .NET Framework version
2.0 domain is not guaranteed to unload, because it might not be
possible to terminate executing
threads.
My concern is that if the assembly is not loaded, then it could cause a memory leak. A potential solution would be to kill the main application process itself if the above exception occurs but I rather avoid this drastic action.
I was also considering repeating the unloading call for a few additional attempts. Perhaps a constrained loop like this:
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch (CannotUnloadAppDomainException exception)
{
// log exception
var i = 0;
while (i < 3) // quit after three tries
{
Thread.Sleep(3000); // wait a few secs before trying again...
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch (Exception)
{
// log exception
i++;
continue;
}
break;
}
}
Does this make sense? Should I even bother with trying to unload again? Should I just try it once and move on? Is there something else I should do? Also, is there anything that can be done from the main AppDomain to control the external assembly if threads are still running (keep in mind others are writing and running this external code)?
I'm trying understand what are best practices when managing multiple AppDomains.
I've dealt with a similar problem in my app. Basically, you can't do anything more to force the AppDomain to go down than Unload does.
It basically calls abort of all threads that are executing code in the AppDomain, and if that code is stuck in a finalizer or unmanaged code, there isn't much that can be done.
If, based on the program in question, it's likely that the finalizer/unmanaged code will finish some later time, you can absolutely call Unload again. If not, you can either leak the domain on purpose or cycle the process.
Try to make GC.Collect() if you do not unload the domain.
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch (CannotUnloadAppDomainException)
{
GC.Collect();
AppDomain.Unload(otherAssemblyDomain);
}
I had similar issues with random behavior for months now, (with some app.Unload even BLOCKING forever ! on some machines)
finally decided to take a big breath and made process isolation.
you can spawn child console process and redirect output
if you need to cancel this is finger in the nose to kill child process and all dependencies / handles.
To an extreme i had to run dedicated cleanup code, and came to solution to create additional process with dedicated cmd line waiting input extracted from console output of initial runner process.
yes this app domain is a real joke and i think this is not a coincidence that it is not anymore in net core.

Categories