1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-25 05:42:59 +08:00

Make flow more async to avoid any chance of deadlocks

This commit is contained in:
Dean Herbert 2024-10-24 17:56:51 +09:00
parent be5cb209e5
commit 61f0cfd688
No known key found for this signature in database

View File

@ -112,26 +112,20 @@ namespace osu.Game.Database
parameters.Batch |= tasks.Length >= minimum_items_considered_batch_import; 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.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...";
notification.State = ProgressNotificationState.Active;
await Task.WhenAll(tasks.Select(async task => await pauseIfNecessaryAsync(parameters, notification, notification.CancellationToken).ConfigureAwait(false);
await Parallel.ForEachAsync(tasks, notification.CancellationToken, async (task, cancellation) =>
{ {
if (notification.CancellationToken.IsCancellationRequested) cancellation.ThrowIfCancellationRequested();
return;
try 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) lock (imported)
{ {
@ -150,7 +144,7 @@ namespace osu.Game.Database
{ {
Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database); Logger.Error(e, $@"Could not import ({task})", LoggingTarget.Database);
} }
})).ConfigureAwait(false); }).ConfigureAwait(false);
if (imported.Count == 0) if (imported.Count == 0)
{ {
@ -297,8 +291,6 @@ namespace osu.Game.Database
/// <param name="cancellationToken">An optional cancellation token.</param> /// <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 => public virtual Live<TModel>? ImportModel(TModel item, ArchiveReader? archive = null, ImportParameters parameters = default, CancellationToken cancellationToken = default) => Realm.Run(realm =>
{ {
pauseIfNecessary(parameters, cancellationToken);
TModel? existing; TModel? existing;
if (parameters.Batch && archive != null) if (parameters.Batch && archive != null)
@ -586,21 +578,29 @@ namespace osu.Game.Database
/// <returns>Whether to perform deletion.</returns> /// <returns>Whether to perform deletion.</returns>
protected virtual bool ShouldDeleteArchive(string path) => false; 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) if (!PauseImports || importParameters.ImportImmediately)
return; return;
Logger.Log($@"{GetType().Name} is being paused."); 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) while (PauseImports)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
Thread.Sleep(500); await Task.Delay(500, cancellationToken).ConfigureAwait(false);
} }
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
Logger.Log($@"{GetType().Name} is being resumed."); 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) private IEnumerable<string> getIDs(IEnumerable<INamedFile> files)