//******************************************************************************************************
// ShutdownHandler.cs - Gbtc
//
// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://www.opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 11/03/2016 - Steven E. Chisholm
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
#if !SQLCLR
using System.ComponentModel;
#endif
using GSF.Collections;
using GSF.Diagnostics;
namespace GSF.Threading
{
///
/// The order in which the specified callback should occur when shutting down.
///
public enum ShutdownHandlerOrder
{
///
/// This queue is processed first. Unless there is a compelling reason to execute first, select the Default one.
///
First,
///
/// This shutdown order occurs after First, but before Last.
///
Default,
///
/// This queue is processed last. Items such as flushing application logs should go here.
///
Last
}
///
/// This class will monitor the state to the application and raise events when it detects that the application is about to shutdown.
///
public static class ShutdownHandler
{
private static readonly LogPublisher Log;
///
/// Gets if this process is shutting down.
///
public static bool IsShuttingDown { get; private set; }
///
/// Gets if this process has already shut down.
///
public static bool HasShutdown { get; private set; }
private static readonly List s_onShutdownCallbackFirst;
private static readonly List s_onShutdownCallbackDefault;
private static readonly List s_onShutdownCallbackLast;
private static readonly object s_syncRoot;
static ShutdownHandler()
{
s_syncRoot = new object();
s_onShutdownCallbackFirst = new List();
s_onShutdownCallbackDefault = new List();
s_onShutdownCallbackLast = new List();
Logger.Initialize();
Log = Logger.CreatePublisher(typeof(ShutdownHandler), MessageClass.Component);
if (AppDomain.CurrentDomain.IsDefaultAppDomain())
AppDomain.CurrentDomain.ProcessExit += InitiateSafeShutdown;
else
AppDomain.CurrentDomain.DomainUnload += InitiateSafeShutdown;
}
///
/// Initializes the shutdown handler. This is recommended to put in main loop of the program, but it is not critical.
///
public static void Initialize()
{
//This is handled through the static constructor.
}
///
/// Attempts Registers a callback that will be called
/// when the application is shutdown.
///
/// the callback when the shutdown occurs
/// the order that the callback will occur.
///
public static bool TryRegisterCallback(Action callback, ShutdownHandlerOrder shutdownOrder = ShutdownHandlerOrder.Default)
{
List list;
switch (shutdownOrder)
{
case ShutdownHandlerOrder.First:
list = s_onShutdownCallbackFirst;
break;
case ShutdownHandlerOrder.Default:
list = s_onShutdownCallbackDefault;
break;
case ShutdownHandlerOrder.Last:
list = s_onShutdownCallbackLast;
break;
default:
throw new ArgumentOutOfRangeException(nameof(shutdownOrder), shutdownOrder, null);
}
if (IsShuttingDown)
return false;
lock (s_syncRoot)
{
if (IsShuttingDown)
return false;
list.RemoveWhere(x => !x.IsAlive);
list.Add(new WeakAction(callback));
return true;
}
}
private static void InitiateSafeShutdown(object sender, EventArgs e)
{
List shutdownList = new List();
lock (s_syncRoot)
{
if (IsShuttingDown)
return;
IsShuttingDown = true;
shutdownList.AddRange(s_onShutdownCallbackFirst);
shutdownList.AddRange(s_onShutdownCallbackDefault);
shutdownList.AddRange(s_onShutdownCallbackLast);
}
Log.Publish(MessageLevel.Info, MessageFlags.SystemHealth, "Shutting Down", $"Sending shutdown notification to {shutdownList.Count} objects");
foreach (WeakAction weakAction in shutdownList)
{
try
{
weakAction.TryInvoke();
}
catch (Exception ex)
{
Log.Publish(MessageLevel.Warning, "Application Shutdown Generated an Error", null, null, ex);
}
}
Logger.Shutdown();
HasShutdown = true;
}
///
/// Requests that certain components initiate a safe shutdown.
///
///
/// This method should only be called when the main thread exits. Calling this outside
/// of the application exiting could result in unpredictable behavior.
///
#if !SQLCLR
[EditorBrowsable(EditorBrowsableState.Advanced)]
#endif
public static void InitiateSafeShutdown()
{
InitiateSafeShutdown(null, null);
}
}
}