Why exception in one application domain affect another application domain?
How do I prevent the closing of the program?
using System;
using System.Reflection;
using System.Threading;
namespace domain
{
public class Worker : MarshalByRefObject
{
public static void NotMyCodeThreadProc()
{
throw new Exception();
}
public void NotMyCode()
{
var thread = new Thread(NotMyCodeThreadProc);
thread.Start();
thread.Join();
}
}
class Program
{
static void Main()
{
AppDomain ad = AppDomain.CreateDomain("New domain");
Worker remoteWorker = (Worker) ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "domain.Worker");
try
{
remoteWorker.NotMyCode();
}
catch
{
}
Console.WriteLine("!");
Console.ReadLine();
}
}
}
In .NET 2.0 (and above), an unhandled exception in a thread causes the entire process to terminate.
You can change that policy by following Hans' advice, or you can simply wrap your code with try/catch and handle the exception.
Related
I'm using this class to prevent two application (one windows application and one web application running on the same OS) using a shared file at the same time. But I get the error "Object synchronization method was called from an unsynchronized block of code."
class SharedMutex : IDisposable
{
readonly Mutex mutex;
/// <summary>
/// This function will wait if other thread has owned the mutex
/// </summary>
/// <param name="name"></param>
public SharedMutex(string name)
{
bool m = Mutex.TryOpenExisting(name, out mutex);
if (m)
{
mutex.WaitOne();
}
else
{
mutex = new Mutex(true, name);
}
}
public const string Logs = #"Global\Logs";
public void Dispose()
{
mutex.ReleaseMutex();
mutex.Dispose();
}
}
And this is the way I'm using this class
using (new SharedMutex(SharedMutex.Logs))
{
///access the shared file
}
This class exists in both projects.
Note: I am not looking for a solution to the problem to access the files, I need to know why my code has problem. Because I want to use this code for other purposes also.
Thank you.
I think that this is likely caused by a race condition (as suggested by /u/Sinatr in the comments to the question).
The following code reproduces the issue:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
var t1 = Task.Run(func1);
var t2 = Task.Run(func2);
Task.WaitAll(t1, t2);
Console.WriteLine("Done. Press <ENTER>");
Console.ReadLine();
}
static void func1()
{
using (new SharedMutex("test"))
{
Thread.Sleep(2000);
}
}
static void func2()
{
using (new SharedMutex("test"))
{
Thread.Sleep(1000);
}
}
}
class SharedMutex : IDisposable
{
readonly Mutex mutex;
public SharedMutex(string name)
{
bool m = Mutex.TryOpenExisting(name, out mutex);
if (m)
{
mutex.WaitOne();
}
else
{
Thread.Sleep(10); // Simulate a short delay.
mutex = new Mutex(true, name);
}
}
public void Dispose()
{
mutex.ReleaseMutex();
mutex.Dispose();
}
}
}
Note the short delay Thread.Sleep(10) which is enough to provoke the exception on my system, running the debug build. The delay may have to be increased to provoke the exception on other systems.
If this is indeed the problem, this is how to fix it:
class SharedMutex : IDisposable
{
readonly Mutex mutex;
public SharedMutex(string name)
{
mutex = new Mutex(false, name);
mutex.WaitOne();
}
public void Dispose()
{
mutex.ReleaseMutex();
mutex.Dispose();
}
}
You don't really care if it was newly created. The Mutex constructor takes care of that for you.
Even if this isn't the problem, I still think you should use the implementation above, since it does not have a potential race condition.
As the documentation for the constructor Mutex(bool, string) states:
If name is not null and initiallyOwned is true, the calling thread
owns the mutex only if the named system mutex was created as a result
of this call. Since there is no mechanism for determining whether the
named system mutex was created, it is better to specify false for
initiallyOwned when calling this constructor overload.
I have a console application which talks to an external library. Unfortunately all calls to the library must be made from the same thread.
How can I send method calls from one thread to another? (And, obviously, send the method results back to the calling thread.)
(No, this isn't to do with GUI programming. No, using the GUI message pump won't work.)
What I really want is for every single method on a particular class to always be executed in the same thread. But I have no idea how to do that.
My advice is to do what Windows Forms and WPF do to set up their single threaded message pumps - inherit SynchronizationContext. http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext%28v=vs.110%29.aspx
In your implementation, you will need to maintain a thread safe message queue, similar to this one:
http://www.codeproject.com/Articles/56369/Thread-safe-priority-queue-in-C
Your message pump worker thread will constantly check for new delegates, and invoke them.
So why not just write a message pump?
Well, by inheriting SynchronizationContext, you get all the CLR goodies like BackgroundWorker, AsyncOperationManager and the new await/async pattern keyword for free! They will all magically join back to your library thread.
Here is some code for a basic message pump. It does not implement SynchronizationContext:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace MessagePump
{
class Program
{
static void Main(string[] args)
{
MessagePump p = new MessagePump();
p.Start();
p.AddMessage(() => Console.WriteLine("message 1"));
p.AddMessage(() => Console.WriteLine("message 2"));
p.AddMessage(() => Console.WriteLine("message 3"));
Console.ReadLine();
p.Stop();
}
}
class MessagePump
{
bool m_Working = false;
Queue<Action> m_Actions = new Queue<Action>();
public void Start()
{
m_Working = true;
Thread t = new Thread(DoPump);
t.Name = "Message Pump Thread";
t.Start();
}
void DoPump()
{
while (m_Working)
{
try
{
Monitor.Enter(m_Actions);
while (m_Actions.Count > 0)
{
m_Actions.Dequeue()(); //dequeue and invoke a delegate
}
}
finally
{
Monitor.Exit(m_Actions);
}
Thread.Sleep(100); //dont want to lock this core!
}
}
public void Stop()
{
m_Working = false;
}
public void AddMessage(Action act)
{
lock (m_Actions)
{
m_Actions.Enqueue(act);
}
}
}
}
It is my first program for service.
If i run this code as Console, LOOP works, but if I convert it to service, it does the operation initially, but does not LOOP.
Could you help me correct it?
tnx
using System;
using System.Net;
using KICBservice;
using System.Data;
using ConsoleApplication1.Classes;
using System.IO;
using System.ServiceProcess;
using System.Configuration.Install;
using System.ComponentModel;
namespace KICBService
{
[RunInstaller(true)]
public class MyWindowsServiceInstaller : Installer
{
public MyWindowsServiceInstaller()
{
var processInstaller = new ServiceProcessInstaller();
var serviceInstaller = new ServiceInstaller();
//set the privileges
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = "KICB_Payment";
serviceInstaller.StartType = ServiceStartMode.Manual;
//must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = "KICB_Payment";
this.Installers.Add(processInstaller);
this.Installers.Add(serviceInstaller);
}
}
class Program : ServiceBase
{
static void Main(string[] args)
{
ServiceBase.Run(new Program());
KICBservice.Service1SoapClient kicb = new KICBservice.Service1SoapClient();
kicb.ClientCredentials.Windows.ClientCredential = new NetworkCredential("register", "KICBregistr1");
kicb.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
while (true)
{
try
{
kicb.Open();
StreamWriter tw = File.AppendText("c:\\KICB.log");
NewPayment np = new NewPayment();
np = kicb.GetPayment("register", "KICBregistr1");
// Operation with Database
tw.WriteLine("----------------");
tw.WriteLine(DateTime.Now);
tw.Close();
kicb.Close();
System.Threading.Thread.Sleep(60000);
}
catch (Exception ex)
{
kicb.Abort();
}
}
}
public Program()
{
this.ServiceName = "KICB_Payment";
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
}
protected override void OnStop()
{
base.OnStop();
//TODO: clean up any variables and stop any threads
}
}
}
I am pasting full code of my program.
Where is that first code located?
Without that context, my best guess is that your OnStart() method fires, and then the service quits as soon the method ends because there's nothing left to do.
Also, I'm not a fan of the while (true) { Sleep(60000); // do work } pattern for services. Instead, you want to look for a function that actually blocks execution to keep your code going. Examples include TcpListener.AcceptTcpClient() and Thread.Join(). If you can't find something like that for the meat of your service, you may want to do something like set up a scheduled task instead.
You've placed the code outside of a function. What you have shown in the question should not even compile, and it certainly won't loop.
Note the //TODO: comment in the OnStart function definition:
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
}
According to the Microsoft documentation, when an unhandled exception occurs on a thread (from either the thread pool or created using the System.Threading.Thread class) the AppDomain.UnhandledException event should fire for the default AppDomain of the application. Here is the MSDN link which explains it after the second NOTE section.
But I cannot reproduce this behaviour, as far as I can tell from my test application it never fires the UnhandledException on either the default AppDomain or the AppDomain used to create the thread. Is the documentation wrong or my testing code?
using System;
using System.Runtime.ExceptionServices;
using System.Reflection;
public class Program
{
static void Main()
{
Program.HookAppDomainExceptions();
Test t = CreateTestInsideAppDomain("Nested1");
t.SetupNested1();
Console.ReadLine();
}
public static Test CreateTestInsideAppDomain(string appDomainName)
{
AppDomain nested1 = AppDomain.CreateDomain(appDomainName);
string executingName = Assembly.GetExecutingAssembly().FullName;
return (Test)nested1.CreateInstanceAndUnwrap(executingName, "Test");
}
public static void HookAppDomainExceptions()
{
AppDomain.CurrentDomain.FirstChanceException +=
new EventHandler<FirstChanceExceptionEventArgs>(FirstChanceException);
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
public static void FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
{
Console.WriteLine("Domain:{0} FirstChanceException Handler",
AppDomain.CurrentDomain.FriendlyName);
}
public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("Domain:{0} UnhandledException Handler",
AppDomain.CurrentDomain.FriendlyName);
}
}
public class Test : MarshalByRefObject
{
private delegate void Nothing();
public void SetupNested1()
{
var start = new Nothing(Nested1ThreadStart);
start.BeginInvoke(null, null);
}
static void Nested1ThreadStart()
{
Program.HookAppDomainExceptions();
Test t = Program.CreateTestInsideAppDomain("Nested2");
t.SetupNested2();
}
public void SetupNested2()
{
Program.HookAppDomainExceptions();
Test t = Program.CreateTestInsideAppDomain("Nested3");
t.ThrowException();
}
public void ThrowException()
{
Program.HookAppDomainExceptions();
throw new ApplicationException("Raise Exception");
}
}
In your code UnhandledException isn't fired on any AppDomain, because if you call a delegate using BeginInvoke(), any exception that is thrown during its execution is handled and then rethrown when you call EndInvoke(), which you don't.
If you either call EndInvoke():
start.EndInvoke(start.BeginInvoke(null, null));
or execute the delegate synchronously:
start();
You get similar results: UnhandledException of the main domain is raised.
If instead, you do what the documentation says and start a new thread using the Thread class:
new Thread(Nested1ThreadStart).Start();
UnhandledException of Nested1 and the main app domain are raised.
So, to answer your question: The documentation is right. Your code is wrong. When you call delegate asynchronously using BeginInvoke(), you should always call EndInvoke() later.
I had this problem too. I used Observer Pattern to solve that.
you can implement an interface in your caller class that have a method which call from the other thread when an exception occurs.
Here's a link that shows how to implement this pattern Exploring the Observer Design Pattern
Let's asume the folowing bit of code, which allows you to call a class in a different AppDomain and handle almost any exception:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace MyAppDomain
{
class Program
{
static void Main(string[] args)
{
AppDomain myDomain = null;
try
{
myDomain = AppDomain.CreateDomain("Remote Domain");
myDomain.UnhandledException += new UnhandledExceptionEventHandler(myDomain_UnhandledException);
Worker remoteWorker = (Worker)myDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Worker).FullName);
remoteWorker.VeryBadMethod();
}
catch(Exception ex)
{
myDomain_UnhandledException(myDomain, new UnhandledExceptionEventArgs(ex, false));
}
finally
{
if (myDomain != null)
AppDomain.Unload(myDomain);
}
Console.ReadLine();
}
static void myDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
if (ex != null)
Console.WriteLine(ex.Message);
else
Console.WriteLine("A unknown exception was thrown");
}
}
public class Worker : MarshalByRefObject
{
public Worker()
{
}
public string DomainName
{
get
{
return AppDomain.CurrentDomain.FriendlyName;
}
}
public void VeryBadMethod()
{
// Autch!
throw new InvalidOperationException();
}
}
}
Now the problem is, allmost any exception can be handled, not every exception. A StackOverflowException for example will still crash the process. Is there a way to detect critical exceptions in different appdomains and handle these by unloading the AppDomain, but still allow other AppDomains to continue?
Unfortunately a StackOverflowException cannot be caught.
See: http://msdn.microsoft.com/en-us/library/system.stackoverflowexception.aspx
...
Starting with the .NET Framework
version 2.0, a StackOverflowException
object cannot be caught by a try-catch
block and the corresponding process is
terminated by default.
...
Update:
After further investigation in my old issues I found this old thread:
http://www.c-sharpcorner.com/Forums/ShowMessages.aspx?ThreadID=36073
Since .net framework 2.0, a StackOverflowException cannot be catched using a try-catch statement.
http://msdn.microsoft.com/en-us/library/system.stackoverflowexception.aspx