diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 2f3cc1f1bf..958a266b8f 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -474,7 +474,8 @@ namespace Tgstation.Server.Host.Components await repo.ResetToSha(startSha, progressReporter, CancellationToken.None); throw; } - }); + }) + .AsTask(); #pragma warning restore CA1502 // Cyclomatic complexity /// diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index b82bec8f03..46cd7574d4 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -258,7 +258,7 @@ namespace Tgstation.Server.Host.Components logger.LogDebug("Reverting instance {instanceId}'s path to {oldPath} in the DB...", instance.Id, oldPath); // DCT: Operation must always run - await databaseContextFactory.UseContext(db => + await databaseContextFactory.UseContext2(db => { var targetInstance = new Models.Instance { @@ -544,8 +544,9 @@ namespace Tgstation.Server.Host.Components await InitializeSwarm(cancellationToken); List dbInstances = null; - var instanceEnumeration = databaseContextFactory.UseContext( - async databaseContext => dbInstances = await databaseContext + + async ValueTask EnumerateInstances(IDatabaseContext databaseContext) + => dbInstances = await databaseContext .Instances .AsQueryable() .Where(x => x.Online.Value && x.SwarmIdentifer == swarmConfiguration.Identifier) @@ -553,12 +554,14 @@ namespace Tgstation.Server.Host.Components .Include(x => x.ChatSettings) .ThenInclude(x => x.Channels) .Include(x => x.DreamDaemonSettings) - .ToListAsync(cancellationToken)); + .ToListAsync(cancellationToken); + + var instanceEnumeration = databaseContextFactory.UseContext(EnumerateInstances); var factoryStartup = instanceFactory.StartAsync(cancellationToken); var jobManagerStartup = jobService.StartAsync(cancellationToken); - await Task.WhenAll(instanceEnumeration, factoryStartup, jobManagerStartup); + await Task.WhenAll(instanceEnumeration.AsTask(), factoryStartup, jobManagerStartup); var instanceOnliningTasks = dbInstances.Select( async metadata => diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryUpdateService.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryUpdateService.cs index 53de9c45c3..be19c97785 100644 --- a/src/Tgstation.Server.Host/Components/Repository/RepositoryUpdateService.cs +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryUpdateService.cs @@ -192,7 +192,7 @@ namespace Tgstation.Server.Host.Components.Repository Id = instanceId, }; - Task CallLoadRevInfo(Models.TestMerge testMergeToAdd = null, string lastOriginCommitSha = null) => databaseContextFactory + ValueTask CallLoadRevInfo(Models.TestMerge testMergeToAdd = null, string lastOriginCommitSha = null) => databaseContextFactory .UseContext( async databaseContext => { @@ -243,7 +243,7 @@ namespace Tgstation.Server.Host.Components.Repository await CallLoadRevInfo(); // apply new rev info, tracking applied test merges - Task UpdateRevInfo(Models.TestMerge testMergeToAdd = null) => CallLoadRevInfo(testMergeToAdd, lastRevisionInfo.OriginCommitSha); + ValueTask UpdateRevInfo(Models.TestMerge testMergeToAdd = null) => CallLoadRevInfo(testMergeToAdd, lastRevisionInfo.OriginCommitSha); try { diff --git a/src/Tgstation.Server.Host/Components/Session/ISessionPersistor.cs b/src/Tgstation.Server.Host/Components/Session/ISessionPersistor.cs index 3028e6daf4..97b9065b47 100644 --- a/src/Tgstation.Server.Host/Components/Session/ISessionPersistor.cs +++ b/src/Tgstation.Server.Host/Components/Session/ISessionPersistor.cs @@ -13,8 +13,8 @@ namespace Tgstation.Server.Host.Components.Session /// /// The to save. /// The for the operation. - /// A representing the running operation. - Task Save(ReattachInformation reattachInformation, CancellationToken cancellationToken); + /// A representing the running operation. + ValueTask Save(ReattachInformation reattachInformation, CancellationToken cancellationToken); /// /// Load a saved . @@ -27,7 +27,7 @@ namespace Tgstation.Server.Host.Components.Session /// Clear any stored . /// /// The for the operation. - /// A representing the running operation. - Task Clear(CancellationToken cancellationToken); + /// A representing the running operation. + ValueTask Clear(CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs b/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs index cd16cfd376..d452f4fac6 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs @@ -65,7 +65,7 @@ namespace Tgstation.Server.Host.Components.Session } /// - public Task Save(ReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) => + public ValueTask Save(ReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) => { ArgumentNullException.ThrowIfNull(reattachInformation); @@ -212,7 +212,7 @@ namespace Tgstation.Server.Host.Components.Session } /// - public Task Clear(CancellationToken cancellationToken) => databaseContextFactory + public ValueTask Clear(CancellationToken cancellationToken) => databaseContextFactory .UseContext( db => { @@ -226,8 +226,8 @@ namespace Tgstation.Server.Host.Components.Session /// The to use. /// If an SQL DELETE WHERE command should be used rather than an Entity Framework transaction. /// The for the operation. - /// A representing the running operation. - async Task ClearImpl(IDatabaseContext databaseContext, bool instant, CancellationToken cancellationToken) + /// A representing the running operation. + async ValueTask ClearImpl(IDatabaseContext databaseContext, bool instant, CancellationToken cancellationToken) { var baseQuery = databaseContext .ReattachInformations diff --git a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs index 713a7e193a..fce97b6ee6 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs @@ -270,11 +270,9 @@ namespace Tgstation.Server.Host.Components.Watchdog /// Called to save the current into the when initially launched. /// /// The for the operation. - /// A representing the running operation. - protected virtual Task SessionStartupPersist(CancellationToken cancellationToken) - { - return SessionPersistor.Save(Server.ReattachInformation, cancellationToken); - } + /// A representing the running operation. + protected virtual ValueTask SessionStartupPersist(CancellationToken cancellationToken) + => SessionPersistor.Save(Server.ReattachInformation, cancellationToken); /// /// Handler for when the is . diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs index e797127e3b..c8c8ce67b5 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdog.cs @@ -244,7 +244,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } /// - protected override async Task SessionStartupPersist(CancellationToken cancellationToken) + protected override async ValueTask SessionStartupPersist(CancellationToken cancellationToken) { await ApplyInitialDmb(cancellationToken); await base.SessionStartupPersist(cancellationToken); diff --git a/src/Tgstation.Server.Host/Database/DatabaseContext.cs b/src/Tgstation.Server.Host/Database/DatabaseContext.cs index 7309d6e639..7689f29060 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseContext.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseContext.cs @@ -283,7 +283,7 @@ namespace Tgstation.Server.Host.Database public Task Drop(CancellationToken cancellationToken) => Database.EnsureDeletedAsync(cancellationToken); /// - public async Task Migrate(ILogger logger, CancellationToken cancellationToken) + public async ValueTask Migrate(ILogger logger, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(logger); var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken); @@ -396,7 +396,7 @@ namespace Tgstation.Server.Host.Database /// #pragma warning disable CA1502 // Cyclomatic complexity - public async Task SchemaDowngradeForServerVersion( + public async ValueTask SchemaDowngradeForServerVersion( ILogger logger, Version targetVersion, DatabaseType currentDatabaseType, diff --git a/src/Tgstation.Server.Host/Database/DatabaseContextFactory.cs b/src/Tgstation.Server.Host/Database/DatabaseContextFactory.cs index 0f6a4fcebd..367b9f030f 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseContextFactory.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseContextFactory.cs @@ -26,7 +26,16 @@ namespace Tgstation.Server.Host.Database } /// - public async Task UseContext(Func operation) + public async ValueTask UseContext(Func operation) + { + ArgumentNullException.ThrowIfNull(operation); + + await using var scope = scopeFactory.CreateAsyncScope(); + await operation(scope.ServiceProvider.GetRequiredService()); + } + + /// + public async ValueTask UseContext2(Func operation) { ArgumentNullException.ThrowIfNull(operation); diff --git a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs index 00188bfd7f..b7512c7ae5 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs @@ -102,7 +102,7 @@ namespace Tgstation.Server.Host.Database } /// - public async Task Initialize(IDatabaseContext databaseContext, CancellationToken cancellationToken) + public async ValueTask Initialize(IDatabaseContext databaseContext, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(databaseContext); @@ -131,7 +131,7 @@ namespace Tgstation.Server.Host.Database } /// - public Task Downgrade(IDatabaseContext databaseContext, Version downgradeVersion, CancellationToken cancellationToken) + public ValueTask Downgrade(IDatabaseContext databaseContext, Version downgradeVersion, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(databaseContext); ArgumentNullException.ThrowIfNull(downgradeVersion); @@ -172,8 +172,8 @@ namespace Tgstation.Server.Host.Database /// /// The to seed. /// The for the operation. - /// A representing the running operation. - async Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken) + /// A representing the running operation. + async ValueTask SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken) { var adminUser = SeedAdminUser(databaseContext); @@ -191,8 +191,8 @@ namespace Tgstation.Server.Host.Database /// /// The to sanitize. /// The for the operation. - /// A representing the running operation. - async Task SanitizeDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken) + /// A representing the running operation. + async ValueTask SanitizeDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken) { var admin = await GetAdminUser(databaseContext, cancellationToken); if (admin != null) @@ -270,8 +270,8 @@ namespace Tgstation.Server.Host.Database /// /// The to reset the admin password for. /// The for the operation. - /// A representing the running operation. - async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken) + /// A representing the running operation. + async ValueTask ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken) { var admin = await GetAdminUser(databaseContext, cancellationToken); if (admin != null) @@ -302,8 +302,8 @@ namespace Tgstation.Server.Host.Database /// /// The to use. /// The for the operation. - /// A resulting in the admin or . If , must be called on . - async Task GetAdminUser(IDatabaseContext databaseContext, CancellationToken cancellationToken) + /// A resulting in the admin or . If , must be called on . + async ValueTask GetAdminUser(IDatabaseContext databaseContext, CancellationToken cancellationToken) { var admin = await databaseContext .Users diff --git a/src/Tgstation.Server.Host/Database/IDatabaseContext.cs b/src/Tgstation.Server.Host/Database/IDatabaseContext.cs index f4468c63f2..00b90298c6 100644 --- a/src/Tgstation.Server.Host/Database/IDatabaseContext.cs +++ b/src/Tgstation.Server.Host/Database/IDatabaseContext.cs @@ -109,8 +109,8 @@ namespace Tgstation.Server.Host.Database /// /// The to use. /// The for the operation. - /// A resulting in if the database should be seeded, otherwise. - Task Migrate(ILogger logger, CancellationToken cancellationToken); + /// A resulting in if the database should be seeded, otherwise. + ValueTask Migrate(ILogger logger, CancellationToken cancellationToken); /// /// Attempt to downgrade the schema to the migration used for a given server . @@ -119,8 +119,8 @@ namespace Tgstation.Server.Host.Database /// The tgstation-server that the schema should downgrade for. /// The in use. /// The for the operation. - /// A representing the running operation. - Task SchemaDowngradeForServerVersion( + /// A representing the running operation. + ValueTask SchemaDowngradeForServerVersion( ILogger logger, Version targetVersion, DatabaseType currentDatabaseType, diff --git a/src/Tgstation.Server.Host/Database/IDatabaseContextFactory.cs b/src/Tgstation.Server.Host/Database/IDatabaseContextFactory.cs index 7954066e0b..228d34f498 100644 --- a/src/Tgstation.Server.Host/Database/IDatabaseContextFactory.cs +++ b/src/Tgstation.Server.Host/Database/IDatabaseContextFactory.cs @@ -12,7 +12,14 @@ namespace Tgstation.Server.Host.Database /// Run an in the scope of an . /// /// The operation to run. - /// A representing the running . - Task UseContext(Func operation); + /// A representing the running . + ValueTask UseContext(Func operation); + + /// + /// Run an in the scope of an . + /// + /// The operation to run. + /// A representing the running . + ValueTask UseContext2(Func operation); } } diff --git a/src/Tgstation.Server.Host/Database/IDatabaseSeeder.cs b/src/Tgstation.Server.Host/Database/IDatabaseSeeder.cs index 8e218c5e61..e888b0093a 100644 --- a/src/Tgstation.Server.Host/Database/IDatabaseSeeder.cs +++ b/src/Tgstation.Server.Host/Database/IDatabaseSeeder.cs @@ -14,8 +14,8 @@ namespace Tgstation.Server.Host.Database /// /// The to setup. /// The for the operation. - /// A representing the running operation. - Task Initialize(IDatabaseContext databaseContext, CancellationToken cancellationToken); + /// A representing the running operation. + ValueTask Initialize(IDatabaseContext databaseContext, CancellationToken cancellationToken); /// /// Migrate a given down. @@ -23,7 +23,7 @@ namespace Tgstation.Server.Host.Database /// The to downgrade. /// The migration to downgrade the to. /// The for the operation. - /// A representing the running operation. - Task Downgrade(IDatabaseContext databaseContext, Version downgradeVersion, CancellationToken cancellationToken); + /// A representing the running operation. + ValueTask Downgrade(IDatabaseContext databaseContext, Version downgradeVersion, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Jobs/IJobManager.cs b/src/Tgstation.Server.Host/Jobs/IJobManager.cs index f02f765994..4741406a16 100644 --- a/src/Tgstation.Server.Host/Jobs/IJobManager.cs +++ b/src/Tgstation.Server.Host/Jobs/IJobManager.cs @@ -23,8 +23,8 @@ namespace Tgstation.Server.Host.Jobs /// The . Should at least have and . If is , the TGS user will be used. /// The for the . /// The for the operation. - /// A representing a running operation. - Task RegisterOperation(Job job, JobEntrypoint operation, CancellationToken cancellationToken); + /// A representing a running operation. + ValueTask RegisterOperation(Job job, JobEntrypoint operation, CancellationToken cancellationToken); /// /// Wait for a given to complete. diff --git a/src/Tgstation.Server.Host/Jobs/JobService.cs b/src/Tgstation.Server.Host/Jobs/JobService.cs index 6f8b3901fe..445d7e235d 100644 --- a/src/Tgstation.Server.Host/Jobs/JobService.cs +++ b/src/Tgstation.Server.Host/Jobs/JobService.cs @@ -87,7 +87,7 @@ namespace Tgstation.Server.Host.Jobs } /// - public Task RegisterOperation(Job job, JobEntrypoint operation, CancellationToken cancellationToken) + public ValueTask RegisterOperation(Job job, JobEntrypoint operation, CancellationToken cancellationToken) => databaseContextFactory.UseContext( async databaseContext => { @@ -168,7 +168,8 @@ namespace Tgstation.Server.Host.Jobs } noMoreJobsShouldStart = false; - }); + }) + .AsTask(); /// public async Task StopAsync(CancellationToken cancellationToken) diff --git a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs index 7389acbc81..21fabce4d7 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestDiscordProvider.cs @@ -36,7 +36,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests mockSetup .Setup(x => x.RegisterOperation(It.IsNotNull(), It.IsNotNull(), It.IsAny())) .Callback((job, entrypoint, cancellationToken) => job.StartedBy ??= new User { }) - .Returns(Task.CompletedTask); + .Returns(ValueTask.CompletedTask); mockSetup .Setup(x => x.WaitForJobCompletion(It.IsNotNull(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(Task.CompletedTask); diff --git a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs index b11b0ea074..87f61edf6f 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs @@ -73,7 +73,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests mockSetup .Setup(x => x.RegisterOperation(It.IsNotNull(), It.IsNotNull(), It.IsAny())) .Callback((job, entrypoint, cancellationToken) => job.StartedBy ??= new User { }) - .Returns(Task.CompletedTask); + .Returns(ValueTask.CompletedTask); mockSetup .Setup(x => x.WaitForJobCompletion(It.IsNotNull(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(Task.CompletedTask); diff --git a/tests/Tgstation.Server.Host.Tests/Database/TestDatabaseContextFactory.cs b/tests/Tgstation.Server.Host.Tests/Database/TestDatabaseContextFactory.cs index 06d125fcb9..389f2e4087 100644 --- a/tests/Tgstation.Server.Host.Tests/Database/TestDatabaseContextFactory.cs +++ b/tests/Tgstation.Server.Host.Tests/Database/TestDatabaseContextFactory.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System; @@ -41,12 +41,12 @@ namespace Tgstation.Server.Host.Database.Tests var factory = new DatabaseContextFactory(mockScopeFactory.Object); - await Assert.ThrowsExceptionAsync(() => factory.UseContext(null)); + await Assert.ThrowsExceptionAsync(async () => await factory.UseContext(null)); await factory.UseContext(context => { Assert.AreSame(mockDbo, context); - return Task.CompletedTask; + return ValueTask.CompletedTask; }); mockScopeFactory.VerifyAll(); diff --git a/tests/Tgstation.Server.Host.Tests/Swarm/TestableSwarmNode.cs b/tests/Tgstation.Server.Host.Tests/Swarm/TestableSwarmNode.cs index 77585c75a5..87ecc100f7 100644 --- a/tests/Tgstation.Server.Host.Tests/Swarm/TestableSwarmNode.cs +++ b/tests/Tgstation.Server.Host.Tests/Swarm/TestableSwarmNode.cs @@ -51,7 +51,7 @@ namespace Tgstation.Server.Host.Swarm.Tests readonly Mock mockHttpClient; readonly Mock mockDBContextFactory; readonly Mock mockDatabaseSeeder; - readonly ISetup mockDatabaseSeederInitialize; + readonly ISetup mockDatabaseSeederInitialize; readonly Action recreateControllerAndService; @@ -95,7 +95,7 @@ namespace Tgstation.Server.Host.Swarm.Tests mockDatabaseSeederInitialize = new Mock().Setup(x => x.Initialize(mockDatabaseContext, It.IsAny())); mockDBContextFactory = new Mock(); mockDBContextFactory - .Setup(x => x.UseContext(It.IsNotNull>())) + .Setup(x => x.UseContext(It.IsNotNull>())) .Callback>((func) => func(mockDatabaseContext)); var mockHttpClientFactory = new Mock(); @@ -226,9 +226,13 @@ namespace Tgstation.Server.Host.Swarm.Tests Assert.Fail("Initialized twice!"); if (!cancel) - mockDatabaseSeederInitialize.Returns(Task.CompletedTask).Verifiable(); + mockDatabaseSeederInitialize.Returns(ValueTask.CompletedTask).Verifiable(); else - mockDatabaseSeederInitialize.ThrowsAsync(new TaskCanceledException()).Verifiable(); + mockDatabaseSeederInitialize.Returns(async () => + { + await Task.Yield(); + throw new TaskCanceledException(); + }).Verifiable(); Task Invoke() => Service.Initialize(default); diff --git a/tests/Tgstation.Server.Tests/TestDatabase.cs b/tests/Tgstation.Server.Tests/TestDatabase.cs index 12361f0949..1a5e60d19f 100644 --- a/tests/Tgstation.Server.Tests/TestDatabase.cs +++ b/tests/Tgstation.Server.Tests/TestDatabase.cs @@ -73,7 +73,7 @@ namespace Tgstation.Server.Tests return null; } - using var context = CreateContext(); + await using var context = CreateContext(); await context.Database.EnsureDeletedAsync(); await context.Database.MigrateAsync(default);