فهرست منبع

Use IHostLifetime to handle restarting and shutting down

Patrick Barron 1 سال پیش
والد
کامیت
493de3297a

+ 8 - 116
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;
@@ -112,7 +111,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 +126,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 +170,8 @@ namespace Emby.Server.Implementations
                 ConfigurationManager.Configuration,
                 ApplicationPaths.PluginsPath,
                 ApplicationVersion);
+
+            _disposableParts.TryAdd((PluginManager)_pluginManager, byte.MinValue);
         }
 
         /// <summary>
@@ -202,7 +202,10 @@ namespace Emby.Server.Implementations
         public bool HasPendingRestart { get; private set; }
 
         /// <inheritdoc />
-        public bool IsShuttingDown { get; private set; }
+        public bool IsShuttingDown { get; set; }
+
+        /// <inheritdoc />
+        public bool ShouldRestart { get; set; }
 
         /// <summary>
         /// Gets the logger.
@@ -406,11 +409,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 +425,6 @@ namespace Emby.Server.Implementations
 
             var entryPoints = GetExports<IServerEntryPoint>();
 
-            cancellationToken.ThrowIfCancellationRequested();
-
             var stopWatch = new Stopwatch();
             stopWatch.Start();
 
@@ -435,8 +434,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 +630,6 @@ namespace Emby.Server.Implementations
             var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
             await localizationManager.LoadAll().ConfigureAwait(false);
 
-            _sessionManager = Resolve<ISessionManager>();
-
             SetStaticProperties();
 
             FindParts();
@@ -855,38 +850,6 @@ namespace Emby.Server.Implementations
             }
         }
 
-        /// <summary>
-        /// Restarts this instance.
-        /// </summary>
-        public void Restart()
-        {
-            if (IsShuttingDown)
-            {
-                return;
-            }
-
-            IsShuttingDown = true;
-            _pluginManager.UnloadAssemblies();
-
-            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();
-            });
-        }
-
-        protected abstract void RestartInternal();
-
         /// <summary>
         /// Gets the composable part assemblies.
         /// </summary>
@@ -1065,30 +1028,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 +1091,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);
-                }
-            }
-        }
     }
 }

+ 10 - 12
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.

+ 54 - 63
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)
@@ -1330,32 +1301,6 @@ namespace Emby.Server.Implementations.Session
             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>
         /// Adds the additional user.
         /// </summary>
@@ -1833,5 +1778,51 @@ 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);
+            }
+        }
     }
 }

+ 11 - 3
Jellyfin.Api/Controllers/SystemController.cs

@@ -18,6 +18,7 @@ using MediaBrowser.Model.System;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.Logging;
 
 namespace Jellyfin.Api.Controllers;
@@ -32,6 +33,7 @@ public class SystemController : BaseJellyfinApiController
     private readonly IFileSystem _fileSystem;
     private readonly INetworkManager _network;
     private readonly ILogger<SystemController> _logger;
+    private readonly IHostApplicationLifetime _hostApplicationLifetime;
 
     /// <summary>
     /// Initializes a new instance of the <see cref="SystemController"/> class.
@@ -41,18 +43,21 @@ public class SystemController : BaseJellyfinApiController
     /// <param name="fileSystem">Instance of <see cref="IFileSystem"/> interface.</param>
     /// <param name="network">Instance of <see cref="INetworkManager"/> interface.</param>
     /// <param name="logger">Instance of <see cref="ILogger{SystemController}"/> interface.</param>
+    /// <param name="hostApplicationLifetime">Instance of <see cref="IHostApplicationLifetime"/> interface.</param>
     public SystemController(
         IServerConfigurationManager serverConfigurationManager,
         IServerApplicationHost appHost,
         IFileSystem fileSystem,
         INetworkManager network,
-        ILogger<SystemController> logger)
+        ILogger<SystemController> logger,
+        IHostApplicationLifetime hostApplicationLifetime)
     {
         _appPaths = serverConfigurationManager.ApplicationPaths;
         _appHost = appHost;
         _fileSystem = fileSystem;
         _network = network;
         _logger = logger;
+        _hostApplicationLifetime = hostApplicationLifetime;
     }
 
     /// <summary>
@@ -110,7 +115,9 @@ public class SystemController : BaseJellyfinApiController
         Task.Run(async () =>
         {
             await Task.Delay(100).ConfigureAwait(false);
-            _appHost.Restart();
+            _appHost.ShouldRestart = true;
+            _appHost.IsShuttingDown = true;
+            _hostApplicationLifetime.StopApplication();
         });
         return NoContent();
     }
@@ -130,7 +137,8 @@ public class SystemController : BaseJellyfinApiController
         Task.Run(async () =>
         {
             await Task.Delay(100).ConfigureAwait(false);
-            await _appHost.Shutdown().ConfigureAwait(false);
+            _appHost.IsShuttingDown = true;
+            _hostApplicationLifetime.StopApplication();
         });
         return NoContent();
     }

+ 0 - 6
Jellyfin.Server/CoreAppHost.cs

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

+ 7 - 63
Jellyfin.Server/Program.cs

@@ -4,7 +4,6 @@ using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Reflection;
-using System.Threading;
 using System.Threading.Tasks;
 using CommandLine;
 using Emby.Server.Implementations;
@@ -42,7 +41,6 @@ namespace Jellyfin.Server
         public const string LoggingConfigFileSystem = "logging.json";
 
         private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory();
-        private static CancellationTokenSource _tokenSource = new();
         private static long _startTimestamp;
         private static ILogger _logger = NullLogger.Instance;
         private static bool _restartOnShutdown;
@@ -65,27 +63,6 @@ namespace Jellyfin.Server
                 .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)
         {
             _startTimestamp = Stopwatch.GetTimestamp();
@@ -110,33 +87,6 @@ namespace Jellyfin.Server
             AppDomain.CurrentDomain.UnhandledException += (_, e)
                 => _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(
                 "Jellyfin version: {Version}",
                 Assembly.GetEntryAssembly()!.GetName().Version!.ToString(3));
@@ -166,12 +116,10 @@ namespace Jellyfin.Server
 
             do
             {
-                _restartOnShutdown = false;
                 await StartServer(appPaths, options, startupConfig).ConfigureAwait(false);
 
                 if (_restartOnShutdown)
                 {
-                    _tokenSource = new CancellationTokenSource();
                     _startTimestamp = Stopwatch.GetTimestamp();
                 }
             } while (_restartOnShutdown);
@@ -179,7 +127,7 @@ namespace Jellyfin.Server
 
         private static async Task StartServer(IServerApplicationPaths appPaths, StartupOptions options, IConfiguration startupConfig)
         {
-            var appHost = new CoreAppHost(
+            using var appHost = new CoreAppHost(
                 appPaths,
                 _loggerFactory,
                 options,
@@ -189,6 +137,7 @@ namespace Jellyfin.Server
             try
             {
                 host = Host.CreateDefaultBuilder()
+                    .UseConsoleLifetime()
                     .ConfigureServices(services => appHost.Init(services))
                     .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.ConfigureWebHostBuilder(appHost, startupConfig, appPaths, _logger))
                     .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(options, appPaths, startupConfig))
@@ -203,7 +152,7 @@ namespace Jellyfin.Server
 
                 try
                 {
-                    await host.StartAsync(_tokenSource.Token).ConfigureAwait(false);
+                    await host.StartAsync().ConfigureAwait(false);
 
                     if (!OperatingSystem.IsWindows() && startupConfig.UseUnixSocket())
                     {
@@ -212,22 +161,18 @@ namespace Jellyfin.Server
                         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");
                     throw;
                 }
 
-                await appHost.RunStartupTasksAsync(_tokenSource.Token).ConfigureAwait(false);
+                await appHost.RunStartupTasksAsync().ConfigureAwait(false);
 
                 _logger.LogInformation("Startup complete {Time:g}", Stopwatch.GetElapsedTime(_startTimestamp));
 
-                // Block main thread until shutdown
-                await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false);
-            }
-            catch (TaskCanceledException)
-            {
-                // Don't throw on cancellation
+                await host.WaitForShutdownAsync().ConfigureAwait(false);
+                _restartOnShutdown = appHost.ShouldRestart;
             }
             catch (Exception ex)
             {
@@ -250,7 +195,6 @@ namespace Jellyfin.Server
                     }
                 }
 
-                await appHost.DisposeAsync().ConfigureAwait(false);
                 host?.Dispose();
             }
         }

+ 7 - 14
MediaBrowser.Common/IApplicationHost.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Collections.Generic;
 using System.Reflection;
-using System.Threading.Tasks;
 using Microsoft.Extensions.DependencyInjection;
 
 namespace MediaBrowser.Common
@@ -42,10 +41,15 @@ namespace MediaBrowser.Common
         bool HasPendingRestart { get; }
 
         /// <summary>
-        /// Gets a value indicating whether this instance is currently shutting down.
+        /// Gets or sets a value indicating whether this instance is currently shutting down.
         /// </summary>
         /// <value><c>true</c> if this instance is shutting down; otherwise, <c>false</c>.</value>
-        bool IsShuttingDown { get; }
+        bool IsShuttingDown { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether the application should restart.
+        /// </summary>
+        bool ShouldRestart { get; set; }
 
         /// <summary>
         /// Gets the application version.
@@ -87,11 +91,6 @@ namespace MediaBrowser.Common
         /// </summary>
         void NotifyPendingRestart();
 
-        /// <summary>
-        /// Restarts this instance.
-        /// </summary>
-        void Restart();
-
         /// <summary>
         /// Gets the exports.
         /// </summary>
@@ -123,12 +122,6 @@ namespace MediaBrowser.Common
         /// <returns>``0.</returns>
         T Resolve<T>();
 
-        /// <summary>
-        /// Shuts down.
-        /// </summary>
-        /// <returns>A task.</returns>
-        Task Shutdown();
-
         /// <summary>
         /// Initializes this instance.
         /// </summary>

+ 0 - 5
MediaBrowser.Common/Plugins/IPluginManager.cs

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

+ 0 - 14
MediaBrowser.Controller/Session/ISessionManager.cs

@@ -232,20 +232,6 @@ namespace MediaBrowser.Controller.Session
         /// <returns>Task.</returns>
         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>
         /// Adds the additional user.
         /// </summary>

+ 1 - 2
tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs

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