From 775d584df7fa91704ec7070f0394b5ac1b893874 Mon Sep 17 00:00:00 2001 From: ClemensFischer Date: Thu, 21 Aug 2025 14:25:58 +0200 Subject: [PATCH] Reverted request cancellation in TileImageLoader --- .../Avalonia/TileImageLoader.Avalonia.cs | 11 +---- MapControl/Shared/GroundOverlay.cs | 2 +- MapControl/Shared/ImageLoader.cs | 9 +++- MapControl/Shared/TileImageLoader.cs | 49 +++++-------------- MapControl/Shared/TileSource.cs | 4 +- MapControl/UWP/TileImageLoader.UWP.cs | 18 ++----- MapControl/WPF/TileImageLoader.WPF.cs | 11 +---- MapControl/WinUI/TileImageLoader.WinUI.cs | 18 ++----- 8 files changed, 35 insertions(+), 87 deletions(-) diff --git a/MapControl/Avalonia/TileImageLoader.Avalonia.cs b/MapControl/Avalonia/TileImageLoader.Avalonia.cs index 90010fec..6fef3397 100644 --- a/MapControl/Avalonia/TileImageLoader.Avalonia.cs +++ b/MapControl/Avalonia/TileImageLoader.Avalonia.cs @@ -8,18 +8,11 @@ namespace MapControl { public partial class TileImageLoader { - private static async Task LoadTileImage(Tile tile, Func> loadImageFunc, CancellationToken cancellationToken) + private static async Task LoadTileImage(Tile tile, Func> loadImageFunc) { var image = await loadImageFunc().ConfigureAwait(false); - if (cancellationToken.IsCancellationRequested) - { - tile.IsPending = true; - } - else - { - _ = Dispatcher.UIThread.InvokeAsync(() => tile.SetImageSource(image)); // no need to await InvokeAsync - } + await Dispatcher.UIThread.InvokeAsync(() => tile.SetImageSource(image)); } } } diff --git a/MapControl/Shared/GroundOverlay.cs b/MapControl/Shared/GroundOverlay.cs index 5d7a39a9..39f95a38 100644 --- a/MapControl/Shared/GroundOverlay.cs +++ b/MapControl/Shared/GroundOverlay.cs @@ -165,7 +165,7 @@ namespace MapControl foreach (var imageOverlay in imageOverlays) { - imageOverlay.ImageSource = await ImageLoader.LoadImageAsync(new Uri(docUri, imageOverlay.ImagePath), null, CancellationToken.None); + imageOverlay.ImageSource = await ImageLoader.LoadImageAsync(new Uri(docUri, imageOverlay.ImagePath)); } return imageOverlays; diff --git a/MapControl/Shared/ImageLoader.cs b/MapControl/Shared/ImageLoader.cs index 97bd9b4c..4c7ffa02 100644 --- a/MapControl/Shared/ImageLoader.cs +++ b/MapControl/Shared/ImageLoader.cs @@ -32,6 +32,11 @@ namespace MapControl HttpClient.DefaultRequestHeaders.Add("User-Agent", $"XAML-Map-Control/{typeof(ImageLoader).Assembly.GetName().Version}"); } + public static Task LoadImageAsync(Uri uri, IProgress progress = null) + { + return LoadImageAsync(uri, progress, CancellationToken.None); + } + public static async Task LoadImageAsync(Uri uri, IProgress progress, CancellationToken cancellationToken) { ImageSource image = null; @@ -94,7 +99,9 @@ namespace MapControl try { - using (var responseMessage = await HttpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false)) + var completionOptions = progress != null ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead; + + using (var responseMessage = await HttpClient.GetAsync(uri, completionOptions, cancellationToken).ConfigureAwait(false)) { if (responseMessage.IsSuccessStatusCode) { diff --git a/MapControl/Shared/TileImageLoader.cs b/MapControl/Shared/TileImageLoader.cs index a354c8cc..68ce5676 100644 --- a/MapControl/Shared/TileImageLoader.cs +++ b/MapControl/Shared/TileImageLoader.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; #if WPF using System.Windows.Media; -using static System.Net.WebRequestMethods; #elif UWP using Windows.UI.Xaml.Media; #elif WINUI @@ -68,13 +67,6 @@ namespace MapControl /// public static int MaxLoadTasks { get; set; } = 4; - /// - /// Indicates whether HTTP requests are cancelled when the LoadTilesAsync method is cancelled. - /// If the property value is false, cancellation only stops dequeuing entries from the tile queue, - /// but lets currently running requests run to completion. - /// - public static bool RequestCancellationEnabled { get; set; } - private static ILogger logger; private static ILogger Logger => logger ?? (logger = ImageLoader.LoggerFactory?.CreateLogger()); @@ -103,13 +95,11 @@ namespace MapControl progress?.Report((double)(tileCount - pendingTiles.Count) / tileCount); - Logger?.LogTrace("[{thread}] Load {zoom}/{column}/{row}", Environment.CurrentManagedThreadId, tile.ZoomLevel, tile.Column, tile.Row); + Logger?.LogTrace("[{thread}] Loading {zoom}/{column}/{row}", Environment.CurrentManagedThreadId, tile.ZoomLevel, tile.Column, tile.Row); try { - var requestCancellationToken = RequestCancellationEnabled ? cancellationToken : CancellationToken.None; - - await LoadTileImage(tile, tileSource, cacheName, requestCancellationToken).ConfigureAwait(false); + await LoadTileImage(tile, tileSource, cacheName).ConfigureAwait(false); } catch (Exception ex) { @@ -120,14 +110,7 @@ namespace MapControl try { - var tasks = new Task[taskCount]; - - for (int i = 0; i < taskCount; i++) - { - tasks[i] = Task.Run(LoadTilesFromQueue, cancellationToken); - } - - await Task.WhenAll(tasks); + await Task.WhenAll(Enumerable.Range(0, taskCount).Select(_ => Task.Run(LoadTilesFromQueue, cancellationToken))); } catch (OperationCanceledException) { @@ -141,16 +124,16 @@ namespace MapControl } } - private static async Task LoadTileImage(Tile tile, TileSource tileSource, string cacheName, CancellationToken cancellationToken) + private static async Task LoadTileImage(Tile tile, TileSource tileSource, string cacheName) { // Pass tileSource.LoadImageAsync calls to platform-specific method // LoadTileImage(Tile, Func>) for execution on the UI thread in WinUI and UWP. if (string.IsNullOrEmpty(cacheName)) { - Task LoadImage() => tileSource.LoadImageAsync(tile.Column, tile.Row, tile.ZoomLevel, cancellationToken); + Task LoadImage() => tileSource.LoadImageAsync(tile.Column, tile.Row, tile.ZoomLevel); - await LoadTileImage(tile, LoadImage, cancellationToken).ConfigureAwait(false); + await LoadTileImage(tile, LoadImage).ConfigureAwait(false); } else { @@ -158,19 +141,19 @@ namespace MapControl if (uri != null) { - var buffer = await LoadCachedBuffer(tile, uri, cacheName, cancellationToken).ConfigureAwait(false); + var buffer = await LoadCachedBuffer(tile, uri, cacheName).ConfigureAwait(false); if (buffer != null && buffer.Length > 0) { Task LoadImage() => tileSource.LoadImageAsync(buffer); - await LoadTileImage(tile, LoadImage, cancellationToken).ConfigureAwait(false); + await LoadTileImage(tile, LoadImage).ConfigureAwait(false); } } } } - private static async Task LoadCachedBuffer(Tile tile, Uri uri, string cacheName, CancellationToken cancellationToken) + private static async Task LoadCachedBuffer(Tile tile, Uri uri, string cacheName) { byte[] buffer = null; @@ -185,11 +168,7 @@ namespace MapControl try { - buffer = await Cache.GetAsync(cacheKey, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - Logger?.LogTrace("Cancelled Cache.GetAsync({cacheKey})", cacheKey); + buffer = await Cache.GetAsync(cacheKey).ConfigureAwait(false); } catch (Exception ex) { @@ -198,7 +177,7 @@ namespace MapControl if (buffer == null) { - var response = await ImageLoader.GetHttpResponseAsync(uri, null, cancellationToken).ConfigureAwait(false); + var response = await ImageLoader.GetHttpResponseAsync(uri, null, CancellationToken.None).ConfigureAwait(false); if (response != null) { @@ -215,11 +194,7 @@ namespace MapControl : response.MaxAge.Value }; - await Cache.SetAsync(cacheKey, buffer, options, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - Logger?.LogTrace("Cancelled Cache.SetAsync({cacheKey})", cacheKey); + await Cache.SetAsync(cacheKey, buffer, options).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MapControl/Shared/TileSource.cs b/MapControl/Shared/TileSource.cs index d656470d..15885b84 100644 --- a/MapControl/Shared/TileSource.cs +++ b/MapControl/Shared/TileSource.cs @@ -74,11 +74,11 @@ namespace MapControl /// Loads a tile ImageSource asynchronously from GetUri(column, row, zoomLevel). /// This method is called by TileImageLoader when caching is disabled. /// - public virtual Task LoadImageAsync(int column, int row, int zoomLevel, CancellationToken cancellationToken) + public virtual Task LoadImageAsync(int column, int row, int zoomLevel) { var uri = GetUri(column, row, zoomLevel); - return uri != null ? ImageLoader.LoadImageAsync(uri, null, cancellationToken) : Task.FromResult((ImageSource)null); + return uri != null ? ImageLoader.LoadImageAsync(uri) : Task.FromResult((ImageSource)null); } /// diff --git a/MapControl/UWP/TileImageLoader.UWP.cs b/MapControl/UWP/TileImageLoader.UWP.cs index f3c8d0bc..2f8640d1 100644 --- a/MapControl/UWP/TileImageLoader.UWP.cs +++ b/MapControl/UWP/TileImageLoader.UWP.cs @@ -8,7 +8,7 @@ namespace MapControl { public partial class TileImageLoader { - private static async Task LoadTileImage(Tile tile, Func> loadImageFunc, CancellationToken cancellationToken) + private static async Task LoadTileImage(Tile tile, Func> loadImageFunc) { var tcs = new TaskCompletionSource(); @@ -16,18 +16,8 @@ namespace MapControl { try { - var image = await loadImageFunc(); - - if (cancellationToken.IsCancellationRequested) - { - tile.IsPending = true; - tcs.TrySetCanceled(cancellationToken); - } - else - { - tile.SetImageSource(image); - tcs.TrySetResult(null); - } + tile.SetImageSource(await loadImageFunc()); + tcs.TrySetResult(null); } catch (Exception ex) { @@ -37,7 +27,7 @@ namespace MapControl if (!await tile.Image.Dispatcher.TryRunAsync(CoreDispatcherPriority.Low, LoadTileImage)) { - tcs.TrySetCanceled(CancellationToken.None); + tcs.TrySetCanceled(); } await tcs.Task; diff --git a/MapControl/WPF/TileImageLoader.WPF.cs b/MapControl/WPF/TileImageLoader.WPF.cs index 48e2a992..a37c32e9 100644 --- a/MapControl/WPF/TileImageLoader.WPF.cs +++ b/MapControl/WPF/TileImageLoader.WPF.cs @@ -7,18 +7,11 @@ namespace MapControl { public partial class TileImageLoader { - private static async Task LoadTileImage(Tile tile, Func> loadImageFunc, CancellationToken cancellationToken) + private static async Task LoadTileImage(Tile tile, Func> loadImageFunc) { var image = await loadImageFunc().ConfigureAwait(false); - if (cancellationToken.IsCancellationRequested) - { - tile.IsPending = true; - } - else - { - _ = tile.Image.Dispatcher.InvokeAsync(() => tile.SetImageSource(image)); // no need to await InvokeAsync - } + await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImageSource(image)); } } } diff --git a/MapControl/WinUI/TileImageLoader.WinUI.cs b/MapControl/WinUI/TileImageLoader.WinUI.cs index ea40bff6..c526c233 100644 --- a/MapControl/WinUI/TileImageLoader.WinUI.cs +++ b/MapControl/WinUI/TileImageLoader.WinUI.cs @@ -8,7 +8,7 @@ namespace MapControl { public partial class TileImageLoader { - private static Task LoadTileImage(Tile tile, Func> loadImageFunc, CancellationToken cancellationToken) + private static Task LoadTileImage(Tile tile, Func> loadImageFunc) { var tcs = new TaskCompletionSource(); @@ -16,18 +16,8 @@ namespace MapControl { try { - var image = await loadImageFunc(); - - if (cancellationToken.IsCancellationRequested) - { - tile.IsPending = true; - tcs.TrySetCanceled(cancellationToken); - } - else - { - tile.SetImageSource(image); - tcs.TrySetResult(); - } + tile.SetImageSource(await loadImageFunc()); + tcs.TrySetResult(); } catch (Exception ex) { @@ -37,7 +27,7 @@ namespace MapControl if (!tile.Image.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, LoadTileImage)) { - tcs.TrySetCanceled(CancellationToken.None); + tcs.TrySetCanceled(); } return tcs.Task;