mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-30 20:33:40 +01:00
Better tex load cancellation handling
This commit is contained in:
parent
517abb0c71
commit
ba51ec52f5
3 changed files with 70 additions and 65 deletions
|
|
@ -148,6 +148,7 @@ internal abstract class SharableTexture : IRefCountable, TextureLoadThrottler.IT
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.cancellationTokenSource?.Cancel();
|
||||||
this.cancellationTokenSource = null;
|
this.cancellationTokenSource = null;
|
||||||
this.ReleaseResources();
|
this.ReleaseResources();
|
||||||
this.resourceReleased = true;
|
this.resourceReleased = true;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ namespace Dalamud.Interface.Internal.SharableTextures;
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class TextureLoadThrottler : IServiceType
|
internal class TextureLoadThrottler : IServiceType
|
||||||
{
|
{
|
||||||
private readonly List<WorkItem> workList = new();
|
private readonly object workListLock = new();
|
||||||
|
private readonly List<WorkItem> pendingWorkList = new();
|
||||||
private readonly List<WorkItem> activeWorkList = new();
|
private readonly List<WorkItem> activeWorkList = new();
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
|
|
@ -61,78 +62,82 @@ internal class TextureLoadThrottler : IServiceType
|
||||||
ImmediateLoadFunction = immediateLoadFunction,
|
ImmediateLoadFunction = immediateLoadFunction,
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = Task.Run(
|
_ = Task.Run(() => this.ContinueWork(work), default);
|
||||||
() =>
|
|
||||||
{
|
|
||||||
lock (this.workList)
|
|
||||||
{
|
|
||||||
this.workList.Add(work);
|
|
||||||
if (this.activeWorkList.Count >= this.MaxActiveWorkItems)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ContinueWork();
|
|
||||||
},
|
|
||||||
default);
|
|
||||||
|
|
||||||
return work.TaskCompletionSource.Task;
|
return work.TaskCompletionSource.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ContinueWork()
|
private async Task ContinueWork(WorkItem? newItem)
|
||||||
{
|
{
|
||||||
WorkItem minWork;
|
while (true)
|
||||||
lock (this.workList)
|
|
||||||
{
|
{
|
||||||
if (this.workList.Count == 0)
|
WorkItem? minWork = null;
|
||||||
return;
|
lock (this.workListLock)
|
||||||
|
|
||||||
if (this.activeWorkList.Count >= this.MaxActiveWorkItems)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var minIndex = 0;
|
|
||||||
for (var i = 1; i < this.workList.Count; i++)
|
|
||||||
{
|
{
|
||||||
if (this.workList[i].CompareTo(this.workList[minIndex]) < 0)
|
if (newItem is not null)
|
||||||
minIndex = i;
|
{
|
||||||
|
this.pendingWorkList.Add(newItem);
|
||||||
|
newItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.activeWorkList.Count >= this.MaxActiveWorkItems)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var minIndex = -1;
|
||||||
|
for (var i = 0; i < this.pendingWorkList.Count; i++)
|
||||||
|
{
|
||||||
|
var work = this.pendingWorkList[i];
|
||||||
|
if (work.CancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
work.TaskCompletionSource.SetCanceled(work.CancellationToken);
|
||||||
|
this.RelocatePendingWorkItemToEndAndEraseUnsafe(i--);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minIndex == -1 || work.CompareTo(this.pendingWorkList[minIndex]) < 0)
|
||||||
|
{
|
||||||
|
minIndex = i;
|
||||||
|
minWork = work;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minWork is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.RelocatePendingWorkItemToEndAndEraseUnsafe(minIndex);
|
||||||
|
|
||||||
|
this.activeWorkList.Add(minWork);
|
||||||
}
|
}
|
||||||
|
|
||||||
minWork = this.workList[minIndex];
|
try
|
||||||
// Avoid shifting; relocate the element to remove to the last
|
|
||||||
if (minIndex != this.workList.Count - 1)
|
|
||||||
(this.workList[^1], this.workList[minIndex]) = (this.workList[minIndex], this.workList[^1]);
|
|
||||||
this.workList.RemoveAt(this.workList.Count - 1);
|
|
||||||
|
|
||||||
this.activeWorkList.Add(minWork);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
minWork.CancellationToken.ThrowIfCancellationRequested();
|
|
||||||
minWork.InnerTask = minWork.ImmediateLoadFunction(minWork.CancellationToken);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
minWork.InnerTask = Task.FromException<IDalamudTextureWrap>(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
minWork.InnerTask.ContinueWith(
|
|
||||||
r =>
|
|
||||||
{
|
{
|
||||||
// Swallow exception, if any
|
var r = await minWork.ImmediateLoadFunction(minWork.CancellationToken);
|
||||||
_ = r.Exception;
|
minWork.TaskCompletionSource.SetResult(r);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
minWork.TaskCompletionSource.SetException(e);
|
||||||
|
}
|
||||||
|
|
||||||
lock (this.workList)
|
lock (this.workListLock)
|
||||||
this.activeWorkList.Remove(minWork);
|
this.activeWorkList.Remove(minWork);
|
||||||
if (r.IsCompletedSuccessfully)
|
}
|
||||||
minWork.TaskCompletionSource.SetResult(r.Result);
|
}
|
||||||
else if (r.Exception is not null)
|
|
||||||
minWork.TaskCompletionSource.SetException(r.Exception);
|
/// <summary>
|
||||||
else if (r.IsCanceled)
|
/// Remove an item in <see cref="pendingWorkList"/>, avoiding shifting.
|
||||||
minWork.TaskCompletionSource.SetCanceled();
|
/// </summary>
|
||||||
else
|
/// <param name="index">Index of the item to remove.</param>
|
||||||
minWork.TaskCompletionSource.SetException(new Exception("??"));
|
private void RelocatePendingWorkItemToEndAndEraseUnsafe(int index)
|
||||||
this.ContinueWork();
|
{
|
||||||
});
|
// Relocate the element to remove to the last.
|
||||||
|
if (index != this.pendingWorkList.Count - 1)
|
||||||
|
{
|
||||||
|
(this.pendingWorkList[^1], this.pendingWorkList[index]) =
|
||||||
|
(this.pendingWorkList[index], this.pendingWorkList[^1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pendingWorkList.RemoveAt(this.pendingWorkList.Count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -164,8 +169,6 @@ internal class TextureLoadThrottler : IServiceType
|
||||||
|
|
||||||
public required Func<CancellationToken, Task<IDalamudTextureWrap>> ImmediateLoadFunction { get; init; }
|
public required Func<CancellationToken, Task<IDalamudTextureWrap>> ImmediateLoadFunction { get; init; }
|
||||||
|
|
||||||
public Task<IDalamudTextureWrap>? InnerTask { get; set; }
|
|
||||||
|
|
||||||
public int CompareTo(WorkItem other)
|
public int CompareTo(WorkItem other)
|
||||||
{
|
{
|
||||||
if (this.Basis.IsOpportunistic != other.Basis.IsOpportunistic)
|
if (this.Basis.IsOpportunistic != other.Basis.IsOpportunistic)
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,9 @@ internal class TaskTracker : IDisposable, IServiceType
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a read-only list of tracked tasks.
|
/// Gets a read-only list of tracked tasks.
|
||||||
|
/// Intended for use only from UI thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IReadOnlyList<TaskInfo> Tasks => TrackedTasksInternal.ToArray();
|
public static IReadOnlyList<TaskInfo> Tasks => TrackedTasksInternal;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clear the list of tracked tasks.
|
/// Clear the list of tracked tasks.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue