mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
Hide scheduler from RunOnFrameworkThread (#1725)
* Hide scheduler from RunOnFrameworkThread Creating new tasks via Task.Run and alike would fetch the current scheduler, which we do not want in case of running stuff from the framework thread. Change is to prevent the standard library from seeing the "current scheduler". If one wants to use `await` with an async function to be run in the framework thread, one can use `RunOnFrameworkThreadAwaitable` instead now. * TaskSchedulerWidget: test better stuff * TaskSchedulerWidget: add freeze tests * More comments * Make TaskFactory a getter method instead of property to avoid bad suggestions * Why are there stuff still not pushed
This commit is contained in:
parent
c709ad1811
commit
5d473919a1
3 changed files with 310 additions and 22 deletions
|
|
@ -103,9 +103,6 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
/// <inheritdoc/>
|
||||
public DateTime LastUpdateUTC { get; private set; } = DateTime.MinValue;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TaskFactory FrameworkThreadTaskFactory { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TimeSpan UpdateDelta { get; private set; } = TimeSpan.Zero;
|
||||
|
||||
|
|
@ -125,6 +122,11 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
/// </summary>
|
||||
internal bool DispatchUpdateEvents { get; set; } = true;
|
||||
|
||||
private TaskFactory FrameworkThreadTaskFactory { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TaskFactory GetTaskFactory() => this.FrameworkThreadTaskFactory;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task DelayTicks(long numTicks, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
|
@ -138,6 +140,38 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task RunOnFrameworkThreadAwaitable(Action action, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (cancellationToken == default)
|
||||
cancellationToken = this.FrameworkThreadTaskFactory.CancellationToken;
|
||||
return this.FrameworkThreadTaskFactory.StartNew(action, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<T> RunOnFrameworkThreadAwaitable<T>(Func<T> action, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (cancellationToken == default)
|
||||
cancellationToken = this.FrameworkThreadTaskFactory.CancellationToken;
|
||||
return this.FrameworkThreadTaskFactory.StartNew(action, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task RunOnFrameworkThreadAwaitable(Func<Task> action, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (cancellationToken == default)
|
||||
cancellationToken = this.FrameworkThreadTaskFactory.CancellationToken;
|
||||
return this.FrameworkThreadTaskFactory.StartNew(action, cancellationToken).Unwrap();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<T> RunOnFrameworkThreadAwaitable<T>(Func<Task<T>> action, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (cancellationToken == default)
|
||||
cancellationToken = this.FrameworkThreadTaskFactory.CancellationToken;
|
||||
return this.FrameworkThreadTaskFactory.StartNew(action, cancellationToken).Unwrap();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<T> RunOnFrameworkThread<T>(Func<T> func) =>
|
||||
this.IsInFrameworkUpdateThread || this.IsFrameworkUnloading ? Task.FromResult(func()) : this.RunOnTick(func);
|
||||
|
|
@ -193,7 +227,9 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
this.DelayTicks(delayTicks, cancellationToken),
|
||||
},
|
||||
_ => func(),
|
||||
cancellationToken);
|
||||
cancellationToken,
|
||||
TaskContinuationOptions.HideScheduler,
|
||||
this.frameworkThreadTaskScheduler);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -218,7 +254,9 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
this.DelayTicks(delayTicks, cancellationToken),
|
||||
},
|
||||
_ => action(),
|
||||
cancellationToken);
|
||||
cancellationToken,
|
||||
TaskContinuationOptions.HideScheduler,
|
||||
this.frameworkThreadTaskScheduler);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -243,7 +281,9 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
this.DelayTicks(delayTicks, cancellationToken),
|
||||
},
|
||||
_ => func(),
|
||||
cancellationToken).Unwrap();
|
||||
cancellationToken,
|
||||
TaskContinuationOptions.HideScheduler,
|
||||
this.frameworkThreadTaskScheduler).Unwrap();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -268,7 +308,9 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
this.DelayTicks(delayTicks, cancellationToken),
|
||||
},
|
||||
_ => func(),
|
||||
cancellationToken).Unwrap();
|
||||
cancellationToken,
|
||||
TaskContinuationOptions.HideScheduler,
|
||||
this.frameworkThreadTaskScheduler).Unwrap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -491,9 +533,6 @@ internal class FrameworkPluginScoped : IInternalDisposableService, IFramework
|
|||
/// <inheritdoc/>
|
||||
public DateTime LastUpdateUTC => this.frameworkService.LastUpdateUTC;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TaskFactory FrameworkThreadTaskFactory => this.frameworkService.FrameworkThreadTaskFactory;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TimeSpan UpdateDelta => this.frameworkService.UpdateDelta;
|
||||
|
||||
|
|
@ -511,10 +550,29 @@ internal class FrameworkPluginScoped : IInternalDisposableService, IFramework
|
|||
this.Update = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TaskFactory GetTaskFactory() => this.frameworkService.GetTaskFactory();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task DelayTicks(long numTicks, CancellationToken cancellationToken = default) =>
|
||||
this.frameworkService.DelayTicks(numTicks, cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task RunOnFrameworkThreadAwaitable(Action action, CancellationToken cancellationToken = default) =>
|
||||
this.frameworkService.RunOnFrameworkThreadAwaitable(action, cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<T> RunOnFrameworkThreadAwaitable<T>(Func<T> action, CancellationToken cancellationToken = default) =>
|
||||
this.frameworkService.RunOnFrameworkThreadAwaitable(action, cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task RunOnFrameworkThreadAwaitable(Func<Task> action, CancellationToken cancellationToken = default) =>
|
||||
this.frameworkService.RunOnFrameworkThreadAwaitable(action, cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<T> RunOnFrameworkThreadAwaitable<T>(Func<Task<T>> action, CancellationToken cancellationToken = default) =>
|
||||
this.frameworkService.RunOnFrameworkThreadAwaitable(action, cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<T> RunOnFrameworkThread<T>(Func<T> func)
|
||||
=> this.frameworkService.RunOnFrameworkThread(func);
|
||||
|
|
|
|||
|
|
@ -144,9 +144,7 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 2s+60f"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(2), delayTicks: 60);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Every 60 frames"))
|
||||
if (ImGui.Button("Every 60f"))
|
||||
{
|
||||
_ = framework.RunOnTick(
|
||||
async () =>
|
||||
|
|
@ -154,6 +152,8 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
for (var i = 0L; ; i++)
|
||||
{
|
||||
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
|
||||
var it = i;
|
||||
_ = Task.Factory.StartNew(() => Log.Information($" => Sub #{it}; MainThread={ThreadSafety.IsMainThread}"));
|
||||
await framework.DelayTicks(60, this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
},
|
||||
|
|
@ -162,6 +162,68 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Every 1s"))
|
||||
{
|
||||
_ = framework.RunOnTick(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0L; ; i++)
|
||||
{
|
||||
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
|
||||
var it = i;
|
||||
_ = Task.Factory.StartNew(() => Log.Information($" => Sub #{it}; MainThread={ThreadSafety.IsMainThread}"));
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
},
|
||||
cancellationToken: this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Every 60f (Await)"))
|
||||
{
|
||||
_ = framework.RunOnFrameworkThreadAwaitable(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0L; ; i++)
|
||||
{
|
||||
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
|
||||
var it = i;
|
||||
_ = Task.Factory.StartNew(() => Log.Information($" => Sub #{it}; MainThread={ThreadSafety.IsMainThread}"));
|
||||
await framework.DelayTicks(60, this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
},
|
||||
this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Every 1s (Await)"))
|
||||
{
|
||||
_ = framework.RunOnFrameworkThreadAwaitable(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0L; ; i++)
|
||||
{
|
||||
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
|
||||
var it = i;
|
||||
_ = Task.Factory.StartNew(() => Log.Information($" => Sub #{it}; MainThread={ThreadSafety.IsMainThread}"));
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
},
|
||||
this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("As long as it's in Framework Thread"))
|
||||
{
|
||||
Task.Run(async () => await framework.RunOnFrameworkThread(() => { Log.Information("Task dispatched from non-framework.update thread"); }));
|
||||
framework.RunOnFrameworkThread(() => { Log.Information("Task dispatched from framework.update thread"); }).Wait();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Error in 1s"))
|
||||
{
|
||||
_ = framework.RunOnTick(() => throw new Exception("Test Exception"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1));
|
||||
|
|
@ -169,10 +231,18 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("As long as it's in Framework Thread"))
|
||||
if (ImGui.Button("Freeze 1s"))
|
||||
{
|
||||
Task.Run(async () => await framework.RunOnFrameworkThread(() => { Log.Information("Task dispatched from non-framework.update thread"); }));
|
||||
framework.RunOnFrameworkThread(() => { Log.Information("Task dispatched from framework.update thread"); }).Wait();
|
||||
_ = framework.RunOnFrameworkThread(() => Helper().Wait());
|
||||
static async Task Helper() => await Task.Delay(1000);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Freeze Completely"))
|
||||
{
|
||||
_ = framework.RunOnFrameworkThreadAwaitable(() => Helper().Wait());
|
||||
static async Task Helper() => await Task.Delay(1000);
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader("Download"))
|
||||
|
|
@ -217,7 +287,7 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
this.downloadState = default;
|
||||
var factory = downloadUsingGlobalScheduler
|
||||
? Task.Factory
|
||||
: framework.FrameworkThreadTaskFactory;
|
||||
: framework.GetTaskFactory();
|
||||
this.downloadState = default;
|
||||
this.downloadTask = factory.StartNew(
|
||||
async () =>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,29 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface.Internal.Windows.Data.Widgets;
|
||||
|
||||
namespace Dalamud.Plugin.Services;
|
||||
|
||||
/// <summary>
|
||||
/// This class represents the Framework of the native game client and grants access to various subsystems.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Choosing between <c>RunOnFrameworkThread</c> and <c>RunOnFrameworkThreadAwaitable</c></b></para>
|
||||
/// <ul>
|
||||
/// <li>If you do need to do use <c>await</c> and have your task keep executing on the main thread after waiting is
|
||||
/// done, use <c>RunOnFrameworkThreadAwaitable</c>.</li>
|
||||
/// <li>If you need to call <see cref="Task.Wait()"/> or <see cref="Task{TResult}.Result"/>, use
|
||||
/// <c>RunOnFrameworkThread</c>.</li>
|
||||
/// </ul>
|
||||
/// <para>The game is likely to completely lock up if you call above synchronous function and getter, because starting
|
||||
/// a new task by default runs on <see cref="TaskScheduler.Current"/>, which would make the task run on the framework
|
||||
/// thread if invoked via <c>RunOnFrameworkThreadAwaitable</c>. This includes <c>Task.Factory.StartNew</c> and
|
||||
/// <c>Task.ContinueWith</c>. Use <c>Task.Run</c> if you need to start a new task from the callback specified to
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, as it will force your task to be run in the default thread pool.</para>
|
||||
/// <para>See <see cref="TaskSchedulerWidget"/> to see the difference in behaviors, and how would a misuse of these
|
||||
/// functions result in a deadlock.</para>
|
||||
/// </remarks>
|
||||
public interface IFramework
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -29,11 +47,6 @@ public interface IFramework
|
|||
/// </summary>
|
||||
public DateTime LastUpdateUTC { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="TaskFactory"/> that runs tasks during Framework Update event.
|
||||
/// </summary>
|
||||
public TaskFactory FrameworkThreadTaskFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delta between the last Framework Update and the currently executing one.
|
||||
/// </summary>
|
||||
|
|
@ -49,20 +62,97 @@ public interface IFramework
|
|||
/// </summary>
|
||||
public bool IsFrameworkUnloading { get; }
|
||||
|
||||
/// <summary>Gets a <see cref="TaskFactory"/> that runs tasks during Framework Update event.</summary>
|
||||
/// <returns>The task factory.</returns>
|
||||
public TaskFactory GetTaskFactory();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a task that completes after the given number of ticks.
|
||||
/// </summary>
|
||||
/// <param name="numTicks">Number of ticks to delay.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A new <see cref="Task"/> that gets resolved after specified number of ticks happen.</returns>
|
||||
/// <remarks>The continuation will run on the framework thread by default.</remarks>
|
||||
public Task DelayTicks(long numTicks, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call.
|
||||
/// </summary>
|
||||
/// <param name="action">Function to call.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task representing the pending or already completed function.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Starting new tasks and waiting on them <b>synchronously</b> from this callback will completely lock up
|
||||
/// the game. Use <c>await</c> if you need to wait on something from an <c>async</c> callback.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task RunOnFrameworkThreadAwaitable(Action action, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return type.</typeparam>
|
||||
/// <param name="action">Function to call.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task representing the pending or already completed function.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Starting new tasks and waiting on them <b>synchronously</b> from this callback will completely lock up
|
||||
/// the game. Use <c>await</c> if you need to wait on something from an <c>async</c> callback.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task<T> RunOnFrameworkThreadAwaitable<T>(Func<T> action, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call.
|
||||
/// </summary>
|
||||
/// <param name="action">Function to call.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task representing the pending or already completed function.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Starting new tasks and waiting on them <b>synchronously</b> from this callback will completely lock up
|
||||
/// the game. Use <c>await</c> if you need to wait on something from an <c>async</c> callback.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task RunOnFrameworkThreadAwaitable(Func<Task> action, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return type.</typeparam>
|
||||
/// <param name="action">Function to call.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task representing the pending or already completed function.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Starting new tasks and waiting on them <b>synchronously</b> from this callback will completely lock up
|
||||
/// the game. Use <c>await</c> if you need to wait on something from an <c>async</c> callback.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task<T> RunOnFrameworkThreadAwaitable<T>(Func<Task<T>> action, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return type.</typeparam>
|
||||
/// <param name="func">Function to call.</param>
|
||||
/// <returns>Task representing the pending or already completed function.</returns>
|
||||
/// <remarks>
|
||||
/// <para><c>await</c>, <c>Task.Factory.StartNew</c> or alike will continue off the framework thread.</para>
|
||||
/// <para>Awaiting on the returned <see cref="Task"/> from <c>RunOnFrameworkThread</c>,
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, or <c>RunOnTick</c> right away inside the callback specified to this
|
||||
/// function has a chance of locking up the game. Do not do <c>await framework.RunOnFrameworkThread(...);</c>
|
||||
/// directly or indirectly from the delegate passed to this function.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task<T> RunOnFrameworkThread<T>(Func<T> func);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -70,6 +160,16 @@ public interface IFramework
|
|||
/// </summary>
|
||||
/// <param name="action">Function to call.</param>
|
||||
/// <returns>Task representing the pending or already completed function.</returns>
|
||||
/// <remarks>
|
||||
/// <para><c>await</c>, <c>Task.Factory.StartNew</c> or alike will continue off the framework thread.</para>
|
||||
/// <para>Awaiting on the returned <see cref="Task"/> from <c>RunOnFrameworkThread</c>,
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, or <c>RunOnTick</c> right away inside the callback specified to this
|
||||
/// function has a chance of locking up the game. Do not do <c>await framework.RunOnFrameworkThread(...);</c>
|
||||
/// directly or indirectly from the delegate passed to this function.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task RunOnFrameworkThread(Action action);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -78,6 +178,16 @@ public interface IFramework
|
|||
/// <typeparam name="T">Return type.</typeparam>
|
||||
/// <param name="func">Function to call.</param>
|
||||
/// <returns>Task representing the pending or already completed function.</returns>
|
||||
/// <remarks>
|
||||
/// <para><c>await</c>, <c>Task.Factory.StartNew</c> or alike will continue off the framework thread.</para>
|
||||
/// <para>Awaiting on the returned <see cref="Task"/> from <c>RunOnFrameworkThread</c>,
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, or <c>RunOnTick</c> right away inside the callback specified to this
|
||||
/// function has a chance of locking up the game. Do not do <c>await framework.RunOnFrameworkThread(...);</c>
|
||||
/// directly or indirectly from the delegate passed to this function.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
[Obsolete($"Use {nameof(RunOnTick)} instead.")]
|
||||
public Task<T> RunOnFrameworkThread<T>(Func<Task<T>> func);
|
||||
|
||||
|
|
@ -86,6 +196,16 @@ public interface IFramework
|
|||
/// </summary>
|
||||
/// <param name="func">Function to call.</param>
|
||||
/// <returns>Task representing the pending or already completed function.</returns>
|
||||
/// <remarks>
|
||||
/// <para><c>await</c>, <c>Task.Factory.StartNew</c> or alike will continue off the framework thread.</para>
|
||||
/// <para>Awaiting on the returned <see cref="Task"/> from <c>RunOnFrameworkThread</c>,
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, or <c>RunOnTick</c> right away inside the callback specified to this
|
||||
/// function has a chance of locking up the game. Do not do <c>await framework.RunOnFrameworkThread(...);</c>
|
||||
/// directly or indirectly from the delegate passed to this function.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
[Obsolete($"Use {nameof(RunOnTick)} instead.")]
|
||||
public Task RunOnFrameworkThread(Func<Task> func);
|
||||
|
||||
|
|
@ -98,6 +218,16 @@ public interface IFramework
|
|||
/// <param name="delayTicks">Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter.</param>
|
||||
/// <param name="cancellationToken">Cancellation token which will prevent the execution of this function if wait conditions are not met.</param>
|
||||
/// <returns>Task representing the pending function.</returns>
|
||||
/// <remarks>
|
||||
/// <para><c>await</c>, <c>Task.Factory.StartNew</c> or alike will continue off the framework thread.</para>
|
||||
/// <para>Awaiting on the returned <see cref="Task"/> from <c>RunOnFrameworkThread</c>,
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, or <c>RunOnTick</c> right away inside the callback specified to this
|
||||
/// function has a chance of locking up the game. Do not do <c>await framework.RunOnFrameworkThread(...);</c>
|
||||
/// directly or indirectly from the delegate passed to this function.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task<T> RunOnTick<T>(Func<T> func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -108,6 +238,16 @@ public interface IFramework
|
|||
/// <param name="delayTicks">Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter.</param>
|
||||
/// <param name="cancellationToken">Cancellation token which will prevent the execution of this function if wait conditions are not met.</param>
|
||||
/// <returns>Task representing the pending function.</returns>
|
||||
/// <remarks>
|
||||
/// <para><c>await</c>, <c>Task.Factory.StartNew</c> or alike will continue off the framework thread.</para>
|
||||
/// <para>Awaiting on the returned <see cref="Task"/> from <c>RunOnFrameworkThread</c>,
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, or <c>RunOnTick</c> right away inside the callback specified to this
|
||||
/// function has a chance of locking up the game. Do not do <c>await framework.RunOnFrameworkThread(...);</c>
|
||||
/// directly or indirectly from the delegate passed to this function.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task RunOnTick(Action action, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -119,6 +259,16 @@ public interface IFramework
|
|||
/// <param name="delayTicks">Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter.</param>
|
||||
/// <param name="cancellationToken">Cancellation token which will prevent the execution of this function if wait conditions are not met.</param>
|
||||
/// <returns>Task representing the pending function.</returns>
|
||||
/// <remarks>
|
||||
/// <para><c>await</c>, <c>Task.Factory.StartNew</c> or alike will continue off the framework thread.</para>
|
||||
/// <para>Awaiting on the returned <see cref="Task"/> from <c>RunOnFrameworkThread</c>,
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, or <c>RunOnTick</c> right away inside the callback specified to this
|
||||
/// function has a chance of locking up the game. Do not do <c>await framework.RunOnFrameworkThread(...);</c>
|
||||
/// directly or indirectly from the delegate passed to this function.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task<T> RunOnTick<T>(Func<Task<T>> func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -129,5 +279,15 @@ public interface IFramework
|
|||
/// <param name="delayTicks">Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter.</param>
|
||||
/// <param name="cancellationToken">Cancellation token which will prevent the execution of this function if wait conditions are not met.</param>
|
||||
/// <returns>Task representing the pending function.</returns>
|
||||
/// <remarks>
|
||||
/// <para><c>await</c>, <c>Task.Factory.StartNew</c> or alike will continue off the framework thread.</para>
|
||||
/// <para>Awaiting on the returned <see cref="Task"/> from <c>RunOnFrameworkThread</c>,
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c>, or <c>RunOnTick</c> right away inside the callback specified to this
|
||||
/// function has a chance of locking up the game. Do not do <c>await framework.RunOnFrameworkThread(...);</c>
|
||||
/// directly or indirectly from the delegate passed to this function.</para>
|
||||
/// <para>See the remarks on <see cref="IFramework"/> if you need to choose which one to use, between
|
||||
/// <c>RunOnFrameworkThreadAwaitable</c> and <c>RunOnFrameworkThread</c>. Note that <c>RunOnTick</c> is a fancy
|
||||
/// version of <c>RunOnFrameworkThread</c>.</para>
|
||||
/// </remarks>
|
||||
public Task RunOnTick(Func<Task> func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue