From 720863d55e76db19a786db36c1ac10015e1f29c5 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Mon, 7 May 2018 10:06:01 -0400 Subject: [PATCH] Remove timer shennanigans from repository. Fix null project name handling. Add IDatabaseContextFactory --- src/Tgstation.Server.Api/Models/Instance.cs | 6 + .../Models/Internal/DreamMakerSettings.cs | 6 - .../Models/Internal/RepositorySettings.cs | 6 - .../Rights/DreamMakerRights.cs | 6 +- .../Rights/InstanceManagerRights.cs | 6 +- .../Rights/RepositoryRights.cs | 10 +- .../Components/DmbFactory.cs | 16 +-- .../Components/DreamMaker.cs | 36 +++--- .../Components/IDreamMaker.cs | 6 +- .../Components/IInstance.cs | 3 + .../Components/IInstanceFactory.cs | 6 +- .../Components/IRepositoryManager.cs | 11 +- .../Components/Instance.cs | 110 +++++++++++++++++- .../Components/InstanceFactory.cs | 5 +- .../Components/RepositoryManager.cs | 82 +------------ .../Core/IDatabaseContextFactory.cs | 19 +++ 16 files changed, 181 insertions(+), 153 deletions(-) create mode 100644 src/Tgstation.Server.Host/Core/IDatabaseContextFactory.cs diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs index 22bd31e1be..74910fdf68 100644 --- a/src/Tgstation.Server.Api/Models/Instance.cs +++ b/src/Tgstation.Server.Api/Models/Instance.cs @@ -41,6 +41,12 @@ namespace Tgstation.Server.Api.Models [Permissions(WriteRight = InstanceManagerRights.SetConfiguration)] public bool ConfigurationAllowed { get; set; } + /// + /// The time interval in minutes the repository is automatically pulled and compiles + /// + [Permissions(WriteRight = InstanceManagerRights.SetAutoUpdate)] + public int? AutoUpdateInterval { get; set; } + /// public Instance CloneMetadata() => new Instance { diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs b/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs index 2e07ee683a..154167ec82 100644 --- a/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs @@ -8,12 +8,6 @@ namespace Tgstation.Server.Api.Models.Internal [Model(RightsType.DreamMaker, ReadRight = DreamMakerRights.Read, CanCrud = true, RequiresInstance = true)] public class DreamMakerSettings { - /// - /// How often the automatically compiles in minutes - /// - [Permissions(WriteRight = DreamMakerRights.SetAutoCompile)] - public int? AutoCompileInterval { get; set; } - /// /// The .dme file tries to compile with without the extension /// diff --git a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs index 35a560f7da..5893293fa7 100644 --- a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs +++ b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs @@ -46,11 +46,5 @@ namespace Tgstation.Server.Api.Models.Internal /// [Permissions(WriteRight = RepositoryRights.ChangeTestMergeCommits)] public bool ShowTestMergeCommitters { get; set; } - - /// - /// How often the automatically updates in minutes - /// - [Permissions(WriteRight = RepositoryRights.ChangeAutoUpdate)] - public int? AutoUpdateInterval { get; set; } } } diff --git a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs index 661530c95a..bce942c165 100644 --- a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs +++ b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs @@ -25,12 +25,8 @@ namespace Tgstation.Server.Api.Rights /// CancelCompile = 4, /// - /// User may modify - /// - SetAutoCompile = 8, - /// /// User may modify /// - SetDme = 16 + SetDme = 8 } } diff --git a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs index 106eaa1ad1..fbec63f9d5 100644 --- a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs +++ b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs @@ -43,6 +43,10 @@ namespace Tgstation.Server.Api.Rights /// /// User can change /// - SetConfiguration = 128 + SetConfiguration = 128, + /// + /// User can change + /// + SetAutoUpdate = 256 } } diff --git a/src/Tgstation.Server.Api/Rights/RepositoryRights.cs b/src/Tgstation.Server.Api/Rights/RepositoryRights.cs index c418507036..23703fd446 100644 --- a/src/Tgstation.Server.Api/Rights/RepositoryRights.cs +++ b/src/Tgstation.Server.Api/Rights/RepositoryRights.cs @@ -37,20 +37,16 @@ namespace Tgstation.Server.Api.Rights /// ChangeTestMergeCommits = 64, /// - /// User may change - /// - ChangeAutoUpdate = 128, - /// /// User may read and change and /// - ChangeCredentials = 256, + ChangeCredentials = 128, /// /// User may set to another git reference (not a SHA) /// - SetReference = 512, + SetReference = 256, /// /// User may read all fields in the with the exception of /// - Read = 1024, + Read = 512, } } diff --git a/src/Tgstation.Server.Host/Components/DmbFactory.cs b/src/Tgstation.Server.Host/Components/DmbFactory.cs index cce071c63f..9d21f4747f 100644 --- a/src/Tgstation.Server.Host/Components/DmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/DmbFactory.cs @@ -14,9 +14,9 @@ namespace Tgstation.Server.Host.Components sealed class DmbFactory : IDmbFactory, ICompileJobConsumer { /// - /// The for the + /// The for the /// - readonly IDatabaseContext databaseContext; + readonly IDatabaseContextFactory databaseContextFactory; /// /// The for the /// @@ -42,11 +42,11 @@ namespace Tgstation.Server.Host.Components /// /// Construct a /// - /// The value of + /// The value of /// The value of - public DmbFactory(IDatabaseContext databaseContext, IIOManager ioManager) + public DmbFactory(IDatabaseContextFactory databaseContextFactory, IIOManager ioManager) { - this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext)); + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); cleanupCts = new CancellationTokenSource(); @@ -117,16 +117,16 @@ namespace Tgstation.Server.Host.Components } /// - public async Task StartAsync(CancellationToken cancellationToken) + public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) => { - var cj = await databaseContext.CompileJobs.OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + var cj = await db.CompileJobs.OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (cj == default(CompileJob)) return; LoadCompileJob(cj); //delete all other compile jobs var directories = await ioManager.GetDirectories(".", cancellationToken).ConfigureAwait(false); await Task.WhenAll(directories.Where(x => x != cj.Job.ToString()).Select(x => ioManager.DeleteDirectory(x, cancellationToken))).ConfigureAwait(false); - } + }); /// public async Task StopAsync(CancellationToken cancellationToken) diff --git a/src/Tgstation.Server.Host/Components/DreamMaker.cs b/src/Tgstation.Server.Host/Components/DreamMaker.cs index 441b45de40..f70df87e5e 100644 --- a/src/Tgstation.Server.Host/Components/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/DreamMaker.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -23,8 +24,13 @@ namespace Tgstation.Server.Host.Components /// Name of the secondary directory used for compilation /// public const string BDirectoryName = "B"; + /// + /// Extension for .dmbs + /// public const string DmbExtension = ".dmb"; - + /// + /// Extension for .dmes + /// const string DmeExtension = ".dme"; /// @@ -61,6 +67,7 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of /// The value of + /// public DreamMaker(IIOManager ioManager, IConfiguration configuration, IDreamDaemonExecutor dreamDaemonExecutor, IByond byond, IInterop interop, ICompileJobConsumer compileJobConsumer) { this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); @@ -202,12 +209,12 @@ namespace Tgstation.Server.Host.Components } /// - public async Task Compile(string dmeName, IRepository repository, CancellationToken cancellationToken) + public async Task Compile(string projectName, IRepository repository, CancellationToken cancellationToken) { var job = new Host.Models.CompileJob { DirectoryName = Guid.NewGuid(), - DmeName = dmeName + DmeName = projectName }; await ioManager.CreateDirectory(job.DirectoryName.ToString(), cancellationToken).ConfigureAwait(false); var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName); @@ -229,6 +236,16 @@ namespace Tgstation.Server.Host.Components using (repository) await repository.CopyTo(fullDirA, cancellationToken).ConfigureAwait(false); + if (job.DmeName == null) + { + job.DmeName = (await ioManager.GetFilesWithExtension(dirA, DmeExtension, cancellationToken).ConfigureAwait(false)).FirstOrDefault(); + if (job.DmeName == default) + { + job.Output = "Unable to find any .dme!"; + return job; + } + } + await ModifyDme(job, cancellationToken).ConfigureAwait(false); //run compiler, verify api @@ -263,18 +280,5 @@ namespace Tgstation.Server.Host.Components throw; } } - - /// - public Task StartAsync(CancellationToken cancellationToken) - { - //THIS IS FOR AUTO COMPILE INTERVAL STUFF, STOP TRYING TO REMOVE IT - throw new NotImplementedException(); - } - - /// - public Task StopAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } } } diff --git a/src/Tgstation.Server.Host/Components/IDreamMaker.cs b/src/Tgstation.Server.Host/Components/IDreamMaker.cs index 3e7c52eca5..215c1582ac 100644 --- a/src/Tgstation.Server.Host/Components/IDreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/IDreamMaker.cs @@ -8,15 +8,15 @@ namespace Tgstation.Server.Host.Components /// /// For managing the compiler /// - interface IDreamMaker : IHostedService + interface IDreamMaker { /// /// Starts a compile /// - /// The .dme file to use without the extension + /// The name of the .dme to compile without the extension /// The to copy from /// The for the operation /// A resulting in the partially populated for the operation. In particular, note the field will only have it's field populated - Task Compile(string dmeName, IRepository repository, CancellationToken cancellationToken); + Task Compile(string projectName, IRepository repository, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/IInstance.cs b/src/Tgstation.Server.Host/Components/IInstance.cs index 3735a5cc13..8105873fc5 100644 --- a/src/Tgstation.Server.Host/Components/IInstance.cs +++ b/src/Tgstation.Server.Host/Components/IInstance.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Hosting; +using System.Threading.Tasks; namespace Tgstation.Server.Host.Components { @@ -19,5 +20,7 @@ namespace Tgstation.Server.Host.Components Api.Models.Instance GetMetadata(); void Rename(string newName); + + Task SetAutoUpdateInterval(int? newInterval); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/IInstanceFactory.cs b/src/Tgstation.Server.Host/Components/IInstanceFactory.cs index 7dfd5a667d..5e4e3934f8 100644 --- a/src/Tgstation.Server.Host/Components/IInstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/IInstanceFactory.cs @@ -1,4 +1,4 @@ -using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Core; namespace Tgstation.Server.Host.Components { @@ -11,8 +11,8 @@ namespace Tgstation.Server.Host.Components /// Create an /// /// The - /// The for the operation + /// The for the operation /// A new - IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext); + IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContextFactory databaseContextFactory); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/IRepositoryManager.cs b/src/Tgstation.Server.Host/Components/IRepositoryManager.cs index f778e46798..f7db73a8d7 100644 --- a/src/Tgstation.Server.Host/Components/IRepositoryManager.cs +++ b/src/Tgstation.Server.Host/Components/IRepositoryManager.cs @@ -7,8 +7,8 @@ namespace Tgstation.Server.Host.Components /// /// Factory for creating and loading s /// - interface IRepositoryManager : IHostedService - { + interface IRepositoryManager + { /// /// Attempt to load the from the default location /// @@ -24,12 +24,5 @@ namespace Tgstation.Server.Host.Components /// The for the operation /// The newly cloned Task CloneRepository(string url, string accessString, CancellationToken cancellationToken); - - /// - /// Change the interval in minutes at which the repository auto updates - /// - /// The new interval in minutes or to disable the auto update - /// A representing the running operation - Task SetAutoUpdateInterval(int? newInterval); } } diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 475c12e3d4..aea3891519 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -1,28 +1,58 @@ -using System; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using Tgstation.Server.Host.Core; namespace Tgstation.Server.Host.Components { - sealed class Instance : IInstance + /// + sealed class Instance : IInstance, IDisposable { + /// public IRepositoryManager RepositoryManager { get; } + /// public IByond Byond { get; } + /// public IDreamMaker DreamMaker { get; } + /// public IDreamDaemon DreamDaemon { get; } + /// public IChat Chat { get; } + /// public IConfiguration Configuration { get; } + /// + /// The for the + /// readonly ICompileJobConsumer compileJobConsumer; + + /// + /// The for the + /// + readonly IDatabaseContextFactory databaseContextFactory; + /// + /// The for the + /// readonly Api.Models.Instance metadata; - public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IDreamDaemon dreamDaemon, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer) + /// + /// The auto update + /// + Task timerTask; + /// + /// for + /// + CancellationTokenSource timerCts; + + public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IDreamDaemon dreamDaemon, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory) { this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager)); @@ -32,10 +62,50 @@ namespace Tgstation.Server.Host.Components Chat = chat ?? throw new ArgumentNullException(nameof(chat)); Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer)); + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); } + /// + public void Dispose() => timerCts?.Dispose(); + + /// + /// Pull the repository and compile for every set of given + /// + /// How many minutes the operation should repeat. Does not include running time + /// The for the operation + /// A representing the running operation + async Task TimerLoop(int minutes, CancellationToken cancellationToken) + { + try + { + while (true) + { + await Task.Delay(new TimeSpan(0, minutes, 0), cancellationToken).ConfigureAwait(false); + + string accessToken = null, projectName = null; + var dbTask = databaseContextFactory.UseContext(async (db) => + { + var instanceQuery = db.Instances.Where(x => x.Id == metadata.Id); + var projectNameTask = instanceQuery.Select(x => x.DreamMakerSettings.ProjectName).FirstOrDefaultAsync(cancellationToken); + accessToken = await instanceQuery.Select(x => x.RepositorySettings.AccessToken).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + projectName = await projectNameTask.ConfigureAwait(false); + }); + using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false)) + { + await dbTask.ConfigureAwait(false); + await repo.FetchOrigin(accessToken, cancellationToken).ConfigureAwait(false); + await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false); + await DreamMaker.Compile(projectName, repo, cancellationToken).ConfigureAwait(false); + } + } + } + catch (OperationCanceledException) { } + } + + /// public Api.Models.Instance GetMetadata() => metadata.CloneMetadata(); + /// public void Rename(string newName) { if (String.IsNullOrWhiteSpace(newName)) @@ -43,8 +113,38 @@ namespace Tgstation.Server.Host.Components metadata.Name = newName; } - public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(RepositoryManager.StartAsync(cancellationToken), DreamDaemon.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken), DreamMaker.StartAsync(cancellationToken)); + /// + public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval), DreamDaemon.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken)); - public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(RepositoryManager.StopAsync(cancellationToken), DreamDaemon.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken), DreamMaker.StopAsync(cancellationToken)); + /// + public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(null), DreamDaemon.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken)); + + /// + public async Task SetAutoUpdateInterval(int? newInterval) + { + Task toWait; + lock (this) + { + if (timerTask != null) + { + timerCts.Cancel(); + toWait = timerTask; + } + else + toWait = Task.CompletedTask; + } + await toWait.ConfigureAwait(false); + if (!newInterval.HasValue) + return; + lock (this) + { + //race condition, just quit + if (timerTask != null) + return; + timerCts?.Dispose(); + timerCts = new CancellationTokenSource(); + timerTask = TimerLoop(newInterval.Value, timerCts.Token); + } + } } } diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index ed4955194e..00767fcabd 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -1,6 +1,5 @@ using System; using Tgstation.Server.Host.Core; -using Tgstation.Server.Host.Models; namespace Tgstation.Server.Host.Components { @@ -19,7 +18,7 @@ namespace Tgstation.Server.Host.Components public InstanceFactory(IIOManager ioManager) => this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); /// - public IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext) + public IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContextFactory databaseContextFactory) { //Create the ioManager for the instance @@ -32,7 +31,7 @@ namespace Tgstation.Server.Host.Components var configurationIoManager = new ResolvingIOManager(instanceIoManager, "Configuration"); var codeModificationsIoMananger = new ResolvingIOManager(instanceIoManager, "CodeModifications"); - var dmbFactory = new DmbFactory(databaseContext, gameIoManager); + var dmbFactory = new DmbFactory(databaseContextFactory, gameIoManager); diff --git a/src/Tgstation.Server.Host/Components/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/RepositoryManager.cs index 5982887adb..ffb8cf1049 100644 --- a/src/Tgstation.Server.Host/Components/RepositoryManager.cs +++ b/src/Tgstation.Server.Host/Components/RepositoryManager.cs @@ -25,16 +25,6 @@ namespace Tgstation.Server.Host.Components /// readonly SemaphoreSlim semaphore; - /// - /// for - /// - CancellationTokenSource timerCancellationTokenSource; - - /// - /// Represents the running update timer if any - /// - Task currentTimerTask; - /// /// Construct a /// @@ -48,49 +38,7 @@ namespace Tgstation.Server.Host.Components } /// - public void Dispose() - { - timerCancellationTokenSource?.Dispose(); - semaphore.Dispose(); - } - - /// - /// Stops and joins it - /// - /// A representing the running operation - async Task StopTimer() - { - if (currentTimerTask == null) - return; - timerCancellationTokenSource.Cancel(); - await currentTimerTask.ConfigureAwait(false); - currentTimerTask = null; - } - - /// - /// Asyncronously fetch and reset the current branch for each given amount of - /// - /// The delay of the timer - /// The accessString to use for fetch operations - /// The for the operation - /// A representing the running operation - async Task TimerLoop(int minutes, string accessString, CancellationToken cancellationToken) - { - try - { - while (true) - { - await Task.Delay(TimeSpan.FromMinutes(minutes), cancellationToken).ConfigureAwait(false); - using (var repo = await LoadRepository(cancellationToken).ConfigureAwait(false)) - { - //TODO: Find the unauthorized exception, catch it, and log it - await repo.FetchOrigin(accessString, cancellationToken).ConfigureAwait(false); - await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false); - } - } - } - catch (OperationCanceledException) { } - } + public void Dispose() => semaphore.Dispose(); /// public async Task CloneRepository(string url, string accessString, CancellationToken cancellationToken) @@ -134,33 +82,5 @@ namespace Tgstation.Server.Host.Components localSemaphore = null; }); } - - /// - public async Task SetAutoUpdateInterval(int? newInterval) - { - await StopTimer().ConfigureAwait(false); - if (!newInterval.HasValue) - return; - - string accessString = null; - - if (timerCancellationTokenSource != null) - timerCancellationTokenSource.Dispose(); - timerCancellationTokenSource = new CancellationTokenSource(); - - currentTimerTask = TimerLoop(repositorySettings.AutoUpdateInterval.Value, accessString, timerCancellationTokenSource.Token); - } - - /// - public Task StartAsync(CancellationToken cancellationToken) => SetAutoUpdateInterval(repositorySettings.AutoUpdateInterval); - - /// - public async Task StopAsync(CancellationToken cancellationToken) - { - var timerStopTask = StopTimer(); - var tcs = new TaskCompletionSource(); - using (cancellationToken.Register(() => tcs.SetCanceled())) - await Task.WhenAny(timerStopTask, tcs.Task).ConfigureAwait(false); - } } } diff --git a/src/Tgstation.Server.Host/Core/IDatabaseContextFactory.cs b/src/Tgstation.Server.Host/Core/IDatabaseContextFactory.cs new file mode 100644 index 0000000000..305092416c --- /dev/null +++ b/src/Tgstation.Server.Host/Core/IDatabaseContextFactory.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Core +{ + /// + /// Factory for scoping usage of s. Meant for use by + /// + interface IDatabaseContextFactory + { + /// + /// Run an in the scope of an + /// + /// The operation to run + /// A representing the running + Task UseContext(Func operation); + } +}