Skip to content

Commit

Permalink
Make flow more async to avoid any chance of deadlocks
Browse files Browse the repository at this point in the history
  • Loading branch information
peppy committed Oct 24, 2024
1 parent be5cb20 commit 61f0cfd
Showing 1 changed file with 19 additions and 19 deletions.
38 changes: 19 additions & 19 deletions osu.Game/Database/RealmArchiveModelImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,26 +112,20 @@ public async Task<IEnumerable<Live<TModel>>> Import(ProgressNotification notific

parameters.Batch |= tasks.Length >= minimum_items_considered_batch_import;

// A paused state could obviously be entered mid-import (during the `Task.WhenAll` below),
// but in order to keep things simple let's focus on the most common scenario.
notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is paused due to gameplay...";

try
{
pauseIfNecessary(parameters, notification.CancellationToken);
}
catch { }

notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...";
notification.State = ProgressNotificationState.Active;

await pauseIfNecessaryAsync(parameters, notification, notification.CancellationToken).ConfigureAwait(false);

await Task.WhenAll(tasks.Select(async task =>
await Parallel.ForEachAsync(tasks, notification.CancellationToken, async (task, cancellation) =>
{
if (notification.CancellationToken.IsCancellationRequested)
return;
cancellation.ThrowIfCancellationRequested();
try
{
var model = await Import(task, parameters, notification.CancellationToken).ConfigureAwait(false);
await pauseIfNecessaryAsync(parameters, notification, cancellation).ConfigureAwait(false);
var model = await Import(task, parameters, cancellation).ConfigureAwait(false);
lock (imported)
{
Expand All @@ -150,7 +144,7 @@ await Task.WhenAll(tasks.Select(async task =>
{
Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database);
}
})).ConfigureAwait(false);
}).ConfigureAwait(false);

if (imported.Count == 0)
{
Expand Down Expand Up @@ -297,8 +291,6 @@ public async Task<ExternalEditOperation<TModel>> BeginExternalEditing(TModel mod
/// <param name="cancellationToken">An optional cancellation token.</param>
public virtual Live<TModel>? ImportModel(TModel item, ArchiveReader? archive = null, ImportParameters parameters = default, CancellationToken cancellationToken = default) => Realm.Run(realm =>
{
pauseIfNecessary(parameters, cancellationToken);
TModel? existing;
if (parameters.Batch && archive != null)
Expand Down Expand Up @@ -586,21 +578,29 @@ protected virtual void UndeleteForReuse(TModel existing)
/// <returns>Whether to perform deletion.</returns>
protected virtual bool ShouldDeleteArchive(string path) => false;

private void pauseIfNecessary(ImportParameters importParameters, CancellationToken cancellationToken)
private async Task pauseIfNecessaryAsync(ImportParameters importParameters, ProgressNotification notification, CancellationToken cancellationToken)
{
if (!PauseImports || importParameters.ImportImmediately)
return;

Logger.Log($@"{GetType().Name} is being paused.");

// A paused state could obviously be entered mid-import (during the `Task.WhenAll` below),
// but in order to keep things simple let's focus on the most common scenario.
notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is paused due to gameplay...";
notification.State = ProgressNotificationState.Queued;

while (PauseImports)
{
cancellationToken.ThrowIfCancellationRequested();
Thread.Sleep(500);
await Task.Delay(500, cancellationToken).ConfigureAwait(false);
}

cancellationToken.ThrowIfCancellationRequested();
Logger.Log($@"{GetType().Name} is being resumed.");

notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is resuming...";
notification.State = ProgressNotificationState.Active;
}

private IEnumerable<string> getIDs(IEnumerable<INamedFile> files)
Expand Down

0 comments on commit 61f0cfd

Please sign in to comment.