Skip to content
This repository has been archived by the owner on Nov 19, 2022. It is now read-only.

Commit

Permalink
[xsp] Improved single app mode shutdown sequence.
Browse files Browse the repository at this point in the history
When XSP/mod-mono-server are operating in the single app mode, the application
runs in the same AppDomain what the Server instance. This requires special attention
to handling of the AppDomain's unload. This commit adds code which waits for the
AppDomain unload process to complete before recreating the domain. Without this code
it may happen that the main listening socket will not be destroyed in the "old" domain
before the "new" domain attempts to bind its own listening socket to the same port (which
leads to bind failure and exit from the process). The code is not very elegant but it is
portable.
  • Loading branch information
grendello committed Oct 25, 2010
1 parent fcf139c commit 9455071
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 5 deletions.
32 changes: 31 additions & 1 deletion src/Mono.WebServer.Apache/main.cs
Expand Up @@ -80,6 +80,9 @@ public ApplicationSettings ()
}
}

static bool singleAppUnloading;
static AppDomain singleAppDomain;

static void ShowVersion ()
{
Assembly assembly = Assembly.GetExecutingAssembly ();
Expand Down Expand Up @@ -219,6 +222,10 @@ public static void CurrentDomain_UnhandledException (object sender, UnhandledExc
Console.Error.WriteLine (ex);
}

void ApplicationDomainUnloading (object sender, EventArgs args)
{
singleAppUnloading = true;
}

public static int Main (string [] args)
{
Expand All @@ -228,10 +235,31 @@ public static int Main (string [] args)
try {
return new Server ().RealMain (args, true, null, quiet);
} catch (ThreadAbortException) {
Console.WriteLine ("Single application mode and ASP.NET AppDomain has unloaded.");
// Single-app mode and ASP.NET appdomain unloaded
Thread.ResetAbort ();
quiet = true; // hush 'RealMain'
}
if (singleAppUnloading && singleAppDomain != null) {
Console.WriteLine ("Waiting for the AppDomain unload to finish.");
while (true) {
try {
if (!singleAppDomain.IsFinalizingForUnload ())
break;
Thread.Sleep (500);
} catch (AppDomainUnloadedException) {
Console.WriteLine ("AppDomain unloaded.");
break;
} catch (ThreadAbortException) {
Thread.ResetAbort ();
} catch (Exception ex) {
Console.WriteLine ("Unload exception: {0}", ex);
break;
}
}
singleAppDomain = null;
singleAppUnloading = false;
}
}
}

Expand Down Expand Up @@ -414,7 +442,9 @@ public int RealMain (string [] args, bool root, IApplicationHost ext_apphost, bo
if (root && vh != null) {
// Redo in new domain
vh.CreateHost (server, webSource);
Server svr = (Server) vh.AppHost.Domain.CreateInstanceAndUnwrap (GetType ().Assembly.GetName ().ToString (), GetType ().FullName);
singleAppDomain = vh.AppHost.Domain;
singleAppDomain.DomainUnload += ApplicationDomainUnloading;
Server svr = (Server) singleAppDomain.CreateInstanceAndUnwrap (GetType ().Assembly.GetName ().ToString (), GetType ().FullName);
webSource.Dispose ();
return svr.RealMain (args, false, vh.AppHost, quiet);
}
Expand Down
36 changes: 32 additions & 4 deletions src/Mono.WebServer.XSP/main.cs
Expand Up @@ -77,6 +77,8 @@ public ApplicationSettings ()
}

static RSA key;
static bool singleAppUnloading;
static AppDomain singleAppDomain;

static void ShowVersion ()
{
Expand Down Expand Up @@ -249,19 +251,44 @@ public static void CurrentDomain_UnhandledException (object sender, UnhandledExc
Console.WriteLine (ex);
}

void ApplicationDomainUnloading (object sender, EventArgs args)
{
singleAppUnloading = true;
}

public static int Main (string [] args)
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler (CurrentDomain_UnhandledException);
bool quiet = false;
while (true) {
try {
return new Server ().RealMain (args, true, null, quiet);
} catch (ThreadAbortException ex) {
Console.WriteLine (ex);
} catch (ThreadAbortException) {
Console.WriteLine ("Single application mode and ASP.NET AppDomain is unloading.");
// Single-app mode and ASP.NET appdomain unloaded
Thread.ResetAbort ();
quiet = true; // hush 'RealMain'
}
if (singleAppUnloading && singleAppDomain != null) {
Console.WriteLine ("\twaiting for the unload to finish...");
while (true) {
try {
if (!singleAppDomain.IsFinalizingForUnload ())
break;
Thread.Sleep (500);
} catch (AppDomainUnloadedException) {
Console.WriteLine ("\tdone...");
break;
} catch (ThreadAbortException) {
Thread.ResetAbort ();
} catch (Exception ex) {
Console.WriteLine ("\tunload exception: {0} ({1})", ex.GetType (), ex.Message);
break;
}
}
singleAppDomain = null;
singleAppUnloading = false;
}
}
}

Expand Down Expand Up @@ -446,12 +473,13 @@ public int RealMain (string [] args, bool root, IApplicationHost ext_apphost, bo
if (settings.Apps == null && settings.AppConfigDir == null && settings.AppConfigFile == null)
server.AddApplicationsFromCommandLine ("/:.");


VPathToHost vh = server.GetSingleApp ();
if (root && vh != null) {
// Redo in new domain
vh.CreateHost (server, webSource);
Server svr = (Server) vh.AppHost.Domain.CreateInstanceAndUnwrap (GetType ().Assembly.GetName ().ToString (), GetType ().FullName);
singleAppDomain = vh.AppHost.Domain;
singleAppDomain.DomainUnload += ApplicationDomainUnloading;
Server svr = (Server) singleAppDomain.CreateInstanceAndUnwrap (GetType ().Assembly.GetName ().ToString (), GetType ().FullName);
webSource.Dispose ();
return svr.RealMain (args, false, vh.AppHost, quiet);
}
Expand Down

0 comments on commit 9455071

Please sign in to comment.