diff options
| author | Bond-009 <bond.009@outlook.com> | 2023-10-03 11:05:10 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-03 11:05:10 +0200 |
| commit | 14b06ecbe29f6b9120048ea6d0b993778921a6a6 (patch) | |
| tree | bfbca697dbbeac2a02ca581c25d2affded681653 /Emby.Server.Implementations | |
| parent | 1514b5d0523928b83e802d0e77ab074528b432e3 (diff) | |
| parent | ab0790271aac8846430c9689dff647eb4ce55078 (diff) | |
Merge pull request #10261 from barronpm/ihostlifetime
Diffstat (limited to 'Emby.Server.Implementations')
| -rw-r--r-- | Emby.Server.Implementations/ApplicationHost.cs | 125 | ||||
| -rw-r--r-- | Emby.Server.Implementations/Plugins/PluginManager.cs | 22 | ||||
| -rw-r--r-- | Emby.Server.Implementations/Session/SessionManager.cs | 119 |
3 files changed, 84 insertions, 182 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f8208af70..985ab0db5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -12,7 +12,6 @@ using System.Linq; using System.Net; using System.Reflection; using System.Security.Cryptography.X509Certificates; -using System.Threading; using System.Threading.Tasks; using Emby.Dlna; using Emby.Dlna.Main; @@ -102,6 +101,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; @@ -112,7 +112,7 @@ namespace Emby.Server.Implementations /// <summary> /// Class CompositionRoot. /// </summary> - public abstract class ApplicationHost : IServerApplicationHost, IAsyncDisposable, IDisposable + public abstract class ApplicationHost : IServerApplicationHost, IDisposable { /// <summary> /// The disposable parts. @@ -127,7 +127,6 @@ namespace Emby.Server.Implementations private readonly IPluginManager _pluginManager; private List<Type> _creatingInstances; - private ISessionManager _sessionManager; /// <summary> /// Gets or sets all concrete types. @@ -172,6 +171,8 @@ namespace Emby.Server.Implementations ConfigurationManager.Configuration, ApplicationPaths.PluginsPath, ApplicationVersion); + + _disposableParts.TryAdd((PluginManager)_pluginManager, byte.MinValue); } /// <summary> @@ -204,6 +205,9 @@ namespace Emby.Server.Implementations /// <inheritdoc /> public bool IsShuttingDown { get; private set; } + /// <inheritdoc /> + public bool ShouldRestart { get; private set; } + /// <summary> /// Gets the logger. /// </summary> @@ -406,11 +410,9 @@ namespace Emby.Server.Implementations /// <summary> /// Runs the startup tasks. /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> /// <returns><see cref="Task" />.</returns> - public async Task RunStartupTasksAsync(CancellationToken cancellationToken) + public async Task RunStartupTasksAsync() { - cancellationToken.ThrowIfCancellationRequested(); Logger.LogInformation("Running startup tasks"); Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false)); @@ -424,8 +426,6 @@ namespace Emby.Server.Implementations var entryPoints = GetExports<IServerEntryPoint>(); - cancellationToken.ThrowIfCancellationRequested(); - var stopWatch = new Stopwatch(); stopWatch.Start(); @@ -435,8 +435,6 @@ namespace Emby.Server.Implementations Logger.LogInformation("Core startup complete"); CoreStartupHasCompleted = true; - cancellationToken.ThrowIfCancellationRequested(); - stopWatch.Restart(); await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); @@ -633,8 +631,6 @@ namespace Emby.Server.Implementations var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>(); await localizationManager.LoadAll().ConfigureAwait(false); - _sessionManager = Resolve<ISessionManager>(); - SetStaticProperties(); FindParts(); @@ -855,38 +851,24 @@ namespace Emby.Server.Implementations } } - /// <summary> - /// Restarts this instance. - /// </summary> + /// <inheritdoc /> public void Restart() { - if (IsShuttingDown) - { - return; - } - - IsShuttingDown = true; - _pluginManager.UnloadAssemblies(); + ShouldRestart = true; + Shutdown(); + } + /// <inheritdoc /> + public void Shutdown() + { Task.Run(async () => { - try - { - await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error sending server restart notification"); - } - - Logger.LogInformation("Calling RestartInternal"); - - RestartInternal(); + await Task.Delay(100).ConfigureAwait(false); + IsShuttingDown = true; + Resolve<IHostApplicationLifetime>().StopApplication(); }); } - protected abstract void RestartInternal(); - /// <summary> /// Gets the composable part assemblies. /// </summary> @@ -1065,30 +1047,6 @@ namespace Emby.Server.Implementations }.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() { var assemblies = _allConcreteTypes @@ -1152,52 +1110,5 @@ namespace Emby.Server.Implementations _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); - } - } - } } } diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 1303012e1..d7189ef0c 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Data; using System.Globalization; using System.IO; using System.Linq; @@ -11,7 +10,6 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; using Emby.Server.Implementations.Library; -using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common; @@ -30,7 +28,7 @@ namespace Emby.Server.Implementations.Plugins /// <summary> /// Defines the <see cref="PluginManager" />. /// </summary> - public class PluginManager : IPluginManager + public sealed class PluginManager : IPluginManager, IDisposable { 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> /// Creates all the plugin instances. /// </summary> @@ -441,6 +430,15 @@ namespace Emby.Server.Implementations.Plugins return SaveManifest(manifest, path); } + /// <inheritdoc /> + public void Dispose() + { + foreach (var assemblyLoadContext in _assemblyLoadContexts) + { + assemblyLoadContext.Unload(); + } + } + /// <summary> /// 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. diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index b4a622ccf..50d3e8e46 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -36,6 +36,7 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using MediaBrowser.Model.SyncPlay; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Episode = MediaBrowser.Controller.Entities.TV.Episode; @@ -44,7 +45,7 @@ namespace Emby.Server.Implementations.Session /// <summary> /// Class SessionManager. /// </summary> - public class SessionManager : ISessionManager, IDisposable + public sealed class SessionManager : ISessionManager, IAsyncDisposable { private readonly IUserDataManager _userDataManager; private readonly ILogger<SessionManager> _logger; @@ -57,11 +58,9 @@ namespace Emby.Server.Implementations.Session private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerApplicationHost _appHost; private readonly IDeviceManager _deviceManager; - - /// <summary> - /// The active connections. - /// </summary> - private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections = new(StringComparer.OrdinalIgnoreCase); + private readonly CancellationTokenRegistration _shutdownCallback; + private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections + = new(StringComparer.OrdinalIgnoreCase); private Timer _idleTimer; @@ -79,7 +78,8 @@ namespace Emby.Server.Implementations.Session IImageProcessor imageProcessor, IServerApplicationHost appHost, IDeviceManager deviceManager, - IMediaSourceManager mediaSourceManager) + IMediaSourceManager mediaSourceManager, + IHostApplicationLifetime hostApplicationLifetime) { _logger = logger; _eventManager = eventManager; @@ -92,6 +92,7 @@ namespace Emby.Server.Implementations.Session _appHost = appHost; _deviceManager = deviceManager; _mediaSourceManager = mediaSourceManager; + _shutdownCallback = hostApplicationLifetime.ApplicationStopping.Register(OnApplicationStopping); _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() { if (_disposed) @@ -1331,32 +1302,6 @@ namespace Emby.Server.Implementations.Session } /// <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> /// Adds the additional user. /// </summary> /// <param name="sessionId">The session identifier.</param> @@ -1833,5 +1778,53 @@ namespace Emby.Server.Implementations.Session 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(); + } } } |
