From 973beb3ba9c1c55ce35d997b6697ee51e07fb6cf Mon Sep 17 00:00:00 2001 From: fabian Date: Sat, 1 Aug 2020 16:05:41 +0200 Subject: [PATCH] Optimize tick task schedulers --- api/AltV.Net.Async/ActionTickScheduler.cs | 178 ++++++++++++++++++ .../ActionTickSchedulerFactory.cs | 12 ++ api/AltV.Net.Async/AltAsync.cs | 14 +- api/AltV.Net.Async/AltVAsync.cs | 41 ++-- api/AltV.Net.Async/ChannelTickScheduler.cs | 40 +++- .../ChannelTickSchedulerFactory.cs | 2 +- .../DefaultTickSchedulerFactory.cs | 2 +- api/AltV.Net.Async/ITickScheduler.cs | 22 +++ api/AltV.Net.Async/ITickSchedulerFactory.cs | 2 +- api/AltV.Net.Async/QueueTickScheduler.cs | 40 +++- api/AltV.Net.Async/TickScheduler.cs | 9 - 11 files changed, 333 insertions(+), 29 deletions(-) create mode 100644 api/AltV.Net.Async/ActionTickScheduler.cs create mode 100644 api/AltV.Net.Async/ActionTickSchedulerFactory.cs create mode 100644 api/AltV.Net.Async/ITickScheduler.cs delete mode 100644 api/AltV.Net.Async/TickScheduler.cs diff --git a/api/AltV.Net.Async/ActionTickScheduler.cs b/api/AltV.Net.Async/ActionTickScheduler.cs new file mode 100644 index 000000000..3183755c1 --- /dev/null +++ b/api/AltV.Net.Async/ActionTickScheduler.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; + +namespace AltV.Net.Async +{ + internal class ActionTickScheduler : ITickScheduler + { + private readonly struct ActionContainer + { + private readonly Action action; + + public ActionContainer(Action action) + { + this.action = action; + } + + public void Run() + { + action(); + } + } + + private readonly struct ActionContainer2 + { + private readonly Action action; + + private readonly object state; + + public ActionContainer2(Action action, object state) + { + this.action = action; + this.state = state; + } + + public void Run() + { + action(state); + } + } + + private readonly struct ActionContainer3 + { + private readonly Func func; + + private readonly object state; + + private readonly TaskCompletionSource result; + + public ActionContainer3(Func func, object state, TaskCompletionSource result) + { + this.func = func; + this.state = state; + this.result = result; + } + + public void Run() + { + result.SetResult(func(state)); + } + } + + private readonly struct ActionContainer4 + { + private readonly Func func; + + private readonly TaskCompletionSource result; + + public ActionContainer4(Func func, TaskCompletionSource result) + { + this.func = func; + this.result = result; + } + + public void Run() + { + result.SetResult(func()); + } + } + + private readonly struct ActionContainer5 + { + private readonly Action action; + + private readonly TaskCompletionSource result; + + public ActionContainer5(Action action, TaskCompletionSource result) + { + this.action = action; + this.result = result; + } + + public void Run() + { + action(); + result.SetResult(true); + } + } + + private readonly struct ActionContainer6 + { + private readonly Action action; + + private readonly object state; + + private readonly TaskCompletionSource result; + + public ActionContainer6(Action action, object state,TaskCompletionSource result) + { + this.action = action; + this.state = state; + this.result = result; + } + + public void Run() + { + action(state); + result.SetResult(true); + } + } + + private int runs; + + private readonly ConcurrentQueue actions = new ConcurrentQueue(); + + public ActionTickScheduler() + { + } + + public void Schedule(Action action) + { + actions.Enqueue(new ActionContainer(action).Run); + } + + public void Schedule(Action action, object state) + { + actions.Enqueue(new ActionContainer2(action, state).Run); + } + + public Task ScheduleTask(Action action) + { + var completionSource = new TaskCompletionSource(); + actions.Enqueue(new ActionContainer5(action, completionSource).Run); + return completionSource.Task; + } + + public Task ScheduleTask(Action action, object state) + { + var completionSource = new TaskCompletionSource(); + actions.Enqueue(new ActionContainer6(action, state, completionSource).Run); + return completionSource.Task; + } + + public Task ScheduleTask(Func action) + { + var completionSource = new TaskCompletionSource(); + actions.Enqueue(new ActionContainer4(action, completionSource).Run); + return completionSource.Task; + } + + public Task ScheduleTask(Func action, object value) + { + var completionSource = new TaskCompletionSource(); + actions.Enqueue(new ActionContainer3(action, value, completionSource).Run); + return completionSource.Task; + } + + public void Tick() + { + runs = actions.Count; + + while (runs-- > 0 && actions.TryDequeue(out var currentActionContainer)) + { + currentActionContainer(); + } + } + } +} \ No newline at end of file diff --git a/api/AltV.Net.Async/ActionTickSchedulerFactory.cs b/api/AltV.Net.Async/ActionTickSchedulerFactory.cs new file mode 100644 index 000000000..d4f437c1f --- /dev/null +++ b/api/AltV.Net.Async/ActionTickSchedulerFactory.cs @@ -0,0 +1,12 @@ +using System.Threading; + +namespace AltV.Net.Async +{ + public class ActionTickSchedulerFactory : ITickSchedulerFactory + { + public ITickScheduler Create(Thread mainThread) + { + return new ActionTickScheduler(); + } + } +} \ No newline at end of file diff --git a/api/AltV.Net.Async/AltAsync.cs b/api/AltV.Net.Async/AltAsync.cs index ac4494784..034634014 100644 --- a/api/AltV.Net.Async/AltAsync.cs +++ b/api/AltV.Net.Async/AltAsync.cs @@ -115,7 +115,7 @@ public static event ColShapeAsyncDelegate OnColShape add => Module.ColShapeAsyncDelegateHandlers.Add(value); remove => Module.ColShapeAsyncDelegateHandlers.Remove(value); } - + public static event VehicleDestroyAsyncDelegate OnVehicleDestroy { add => Module.VehicleDestroyAsyncDelegateHandlers.Add(value); @@ -200,5 +200,17 @@ public static Task Do(Func action, object val CheckIfAsyncResource(); return AltVAsync.Schedule(action, value); } + + public static void RunOnMainThread(Action action) + { + CheckIfAsyncResource(); + AltVAsync.ScheduleNoneTask(action); + } + + public static void RunOnMainThread(Action action, object value) + { + CheckIfAsyncResource(); + AltVAsync.ScheduleNoneTask(action, value); + } } } \ No newline at end of file diff --git a/api/AltV.Net.Async/AltVAsync.cs b/api/AltV.Net.Async/AltVAsync.cs index 232cf862a..7f160d295 100644 --- a/api/AltV.Net.Async/AltVAsync.cs +++ b/api/AltV.Net.Async/AltVAsync.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; @@ -7,8 +6,7 @@ namespace AltV.Net.Async { internal class AltVAsync { - private readonly TaskFactory taskFactory; - private readonly TickScheduler scheduler; + private readonly ITickScheduler scheduler; private readonly Thread mainThread; internal Thread TickThread; @@ -23,9 +21,6 @@ public AltVAsync(ITickSchedulerFactory tickSchedulerFactory) } scheduler = tickSchedulerFactory.Create(mainThread); - taskFactory = new TaskFactory( - CancellationToken.None, TaskCreationOptions.DenyChildAttach, - TaskContinuationOptions.None, scheduler); AltAsync.Setup(this); TickDelegate = FirstTick; } @@ -51,9 +46,9 @@ internal Task Schedule(Action action) } } - return taskFactory.StartNew(action); + return scheduler.ScheduleTask(action); } - + internal Task Schedule(Action action, object value) { if (Thread.CurrentThread == mainThread) @@ -69,7 +64,7 @@ internal Task Schedule(Action action, object value) } } - return taskFactory.StartNew(action, value); + return scheduler.ScheduleTask(action, value); } internal Task Schedule(Func action) @@ -86,9 +81,9 @@ internal Task Schedule(Func action) } } - return taskFactory.StartNew(action); + return scheduler.ScheduleTask(action); } - + internal Task Schedule(Func action, object value) { if (Thread.CurrentThread == mainThread) @@ -103,7 +98,29 @@ internal Task Schedule(Func action, object va } } - return taskFactory.StartNew(action, value); + return scheduler.ScheduleTask(action, value); + } + + internal void ScheduleNoneTask(Action action) + { + if (Thread.CurrentThread == mainThread) + { + action(); + return; + } + + scheduler.Schedule(action); + } + + internal void ScheduleNoneTask(Action action, object value) + { + if (Thread.CurrentThread == mainThread) + { + action(value); + return; + } + + scheduler.Schedule(action, value); } } } \ No newline at end of file diff --git a/api/AltV.Net.Async/ChannelTickScheduler.cs b/api/AltV.Net.Async/ChannelTickScheduler.cs index 01f454120..802441d1e 100644 --- a/api/AltV.Net.Async/ChannelTickScheduler.cs +++ b/api/AltV.Net.Async/ChannelTickScheduler.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Channels; @@ -5,7 +6,7 @@ namespace AltV.Net.Async { - internal class ChannelTickScheduler : TickScheduler + internal class ChannelTickScheduler : TaskScheduler , ITickScheduler { private readonly Thread mainThread; @@ -19,12 +20,17 @@ internal class ChannelTickScheduler : TickScheduler private readonly ChannelReader reader; private readonly ChannelWriter writer; + + private readonly TaskFactory taskFactory; public ChannelTickScheduler(Thread mainThread) { this.mainThread = mainThread; reader = tasks.Reader; writer = tasks.Writer; + taskFactory = new TaskFactory( + CancellationToken.None, TaskCreationOptions.DenyChildAttach, + TaskContinuationOptions.None, this); } protected override IEnumerable GetScheduledTasks() => null; @@ -34,7 +40,37 @@ public ChannelTickScheduler(Thread mainThread) protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => Thread.CurrentThread == mainThread && TryExecuteTask(task); - public override void Tick() + public void Schedule(Action action) + { + taskFactory.StartNew(action); + } + + public void Schedule(Action action, object state) + { + taskFactory.StartNew(action, state); + } + + public Task ScheduleTask(Action action) + { + return taskFactory.StartNew(action); + } + + public Task ScheduleTask(Action action, object state) + { + return taskFactory.StartNew(action, state); + } + + public Task ScheduleTask(Func action) + { + return taskFactory.StartNew(action); + } + + public Task ScheduleTask(Func action, object value) + { + return taskFactory.StartNew(action, value); + } + + public void Tick() { while (reader.TryRead(out currentTask)) { diff --git a/api/AltV.Net.Async/ChannelTickSchedulerFactory.cs b/api/AltV.Net.Async/ChannelTickSchedulerFactory.cs index 5345a757e..942b65a5f 100644 --- a/api/AltV.Net.Async/ChannelTickSchedulerFactory.cs +++ b/api/AltV.Net.Async/ChannelTickSchedulerFactory.cs @@ -4,7 +4,7 @@ namespace AltV.Net.Async { public class ChannelTickSchedulerFactory : ITickSchedulerFactory { - public TickScheduler Create(Thread mainThread) + public ITickScheduler Create(Thread mainThread) { return new ChannelTickScheduler(mainThread); } diff --git a/api/AltV.Net.Async/DefaultTickSchedulerFactory.cs b/api/AltV.Net.Async/DefaultTickSchedulerFactory.cs index dc863a87e..2c461a78a 100644 --- a/api/AltV.Net.Async/DefaultTickSchedulerFactory.cs +++ b/api/AltV.Net.Async/DefaultTickSchedulerFactory.cs @@ -4,7 +4,7 @@ namespace AltV.Net.Async { public class DefaultTickSchedulerFactory : ITickSchedulerFactory { - public TickScheduler Create(Thread mainThread) + public ITickScheduler Create(Thread mainThread) { return new QueueTickScheduler(mainThread); } diff --git a/api/AltV.Net.Async/ITickScheduler.cs b/api/AltV.Net.Async/ITickScheduler.cs new file mode 100644 index 000000000..3db130275 --- /dev/null +++ b/api/AltV.Net.Async/ITickScheduler.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; + +namespace AltV.Net.Async +{ + public interface ITickScheduler + { + void Tick(); + + Task ScheduleTask(Action action, object state); + + Task ScheduleTask(Action action); + + Task ScheduleTask(Func action, object value); + + Task ScheduleTask(Func action); + + void Schedule(Action action); + + void Schedule(Action action, object state); + } +} \ No newline at end of file diff --git a/api/AltV.Net.Async/ITickSchedulerFactory.cs b/api/AltV.Net.Async/ITickSchedulerFactory.cs index 6a15e1a69..31906c3c2 100644 --- a/api/AltV.Net.Async/ITickSchedulerFactory.cs +++ b/api/AltV.Net.Async/ITickSchedulerFactory.cs @@ -4,6 +4,6 @@ namespace AltV.Net.Async { public interface ITickSchedulerFactory { - TickScheduler Create(Thread mainThread); + ITickScheduler Create(Thread mainThread); } } \ No newline at end of file diff --git a/api/AltV.Net.Async/QueueTickScheduler.cs b/api/AltV.Net.Async/QueueTickScheduler.cs index 921cd1c12..8ac88203e 100644 --- a/api/AltV.Net.Async/QueueTickScheduler.cs +++ b/api/AltV.Net.Async/QueueTickScheduler.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; @@ -5,7 +6,7 @@ namespace AltV.Net.Async { - internal class QueueTickScheduler : TickScheduler + internal class QueueTickScheduler : TaskScheduler, ITickScheduler { private readonly Thread mainThread; @@ -17,9 +18,14 @@ internal class QueueTickScheduler : TickScheduler private int runs; + private readonly TaskFactory taskFactory; + public QueueTickScheduler(Thread mainThread) { this.mainThread = mainThread; + taskFactory = new TaskFactory( + CancellationToken.None, TaskCreationOptions.DenyChildAttach, + TaskContinuationOptions.None, this); } protected override IEnumerable GetScheduledTasks() => null; @@ -29,7 +35,37 @@ public QueueTickScheduler(Thread mainThread) protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => Thread.CurrentThread == mainThread && TryExecuteTask(task); - public override void Tick() + public void Schedule(Action action) + { + taskFactory.StartNew(action); + } + + public void Schedule(Action action, object state) + { + taskFactory.StartNew(action, state); + } + + public Task ScheduleTask(Action action) + { + return taskFactory.StartNew(action); + } + + public Task ScheduleTask(Action action, object state) + { + return taskFactory.StartNew(action, state); + } + + public Task ScheduleTask(Func action) + { + return taskFactory.StartNew(action); + } + + public Task ScheduleTask(Func action, object value) + { + return taskFactory.StartNew(action, value); + } + + public void Tick() { runs = tasks.Count; diff --git a/api/AltV.Net.Async/TickScheduler.cs b/api/AltV.Net.Async/TickScheduler.cs deleted file mode 100644 index 86ef2fcc3..000000000 --- a/api/AltV.Net.Async/TickScheduler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace AltV.Net.Async -{ - public abstract class TickScheduler : TaskScheduler - { - public abstract void Tick(); - } -} \ No newline at end of file