Merge pull request #10261 from barronpm/ihostlifetime

This commit is contained in:
Bond-009 2023-10-03 11:05:10 +02:00 committed by GitHub
commit 14b06ecbe2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 104 additions and 297 deletions

View File

@ -12,7 +12,6 @@ using System.Linq;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Dlna; using Emby.Dlna;
using Emby.Dlna.Main; using Emby.Dlna.Main;
@ -102,6 +101,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Prometheus.DotNetRuntime; using Prometheus.DotNetRuntime;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
@ -112,7 +112,7 @@ namespace Emby.Server.Implementations
/// <summary> /// <summary>
/// Class CompositionRoot. /// Class CompositionRoot.
/// </summary> /// </summary>
public abstract class ApplicationHost : IServerApplicationHost, IAsyncDisposable, IDisposable public abstract class ApplicationHost : IServerApplicationHost, IDisposable
{ {
/// <summary> /// <summary>
/// The disposable parts. /// The disposable parts.
@ -127,7 +127,6 @@ namespace Emby.Server.Implementations
private readonly IPluginManager _pluginManager; private readonly IPluginManager _pluginManager;
private List<Type> _creatingInstances; private List<Type> _creatingInstances;
private ISessionManager _sessionManager;
/// <summary> /// <summary>
/// Gets or sets all concrete types. /// Gets or sets all concrete types.
@ -172,6 +171,8 @@ namespace Emby.Server.Implementations
ConfigurationManager.Configuration, ConfigurationManager.Configuration,
ApplicationPaths.PluginsPath, ApplicationPaths.PluginsPath,
ApplicationVersion); ApplicationVersion);
_disposableParts.TryAdd((PluginManager)_pluginManager, byte.MinValue);
} }
/// <summary> /// <summary>
@ -204,6 +205,9 @@ namespace Emby.Server.Implementations
/// <inheritdoc /> /// <inheritdoc />
public bool IsShuttingDown { get; private set; } public bool IsShuttingDown { get; private set; }
/// <inheritdoc />
public bool ShouldRestart { get; private set; }
/// <summary> /// <summary>
/// Gets the logger. /// Gets the logger.
/// </summary> /// </summary>
@ -406,11 +410,9 @@ namespace Emby.Server.Implementations
/// <summary> /// <summary>
/// Runs the startup tasks. /// Runs the startup tasks.
/// </summary> /// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns> /// <returns><see cref="Task" />.</returns>
public async Task RunStartupTasksAsync(CancellationToken cancellationToken) public async Task RunStartupTasksAsync()
{ {
cancellationToken.ThrowIfCancellationRequested();
Logger.LogInformation("Running startup tasks"); Logger.LogInformation("Running startup tasks");
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false)); Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
@ -424,8 +426,6 @@ namespace Emby.Server.Implementations
var entryPoints = GetExports<IServerEntryPoint>(); var entryPoints = GetExports<IServerEntryPoint>();
cancellationToken.ThrowIfCancellationRequested();
var stopWatch = new Stopwatch(); var stopWatch = new Stopwatch();
stopWatch.Start(); stopWatch.Start();
@ -435,8 +435,6 @@ namespace Emby.Server.Implementations
Logger.LogInformation("Core startup complete"); Logger.LogInformation("Core startup complete");
CoreStartupHasCompleted = true; CoreStartupHasCompleted = true;
cancellationToken.ThrowIfCancellationRequested();
stopWatch.Restart(); stopWatch.Restart();
await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false);
@ -633,8 +631,6 @@ namespace Emby.Server.Implementations
var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>(); var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
await localizationManager.LoadAll().ConfigureAwait(false); await localizationManager.LoadAll().ConfigureAwait(false);
_sessionManager = Resolve<ISessionManager>();
SetStaticProperties(); SetStaticProperties();
FindParts(); FindParts();
@ -855,38 +851,24 @@ namespace Emby.Server.Implementations
} }
} }
/// <summary> /// <inheritdoc />
/// Restarts this instance.
/// </summary>
public void Restart() public void Restart()
{ {
if (IsShuttingDown) ShouldRestart = true;
{ Shutdown();
return;
} }
IsShuttingDown = true; /// <inheritdoc />
_pluginManager.UnloadAssemblies(); public void Shutdown()
{
Task.Run(async () => Task.Run(async () =>
{ {
try await Task.Delay(100).ConfigureAwait(false);
{ IsShuttingDown = true;
await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); Resolve<IHostApplicationLifetime>().StopApplication();
}
catch (Exception ex)
{
Logger.LogError(ex, "Error sending server restart notification");
}
Logger.LogInformation("Calling RestartInternal");
RestartInternal();
}); });
} }
protected abstract void RestartInternal();
/// <summary> /// <summary>
/// Gets the composable part assemblies. /// Gets the composable part assemblies.
/// </summary> /// </summary>
@ -1065,30 +1047,6 @@ namespace Emby.Server.Implementations
}.ToString().TrimEnd('/'); }.ToString().TrimEnd('/');
} }
/// <inheritdoc />
public async Task Shutdown()
{
if (IsShuttingDown)
{
return;
}
IsShuttingDown = true;
try
{
await _sessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.LogError(ex, "Error sending server shutdown notification");
}
ShutdownInternal();
}
protected abstract void ShutdownInternal();
public IEnumerable<Assembly> GetApiPluginAssemblies() public IEnumerable<Assembly> GetApiPluginAssemblies()
{ {
var assemblies = _allConcreteTypes var assemblies = _allConcreteTypes
@ -1152,52 +1110,5 @@ namespace Emby.Server.Implementations
_disposed = true; _disposed = true;
} }
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore().ConfigureAwait(false);
Dispose(false);
GC.SuppressFinalize(this);
}
/// <summary>
/// Used to perform asynchronous cleanup of managed resources or for cascading calls to <see cref="DisposeAsync"/>.
/// </summary>
/// <returns>A ValueTask.</returns>
protected virtual async ValueTask DisposeAsyncCore()
{
var type = GetType();
Logger.LogInformation("Disposing {Type}", type.Name);
foreach (var (part, _) in _disposableParts)
{
var partType = part.GetType();
if (partType == type)
{
continue;
}
Logger.LogInformation("Disposing {Type}", partType.Name);
try
{
part.Dispose();
}
catch (Exception ex)
{
Logger.LogError(ex, "Error disposing {Type}", partType.Name);
}
}
if (_sessionManager is not null)
{
// used for closing websockets
foreach (var session in _sessionManager.Sessions)
{
await session.DisposeAsync().ConfigureAwait(false);
}
}
}
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -11,7 +10,6 @@ using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Server.Implementations.Library; using Emby.Server.Implementations.Library;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json;
using Jellyfin.Extensions.Json.Converters; using Jellyfin.Extensions.Json.Converters;
using MediaBrowser.Common; using MediaBrowser.Common;
@ -30,7 +28,7 @@ namespace Emby.Server.Implementations.Plugins
/// <summary> /// <summary>
/// Defines the <see cref="PluginManager" />. /// Defines the <see cref="PluginManager" />.
/// </summary> /// </summary>
public class PluginManager : IPluginManager public sealed class PluginManager : IPluginManager, IDisposable
{ {
private const string MetafileName = "meta.json"; private const string MetafileName = "meta.json";
@ -191,15 +189,6 @@ namespace Emby.Server.Implementations.Plugins
} }
} }
/// <inheritdoc />
public void UnloadAssemblies()
{
foreach (var assemblyLoadContext in _assemblyLoadContexts)
{
assemblyLoadContext.Unload();
}
}
/// <summary> /// <summary>
/// Creates all the plugin instances. /// Creates all the plugin instances.
/// </summary> /// </summary>
@ -441,6 +430,15 @@ namespace Emby.Server.Implementations.Plugins
return SaveManifest(manifest, path); return SaveManifest(manifest, path);
} }
/// <inheritdoc />
public void Dispose()
{
foreach (var assemblyLoadContext in _assemblyLoadContexts)
{
assemblyLoadContext.Unload();
}
}
/// <summary> /// <summary>
/// Reconciles the manifest against any properties that exist locally in a pre-packaged meta.json found at the path. /// Reconciles the manifest against any properties that exist locally in a pre-packaged meta.json found at the path.
/// If no file is found, no reconciliation occurs. /// If no file is found, no reconciliation occurs.

View File

@ -36,6 +36,7 @@ using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using MediaBrowser.Model.SyncPlay; using MediaBrowser.Model.SyncPlay;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode; using Episode = MediaBrowser.Controller.Entities.TV.Episode;
@ -44,7 +45,7 @@ namespace Emby.Server.Implementations.Session
/// <summary> /// <summary>
/// Class SessionManager. /// Class SessionManager.
/// </summary> /// </summary>
public class SessionManager : ISessionManager, IDisposable public sealed class SessionManager : ISessionManager, IAsyncDisposable
{ {
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly ILogger<SessionManager> _logger; private readonly ILogger<SessionManager> _logger;
@ -57,11 +58,9 @@ namespace Emby.Server.Implementations.Session
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IDeviceManager _deviceManager; private readonly IDeviceManager _deviceManager;
private readonly CancellationTokenRegistration _shutdownCallback;
/// <summary> private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections
/// The active connections. = new(StringComparer.OrdinalIgnoreCase);
/// </summary>
private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections = new(StringComparer.OrdinalIgnoreCase);
private Timer _idleTimer; private Timer _idleTimer;
@ -79,7 +78,8 @@ namespace Emby.Server.Implementations.Session
IImageProcessor imageProcessor, IImageProcessor imageProcessor,
IServerApplicationHost appHost, IServerApplicationHost appHost,
IDeviceManager deviceManager, IDeviceManager deviceManager,
IMediaSourceManager mediaSourceManager) IMediaSourceManager mediaSourceManager,
IHostApplicationLifetime hostApplicationLifetime)
{ {
_logger = logger; _logger = logger;
_eventManager = eventManager; _eventManager = eventManager;
@ -92,6 +92,7 @@ namespace Emby.Server.Implementations.Session
_appHost = appHost; _appHost = appHost;
_deviceManager = deviceManager; _deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_shutdownCallback = hostApplicationLifetime.ApplicationStopping.Register(OnApplicationStopping);
_deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated; _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated;
} }
@ -151,36 +152,6 @@ namespace Emby.Server.Implementations.Session
} }
} }
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and optionally managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
_idleTimer?.Dispose();
}
_idleTimer = null;
_deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated;
_disposed = true;
}
private void CheckDisposed() private void CheckDisposed()
{ {
if (_disposed) if (_disposed)
@ -1330,32 +1301,6 @@ namespace Emby.Server.Implementations.Session
return SendMessageToSessions(Sessions, SessionMessageType.RestartRequired, string.Empty, cancellationToken); return SendMessageToSessions(Sessions, SessionMessageType.RestartRequired, string.Empty, cancellationToken);
} }
/// <summary>
/// Sends the server shutdown notification.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task SendServerShutdownNotification(CancellationToken cancellationToken)
{
CheckDisposed();
return SendMessageToSessions(Sessions, SessionMessageType.ServerShuttingDown, string.Empty, cancellationToken);
}
/// <summary>
/// Sends the server restart notification.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task SendServerRestartNotification(CancellationToken cancellationToken)
{
CheckDisposed();
_logger.LogDebug("Beginning SendServerRestartNotification");
return SendMessageToSessions(Sessions, SessionMessageType.ServerRestarting, string.Empty, cancellationToken);
}
/// <summary> /// <summary>
/// Adds the additional user. /// Adds the additional user.
/// </summary> /// </summary>
@ -1833,5 +1778,53 @@ namespace Emby.Server.Implementations.Session
return SendMessageToSessions(sessions, name, data, cancellationToken); return SendMessageToSessions(sessions, name, data, cancellationToken);
} }
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
if (_disposed)
{
return;
}
foreach (var session in _activeConnections.Values)
{
await session.DisposeAsync().ConfigureAwait(false);
}
if (_idleTimer is not null)
{
await _idleTimer.DisposeAsync().ConfigureAwait(false);
_idleTimer = null;
}
await _shutdownCallback.DisposeAsync().ConfigureAwait(false);
_deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated;
_disposed = true;
}
private async void OnApplicationStopping()
{
_logger.LogInformation("Sending shutdown notifications");
try
{
var messageType = _appHost.ShouldRestart ? SessionMessageType.ServerRestarting : SessionMessageType.ServerShuttingDown;
await SendMessageToSessions(Sessions, messageType, string.Empty, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending server shutdown notifications");
}
// Close open websockets to allow Kestrel to shut down cleanly
foreach (var session in _activeConnections.Values)
{
await session.DisposeAsync().ConfigureAwait(false);
}
_activeConnections.Clear();
}
} }
} }

View File

@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Mime; using System.Net.Mime;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes; using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@ -107,11 +106,7 @@ public class SystemController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status403Forbidden)]
public ActionResult RestartApplication() public ActionResult RestartApplication()
{ {
Task.Run(async () =>
{
await Task.Delay(100).ConfigureAwait(false);
_appHost.Restart(); _appHost.Restart();
});
return NoContent(); return NoContent();
} }
@ -127,11 +122,7 @@ public class SystemController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status403Forbidden)]
public ActionResult ShutdownApplication() public ActionResult ShutdownApplication()
{ {
Task.Run(async () => _appHost.Shutdown();
{
await Task.Delay(100).ConfigureAwait(false);
await _appHost.Shutdown().ConfigureAwait(false);
});
return NoContent(); return NoContent();
} }

View File

@ -102,9 +102,6 @@ namespace Jellyfin.Server
base.RegisterServices(serviceCollection); base.RegisterServices(serviceCollection);
} }
/// <inheritdoc />
protected override void RestartInternal() => Program.Restart();
/// <inheritdoc /> /// <inheritdoc />
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal() protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
{ {
@ -114,8 +111,5 @@ namespace Jellyfin.Server
// Jellyfin.Server.Implementations // Jellyfin.Server.Implementations
yield return typeof(JellyfinDbContext).Assembly; yield return typeof(JellyfinDbContext).Assembly;
} }
/// <inheritdoc />
protected override void ShutdownInternal() => Program.Shutdown();
} }
} }

View File

@ -4,7 +4,6 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommandLine; using CommandLine;
using Emby.Server.Implementations; using Emby.Server.Implementations;
@ -42,7 +41,6 @@ namespace Jellyfin.Server
public const string LoggingConfigFileSystem = "logging.json"; public const string LoggingConfigFileSystem = "logging.json";
private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory();
private static CancellationTokenSource _tokenSource = new();
private static long _startTimestamp; private static long _startTimestamp;
private static ILogger _logger = NullLogger.Instance; private static ILogger _logger = NullLogger.Instance;
private static bool _restartOnShutdown; private static bool _restartOnShutdown;
@ -65,36 +63,9 @@ namespace Jellyfin.Server
.MapResult(StartApp, ErrorParsingArguments); .MapResult(StartApp, ErrorParsingArguments);
} }
/// <summary>
/// Shuts down the application.
/// </summary>
internal static void Shutdown()
{
if (!_tokenSource.IsCancellationRequested)
{
_tokenSource.Cancel();
}
}
/// <summary>
/// Restarts the application.
/// </summary>
internal static void Restart()
{
_restartOnShutdown = true;
Shutdown();
}
private static async Task StartApp(StartupOptions options) private static async Task StartApp(StartupOptions options)
{ {
_startTimestamp = Stopwatch.GetTimestamp(); _startTimestamp = Stopwatch.GetTimestamp();
// Log all uncaught exceptions to std error
static void UnhandledExceptionToConsole(object sender, UnhandledExceptionEventArgs e) =>
Console.Error.WriteLine("Unhandled Exception\n" + e.ExceptionObject);
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionToConsole;
ServerApplicationPaths appPaths = StartupHelpers.CreateApplicationPaths(options); ServerApplicationPaths appPaths = StartupHelpers.CreateApplicationPaths(options);
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
@ -112,38 +83,10 @@ namespace Jellyfin.Server
StartupHelpers.InitializeLoggingFramework(startupConfig, appPaths); StartupHelpers.InitializeLoggingFramework(startupConfig, appPaths);
_logger = _loggerFactory.CreateLogger("Main"); _logger = _loggerFactory.CreateLogger("Main");
// Log uncaught exceptions to the logging instead of std error // Use the logging framework for uncaught exceptions instead of std error
AppDomain.CurrentDomain.UnhandledException -= UnhandledExceptionToConsole;
AppDomain.CurrentDomain.UnhandledException += (_, e) AppDomain.CurrentDomain.UnhandledException += (_, e)
=> _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");
// Intercept Ctrl+C and Ctrl+Break
Console.CancelKeyPress += (_, e) =>
{
if (_tokenSource.IsCancellationRequested)
{
return; // Already shutting down
}
e.Cancel = true;
_logger.LogInformation("Ctrl+C, shutting down");
Environment.ExitCode = 128 + 2;
Shutdown();
};
// Register a SIGTERM handler
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
{
if (_tokenSource.IsCancellationRequested)
{
return; // Already shutting down
}
_logger.LogInformation("Received a SIGTERM signal, shutting down");
Environment.ExitCode = 128 + 15;
Shutdown();
};
_logger.LogInformation( _logger.LogInformation(
"Jellyfin version: {Version}", "Jellyfin version: {Version}",
Assembly.GetEntryAssembly()!.GetName().Version!.ToString(3)); Assembly.GetEntryAssembly()!.GetName().Version!.ToString(3));
@ -173,12 +116,10 @@ namespace Jellyfin.Server
do do
{ {
_restartOnShutdown = false;
await StartServer(appPaths, options, startupConfig).ConfigureAwait(false); await StartServer(appPaths, options, startupConfig).ConfigureAwait(false);
if (_restartOnShutdown) if (_restartOnShutdown)
{ {
_tokenSource = new CancellationTokenSource();
_startTimestamp = Stopwatch.GetTimestamp(); _startTimestamp = Stopwatch.GetTimestamp();
} }
} while (_restartOnShutdown); } while (_restartOnShutdown);
@ -186,7 +127,7 @@ namespace Jellyfin.Server
private static async Task StartServer(IServerApplicationPaths appPaths, StartupOptions options, IConfiguration startupConfig) private static async Task StartServer(IServerApplicationPaths appPaths, StartupOptions options, IConfiguration startupConfig)
{ {
var appHost = new CoreAppHost( using var appHost = new CoreAppHost(
appPaths, appPaths,
_loggerFactory, _loggerFactory,
options, options,
@ -196,6 +137,7 @@ namespace Jellyfin.Server
try try
{ {
host = Host.CreateDefaultBuilder() host = Host.CreateDefaultBuilder()
.UseConsoleLifetime()
.ConfigureServices(services => appHost.Init(services)) .ConfigureServices(services => appHost.Init(services))
.ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.ConfigureWebHostBuilder(appHost, startupConfig, appPaths, _logger)) .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.ConfigureWebHostBuilder(appHost, startupConfig, appPaths, _logger))
.ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(options, appPaths, startupConfig)) .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(options, appPaths, startupConfig))
@ -210,7 +152,7 @@ namespace Jellyfin.Server
try try
{ {
await host.StartAsync(_tokenSource.Token).ConfigureAwait(false); await host.StartAsync().ConfigureAwait(false);
if (!OperatingSystem.IsWindows() && startupConfig.UseUnixSocket()) if (!OperatingSystem.IsWindows() && startupConfig.UseUnixSocket())
{ {
@ -219,22 +161,18 @@ namespace Jellyfin.Server
StartupHelpers.SetUnixSocketPermissions(startupConfig, socketPath, _logger); StartupHelpers.SetUnixSocketPermissions(startupConfig, socketPath, _logger);
} }
} }
catch (Exception ex) when (ex is not TaskCanceledException) catch (Exception)
{ {
_logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in network.xml and try again"); _logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in network.xml and try again");
throw; throw;
} }
await appHost.RunStartupTasksAsync(_tokenSource.Token).ConfigureAwait(false); await appHost.RunStartupTasksAsync().ConfigureAwait(false);
_logger.LogInformation("Startup complete {Time:g}", Stopwatch.GetElapsedTime(_startTimestamp)); _logger.LogInformation("Startup complete {Time:g}", Stopwatch.GetElapsedTime(_startTimestamp));
// Block main thread until shutdown await host.WaitForShutdownAsync().ConfigureAwait(false);
await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false); _restartOnShutdown = appHost.ShouldRestart;
}
catch (TaskCanceledException)
{
// Don't throw on cancellation
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -257,7 +195,6 @@ namespace Jellyfin.Server
} }
} }
await appHost.DisposeAsync().ConfigureAwait(false);
host?.Dispose(); host?.Dispose();
} }
} }

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common namespace MediaBrowser.Common
@ -47,6 +46,11 @@ namespace MediaBrowser.Common
/// <value><c>true</c> if this instance is shutting down; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is shutting down; otherwise, <c>false</c>.</value>
bool IsShuttingDown { get; } bool IsShuttingDown { get; }
/// <summary>
/// Gets a value indicating whether the application should restart.
/// </summary>
bool ShouldRestart { get; }
/// <summary> /// <summary>
/// Gets the application version. /// Gets the application version.
/// </summary> /// </summary>
@ -126,8 +130,7 @@ namespace MediaBrowser.Common
/// <summary> /// <summary>
/// Shuts down. /// Shuts down.
/// </summary> /// </summary>
/// <returns>A task.</returns> void Shutdown();
Task Shutdown();
/// <summary> /// <summary>
/// Initializes this instance. /// Initializes this instance.

View File

@ -29,11 +29,6 @@ namespace MediaBrowser.Common.Plugins
/// <returns>An IEnumerable{Assembly}.</returns> /// <returns>An IEnumerable{Assembly}.</returns>
IEnumerable<Assembly> LoadAssemblies(); IEnumerable<Assembly> LoadAssemblies();
/// <summary>
/// Unloads all of the assemblies.
/// </summary>
void UnloadAssemblies();
/// <summary> /// <summary>
/// Registers the plugin's services with the DI. /// Registers the plugin's services with the DI.
/// Note: DI is not yet instantiated yet. /// Note: DI is not yet instantiated yet.

View File

@ -232,20 +232,6 @@ namespace MediaBrowser.Controller.Session
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SendRestartRequiredNotification(CancellationToken cancellationToken); Task SendRestartRequiredNotification(CancellationToken cancellationToken);
/// <summary>
/// Sends the server shutdown notification.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendServerShutdownNotification(CancellationToken cancellationToken);
/// <summary>
/// Sends the server restart notification.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendServerRestartNotification(CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Adds the additional user. /// Adds the additional user.
/// </summary> /// </summary>

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Threading;
using Emby.Server.Implementations; using Emby.Server.Implementations;
using Jellyfin.Server.Extensions; using Jellyfin.Server.Extensions;
using Jellyfin.Server.Helpers; using Jellyfin.Server.Helpers;
@ -105,7 +104,7 @@ namespace Jellyfin.Server.Integration.Tests
var appHost = (TestAppHost)testServer.Services.GetRequiredService<IApplicationHost>(); var appHost = (TestAppHost)testServer.Services.GetRequiredService<IApplicationHost>();
appHost.ServiceProvider = testServer.Services; appHost.ServiceProvider = testServer.Services;
appHost.InitializeServices().GetAwaiter().GetResult(); appHost.InitializeServices().GetAwaiter().GetResult();
appHost.RunStartupTasksAsync(CancellationToken.None).GetAwaiter().GetResult(); appHost.RunStartupTasksAsync().GetAwaiter().GetResult();
return testServer; return testServer;
} }