diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 61390fe51b..37aa0024da 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; +using osu.Framework.Statistics; using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; @@ -32,6 +33,8 @@ public abstract class WorkingBeatmap : IDisposable protected AudioManager AudioManager { get; } + private static readonly GlobalStatistic total_count = GlobalStatistics.Get(nameof(Beatmaps), $"Total {nameof(WorkingBeatmap)}s"); + protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) { AudioManager = audioManager; @@ -44,11 +47,8 @@ protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); skin = new RecyclableLazy(GetSkin); - } - ~WorkingBeatmap() - { - Dispose(false); + total_count.Value++; } protected virtual Track GetVirtualTrack() @@ -150,6 +150,7 @@ public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) public Task LoadBeatmapAsync() => (beatmapLoadTask ?? (beatmapLoadTask = Task.Factory.StartNew(() => { + // Todo: Handle cancellation during beatmap parsing var b = GetBeatmap() ?? new Beatmap(); // The original beatmap version needs to be preserved as the database doesn't contain it @@ -161,7 +162,20 @@ public Task LoadBeatmapAsync() => (beatmapLoadTask ?? (beatmapLoadTask return b; }, beatmapCancellation.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default))); - public IBeatmap Beatmap => LoadBeatmapAsync().Result; + public IBeatmap Beatmap + { + get + { + try + { + return LoadBeatmapAsync().Result; + } + catch (TaskCanceledException) + { + return null; + } + } + } private readonly CancellationTokenSource beatmapCancellation = new CancellationTokenSource(); protected abstract IBeatmap GetBeatmap(); @@ -218,15 +232,28 @@ public void Dispose() GC.SuppressFinalize(this); } + private bool isDisposed; + protected virtual void Dispose(bool isDisposing) { + if (isDisposed) + return; + + isDisposed = true; + // recycling logic is not here for the time being, as components which use // retrieved objects from WorkingBeatmap may not hold a reference to the WorkingBeatmap itself. - // this should be fine as each retrieved comopnent do have their own finalizers. + // this should be fine as each retrieved component do have their own finalizers. // cancelling the beatmap load is safe for now since the retrieval is a synchronous // operation. if we add an async retrieval method this may need to be reconsidered. beatmapCancellation.Cancel(); + total_count.Value--; + } + + ~WorkingBeatmap() + { + Dispose(false); } #endregion diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bfa4aeadef..49c543537a 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -296,6 +296,8 @@ private void beatmapChanged(ValueChangedEvent beatmap) if (nextBeatmap?.Track != null) nextBeatmap.Track.Completed += currentTrackCompleted; + beatmap.OldValue?.Dispose(); + nextBeatmap?.LoadBeatmapAsync(); }