Skip to content

Commit f7cba71

Browse files
authored
Merge pull request #1046 from ionite34/fixes
2 parents 9dd9fda + fd30854 commit f7cba71

File tree

9 files changed

+108
-169
lines changed

9 files changed

+108
-169
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2
1818
- (pre.2 re-release) Merged Inference GGUF workflows into the UNet model loader option (no longer need to choose GGUF separately)
1919
- (pre.2 re-release) Updated some date strings to take into account the user's locale
2020
- (pre.2 re-release) Fixed some crashes when using Accelerated Model Discovery
21+
- (pre.2 re-release) Performance optimizations for Checkpoint Manager (progress indicators now fully uses Compiled Bindings)
2122
### Fixed
2223
- Fixed Inference ControlNet Preprocessors using incorrect resolution and increased maximum of smallest dimension to 16384
2324
- Fixed Triton/Sage install option showing for incompatible GPUs
2425
- Fixed errors from invalid pip specifiers in requirements files
2526
- Fixed package images sometimes showing as blank due to concurrent image caching. Requests to same image resources are now de-duplicated
2627
- (pre.2 re-release) Fixed Inference Extra Networks card not allowing for more than one model at a time
28+
- (pre.2 re-release) Reduced memory usage from `ShowDisabledTooltipExtension`
2729
### Supporters
2830
#### Visionaries
2931
- Big shout-out to our Visionary-tier patrons: Waterclouds, Corey T, bluepopsicle, and Bob S! Your steadfast support keeps Stability Matrix moving forward, and we couldn’t do it without you. 🚀 Thank you!

StabilityMatrix.Avalonia/Controls/VendorLabs/Cache/CacheBase.cs

Lines changed: 82 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,25 @@
55
// The .NET Foundation licenses this file to you under the MIT license.
66
// See the LICENSE file in the project root for more information.
77

8-
using System;
98
using System.Collections.Concurrent;
10-
using System.Collections.Generic;
9+
using System.ComponentModel;
1110
using System.Diagnostics;
12-
using System.IO;
13-
using System.Linq;
14-
using System.Net.Http;
15-
using System.Threading;
16-
using System.Threading.Tasks;
1711

1812
namespace StabilityMatrix.Avalonia.Controls.VendorLabs.Cache;
1913

14+
[Localizable(false)]
2015
internal abstract class CacheBase<T>
2116
{
22-
private class ConcurrentRequest
17+
private class ConcurrentRequest(Func<Task<T?>> factory)
2318
{
24-
public Task<T?>? Task { get; set; }
19+
private readonly Lazy<Task<T?>> _factory = new(factory, LazyThreadSafetyMode.ExecutionAndPublication);
20+
21+
public Task<T?> Task => _factory.Value;
2522

2623
public bool EnsureCachedCopy { get; set; }
24+
25+
public bool IsCompletedSuccessfully =>
26+
_factory is { IsValueCreated: true, Value.IsCompletedSuccessfully: true };
2727
}
2828

2929
private readonly SemaphoreSlim _cacheFolderSemaphore = new SemaphoreSlim(1);
@@ -33,8 +33,7 @@ private class ConcurrentRequest
3333
private string? _cacheFolder = null;
3434
protected InMemoryStorage<T>? InMemoryFileStorage = new();
3535

36-
private ConcurrentDictionary<string, ConcurrentRequest> _concurrentTasks =
37-
new ConcurrentDictionary<string, ConcurrentRequest>();
36+
private readonly ConcurrentDictionary<string, ConcurrentRequest> _concurrentTasks = new();
3837

3938
private HttpMessageHandler? _httpMessageHandler;
4039
private HttpClient? _httpClient = null;
@@ -346,50 +345,97 @@ private static ulong CreateHash64(string str)
346345
CancellationToken cancellationToken
347346
)
348347
{
349-
var instance = default(T);
350-
351348
var fileName = GetCacheFileName(uri);
352-
_concurrentTasks.TryGetValue(fileName, out var request);
353349

354-
// if similar request exists check if it was preCacheOnly and validate that current request isn't preCacheOnly
355-
if (request != null && request.EnsureCachedCopy && !preCacheOnly)
350+
// Check if already in memory cache
351+
if (InMemoryFileStorage?.MaxItemCount > 0)
356352
{
357-
if (request.Task != null)
358-
await request.Task.ConfigureAwait(false);
359-
request = null;
353+
var msi = InMemoryFileStorage?.GetItem(fileName, CacheDuration);
354+
if (msi != null)
355+
{
356+
return msi.Item;
357+
}
360358
}
361359

362-
if (request == null)
363-
{
364-
request = new ConcurrentRequest()
360+
// Atomically get or add
361+
var request = _concurrentTasks.GetOrAdd(
362+
fileName,
363+
key =>
365364
{
366-
Task = GetFromCacheOrDownloadAsync(uri, fileName, preCacheOnly, cancellationToken),
367-
EnsureCachedCopy = preCacheOnly
368-
};
369-
370-
_concurrentTasks[fileName] = request;
371-
}
365+
return new ConcurrentRequest(
366+
() => GetFromCacheOrDownloadAsync(uri, key, preCacheOnly, cancellationToken)
367+
);
368+
}
369+
);
372370

373371
try
374372
{
375-
if (request.Task != null)
376-
instance = await request.Task.ConfigureAwait(false);
373+
// Wait for the task to complete
374+
var itemTask = request.Task;
375+
var instance = await itemTask.ConfigureAwait(false);
376+
377+
// --- Handle In-Memory Caching ---
378+
// If the current request is not preCacheOnly, and the instance was successfully retrieved,
379+
// ensure it's in the memory cache.
380+
if (!preCacheOnly && instance != null && InMemoryFileStorage is { MaxItemCount: > 0 })
381+
{
382+
var memItem = InMemoryFileStorage.GetItem(fileName, CacheDuration);
383+
if (memItem == null || memItem.Item == null) // Check if not already in memory or expired
384+
{
385+
var folder = await GetCacheFolderAsync().ConfigureAwait(false);
386+
var lastWriteTime = DateTime.Now;
387+
if (folder != null)
388+
{
389+
var baseFile = Path.Combine(folder, fileName);
390+
try
391+
{
392+
if (File.Exists(baseFile)) // Check existence before FileInfo
393+
lastWriteTime = new FileInfo(baseFile).LastWriteTime;
394+
}
395+
catch (IOException ioEx)
396+
{
397+
Debug.WriteLine(
398+
$"CacheBase: Error getting FileInfo for memory cache update on {fileName}: {ioEx.Message}"
399+
);
400+
}
401+
catch (Exception ex)
402+
{
403+
Debug.WriteLine(
404+
$"CacheBase: Error getting FileInfo for memory cache update on {fileName}: {ex.Message}"
405+
);
406+
}
407+
}
408+
409+
var msi = new InMemoryStorageItem<T>(fileName, lastWriteTime, instance);
410+
InMemoryFileStorage.SetItem(msi);
411+
}
412+
}
413+
414+
return instance;
377415
}
378416
catch (Exception ex)
379417
{
380-
Debug.WriteLine($"Image loading failed for (url={uri}, file={fileName}): {ex.Message}");
418+
Debug.WriteLine($"CacheBase: Exception during GetItemAsync for {fileName} (URI: {uri}): {ex}");
419+
420+
// Attempt to remove the entry associated with the failed task.
421+
_concurrentTasks.TryRemove(new KeyValuePair<string, ConcurrentRequest>(fileName, request));
381422

382423
if (throwOnError)
383424
{
384425
throw;
385426
}
427+
return default;
386428
}
387429
finally
388430
{
389-
_concurrentTasks.TryRemove(fileName, out _);
431+
// If the request was created and its underlying task completed successfully, remove the entry.
432+
// Ensure we don't remove entries for tasks still running.
433+
if (request.IsCompletedSuccessfully)
434+
{
435+
// Remove the entry only if it still contains the Lazy instance we worked with.
436+
_concurrentTasks.TryRemove(new KeyValuePair<string, ConcurrentRequest>(fileName, request));
437+
}
390438
}
391-
392-
return instance;
393439
}
394440

395441
private async Task<T?> GetFromCacheOrDownloadAsync(
@@ -519,6 +565,8 @@ CancellationToken cancellationToken
519565
{
520566
var instance = default(T);
521567

568+
Debug.WriteLine($"CacheBase Getting: {uri}");
569+
522570
using var ms = new MemoryStream();
523571
await using (var stream = await HttpClient.GetStreamAsync(uri, cancellationToken))
524572
{

StabilityMatrix.Avalonia/MarkupExtensions/ShowDisabledTooltipExtension.cs

Lines changed: 0 additions & 113 deletions
This file was deleted.

StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFileViewModel.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
using System;
2-
using System.Collections.Immutable;
1+
using System.Collections.Immutable;
32
using System.ComponentModel;
4-
using System.IO;
5-
using System.Linq;
6-
using System.Threading.Tasks;
73
using Avalonia.Controls.Notifications;
84
using Avalonia.Data;
95
using Avalonia.Threading;
@@ -15,7 +11,6 @@
1511
using StabilityMatrix.Avalonia.Services;
1612
using StabilityMatrix.Avalonia.ViewModels.Base;
1713
using StabilityMatrix.Avalonia.ViewModels.Dialogs;
18-
using StabilityMatrix.Core.Extensions;
1914
using StabilityMatrix.Core.Helper;
2015
using StabilityMatrix.Core.Models;
2116
using StabilityMatrix.Core.Models.Api;
@@ -36,7 +31,7 @@ public partial class CheckpointFileViewModel : SelectableViewModelBase
3631
private string thumbnailUri;
3732

3833
[ObservableProperty]
39-
private ProgressReport? progress;
34+
private ProgressReport progress;
4035

4136
[ObservableProperty]
4237
private bool isLoading;

StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,6 @@
713713
Grid.RowSpan="2"
714714
Margin="0,48,0,0"
715715
VerticalAlignment="Center"
716-
x:CompileBindings="False"
717716
IsVisible="{Binding IsLoading}"
718717
Text="{Binding Progress.Title}"
719718
TextAlignment="Center"
@@ -728,7 +727,6 @@
728727
Padding="0"
729728
HorizontalAlignment="Center"
730729
VerticalAlignment="Center"
731-
x:CompileBindings="False"
732730
BorderThickness="4"
733731
Foreground="{DynamicResource ThemeRedColor}"
734732
IsEnabled="{Binding IsLoading}"

StabilityMatrix.Avalonia/Views/Dialogs/SelectModelVersionDialog.axaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
xmlns:fluentAvalonia="clr-namespace:FluentIcons.Avalonia.Fluent;assembly=FluentIcons.Avalonia.Fluent"
1212
xmlns:input="clr-namespace:FluentAvalonia.UI.Input;assembly=FluentAvalonia"
1313
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
14-
xmlns:markupExtensions="clr-namespace:StabilityMatrix.Avalonia.MarkupExtensions"
1514
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
1615
xmlns:models="clr-namespace:StabilityMatrix.Avalonia.Models"
1716
d:DataContext="{x:Static designData:DesignData.SelectModelVersionViewModel}"
@@ -303,12 +302,12 @@
303302

304303
<Button
305304
Margin="8,0"
306-
markupExtensions:ShowDisabledTooltipExtension.ShowOnDisabled="True"
307305
Classes="accent"
308306
Command="{Binding Import}"
309307
Content="{x:Static lang:Resources.Action_Import}"
310308
IsEnabled="{Binding IsImportEnabled}"
311309
IsVisible="{Binding !SelectedFile.IsInstalled, TargetNullValue=True, FallbackValue=True}"
310+
ToolTip.ShowOnDisabled="True"
312311
ToolTip.Tip="{Binding ImportTooltip}" />
313312

314313
<Button

StabilityMatrix.Avalonia/Views/PackageManager/MainPackageManagerView.axaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66
xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters"
77
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
88
xmlns:designData="clr-namespace:StabilityMatrix.Avalonia.DesignData"
9-
xmlns:generic="clr-namespace:System.Collections.Generic;assembly=System.Runtime"
109
xmlns:icons="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
1110
xmlns:input="clr-namespace:FluentAvalonia.UI.Input;assembly=FluentAvalonia"
1211
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
13-
xmlns:markupExtensions="clr-namespace:StabilityMatrix.Avalonia.MarkupExtensions"
1412
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
1513
xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core"
1614
xmlns:packageManager="clr-namespace:StabilityMatrix.Avalonia.ViewModels.PackageManager"
@@ -335,10 +333,10 @@
335333
<Button
336334
Margin="4,4,4,0"
337335
Padding="4"
338-
markupExtensions:ShowDisabledTooltipExtension.ShowOnDisabled="True"
339336
Classes="borderless-success"
340337
Command="{Binding Update}"
341-
IsEnabled="{Binding !IsRunning}">
338+
IsEnabled="{Binding !IsRunning}"
339+
ToolTip.ShowOnDisabled="True">
342340
<ToolTip.Tip>
343341
<TextBlock>
344342
<Run Text="{x:Static lang:Resources.Label_UpdateAvailable}" />

0 commit comments

Comments
 (0)