Merge remote-tracking branch 'upstream/master' into feature/inotificationmanager

This commit is contained in:
Soreepeong 2024-03-14 13:06:04 +09:00
commit 033a57d19d
51 changed files with 3594 additions and 1284 deletions

View file

@ -1,6 +1,7 @@
using System.Linq;
using Dalamud.Game;
using Dalamud.Game.Gui.ContextMenu;
using Dalamud.Plugin.Services;
using Serilog;
@ -99,6 +100,23 @@ internal static class EventHandlerExtensions
}
}
/// <summary>
/// Replacement for Invoke() on OnMenuOpenedDelegate to catch exceptions that stop event propagation in case
/// of a thrown Exception inside of an invocation.
/// </summary>
/// <param name="openedDelegate">The OnMenuOpenedDelegate in question.</param>
/// <param name="argument">Templated argument for Action.</param>
public static void InvokeSafely(this IContextMenu.OnMenuOpenedDelegate? openedDelegate, MenuOpenedArgs argument)
{
if (openedDelegate == null)
return;
foreach (var action in openedDelegate.GetInvocationList().Cast<IContextMenu.OnMenuOpenedDelegate>())
{
HandleInvoke(() => action(argument));
}
}
private static void HandleInvoke(Action act)
{
try

View file

@ -0,0 +1,90 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Dalamud.Utility;
/// <summary>
/// A task scheduler that runs tasks on a specific thread.
/// </summary>
internal class ThreadBoundTaskScheduler : TaskScheduler
{
private const byte Scheduled = 0;
private const byte Running = 1;
private readonly ConcurrentDictionary<Task, byte> scheduledTasks = new();
/// <summary>
/// Initializes a new instance of the <see cref="ThreadBoundTaskScheduler"/> class.
/// </summary>
/// <param name="boundThread">The thread to bind this task scheduelr to.</param>
public ThreadBoundTaskScheduler(Thread? boundThread = null)
{
this.BoundThread = boundThread;
}
/// <summary>
/// Gets or sets the thread this task scheduler is bound to.
/// </summary>
public Thread? BoundThread { get; set; }
/// <summary>
/// Gets a value indicating whether we're on the bound thread.
/// </summary>
public bool IsOnBoundThread => Thread.CurrentThread == this.BoundThread;
/// <summary>
/// Runs queued tasks.
/// </summary>
public void Run()
{
foreach (var task in this.scheduledTasks.Keys)
{
if (!this.scheduledTasks.TryUpdate(task, Running, Scheduled))
continue;
_ = this.TryExecuteTask(task);
}
}
/// <inheritdoc/>
protected override IEnumerable<Task> GetScheduledTasks()
{
return this.scheduledTasks.Keys;
}
/// <inheritdoc/>
protected override void QueueTask(Task task)
{
this.scheduledTasks[task] = Scheduled;
}
/// <inheritdoc/>
protected override bool TryDequeue(Task task)
{
if (!this.scheduledTasks.TryRemove(task, out _))
return false;
return true;
}
/// <inheritdoc/>
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
if (!this.IsOnBoundThread)
return false;
if (taskWasPreviouslyQueued && !this.scheduledTasks.TryUpdate(task, Running, Scheduled))
return false;
_ = this.TryExecuteTask(task);
return true;
}
private new bool TryExecuteTask(Task task)
{
var r = base.TryExecuteTask(task);
this.scheduledTasks.Remove(task, out _);
return r;
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
namespace Dalamud.Utility;
@ -19,6 +20,7 @@ public static class ThreadSafety
/// Throws an exception when the current thread is not the main thread.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when the current thread is not the main thread.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertMainThread()
{
if (!threadStaticIsMainThread)
@ -31,6 +33,7 @@ public static class ThreadSafety
/// Throws an exception when the current thread is the main thread.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when the current thread is the main thread.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertNotMainThread()
{
if (threadStaticIsMainThread)
@ -39,6 +42,15 @@ public static class ThreadSafety
}
}
/// <summary><see cref="AssertMainThread"/>, but only on debug compilation mode.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DebugAssertMainThread()
{
#if DEBUG
AssertMainThread();
#endif
}
/// <summary>
/// Marks a thread as the main thread.
/// </summary>