public void RunAndBlock(CancellationToken cancellationToken = default(CancellationToken))
{
// Start the host and restart it if requested. Host Restarts will happen when
// host level configuration files change
do
{
ScriptHost newInstance = null;
try
{
// if we were in an error state retain that,
// otherwise move to default
if (State != ScriptHostState.Error)
{
State = ScriptHostState.Default;
}
// Create a new host config, but keep the host id from existing one
_config.HostConfig = new JobHostConfiguration
{
HostId = _config.HostConfig.HostId
};
OnInitializeConfig(_config);
newInstance = _scriptHostFactory.Create(_settingsManager, _config);
_traceWriter = newInstance.TraceWriter;
_currentInstance = newInstance;
lock (_liveInstances)
{
_liveInstances.Add(newInstance);
}
OnHostCreated();
if (_traceWriter != null)
{
string message = string.Format("Starting Host (HostId={0}, Version={1}, ProcessId={2}, Debug={3})",
newInstance.ScriptConfig.HostConfig.HostId, ScriptHost.Version, Process.GetCurrentProcess().Id, newInstance.InDebugMode.ToString());
_traceWriter.Info(message);
}
newInstance.StartAsync(cancellationToken).GetAwaiter().GetResult();
// log any function initialization errors
LogErrors(newInstance);
OnHostStarted();
// only after ALL initialization is complete do we set the
// state to Running
State = ScriptHostState.Running;
LastError = null;
// Wait for a restart signal. This event will automatically reset.
// While we're restarting, it is possible for another restart to be
// signaled. That is fine - the restart will be processed immediately
// once we get to this line again. The important thing is that these
// restarts are only happening on a single thread.
WaitHandle.WaitAny(new WaitHandle[]
{
cancellationToken.WaitHandle,
newInstance.RestartEvent,
_stopEvent
});
// Orphan the current host instance. We're stopping it, so it won't listen for any new functions
// it will finish any currently executing functions and then clean itself up.
// Spin around and create a new host instance.
Task taskIgnore = Orphan(newInstance);
}
catch (Exception ex)
{
State = ScriptHostState.Error;
LastError = ex;
// We need to keep the host running, so we catch and log any errors
// then restart the host
if (_traceWriter != null)
{
_traceWriter.Error("A ScriptHost error has occurred", ex);
}
// If a ScriptHost instance was created before the exception was thrown
// Orphan and cleanup that instance.
if (newInstance != null)
{
Orphan(newInstance, forceStop: true)
.ContinueWith(t =>
{
if (t.IsFaulted)
{
t.Exception.Handle(e => true);
}
}, TaskContinuationOptions.ExecuteSynchronously);
}
// Wait for a short period of time before restarting to
// avoid cases where a host level config error might cause
// a rapid restart cycle
Task.Delay(_config.RestartInterval).GetAwaiter().GetResult();
}
}
while (!_stopped && !cancellationToken.IsCancellationRequested);
}