Skip to content

Commit

Permalink
Optimize tick task schedulers
Browse files Browse the repository at this point in the history
  • Loading branch information
FabianTerhorst committed Aug 1, 2020
1 parent 91decec commit 973beb3
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 29 deletions.
178 changes: 178 additions & 0 deletions api/AltV.Net.Async/ActionTickScheduler.cs
Original file line number Diff line number Diff line change
@@ -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<object> action;

private readonly object state;

public ActionContainer2(Action<object> action, object state)
{
this.action = action;
this.state = state;
}

public void Run()
{
action(state);
}
}

private readonly struct ActionContainer3<TResult>
{
private readonly Func<object, TResult> func;

private readonly object state;

private readonly TaskCompletionSource<TResult> result;

public ActionContainer3(Func<object, TResult> func, object state, TaskCompletionSource<TResult> result)
{
this.func = func;
this.state = state;
this.result = result;
}

public void Run()
{
result.SetResult(func(state));
}
}

private readonly struct ActionContainer4<TResult>
{
private readonly Func<TResult> func;

private readonly TaskCompletionSource<TResult> result;

public ActionContainer4(Func<TResult> func, TaskCompletionSource<TResult> result)
{
this.func = func;
this.result = result;
}

public void Run()
{
result.SetResult(func());
}
}

private readonly struct ActionContainer5
{
private readonly Action action;

private readonly TaskCompletionSource<bool> result;

public ActionContainer5(Action action, TaskCompletionSource<bool> result)
{
this.action = action;
this.result = result;
}

public void Run()
{
action();
result.SetResult(true);
}
}

private readonly struct ActionContainer6
{
private readonly Action<object> action;

private readonly object state;

private readonly TaskCompletionSource<bool> result;

public ActionContainer6(Action<object> action, object state,TaskCompletionSource<bool> result)
{
this.action = action;
this.state = state;
this.result = result;
}

public void Run()
{
action(state);
result.SetResult(true);
}
}

private int runs;

private readonly ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();

public ActionTickScheduler()
{
}

public void Schedule(Action action)
{
actions.Enqueue(new ActionContainer(action).Run);
}

public void Schedule(Action<object> action, object state)
{
actions.Enqueue(new ActionContainer2(action, state).Run);
}

public Task ScheduleTask(Action action)
{
var completionSource = new TaskCompletionSource<bool>();
actions.Enqueue(new ActionContainer5(action, completionSource).Run);
return completionSource.Task;
}

public Task ScheduleTask(Action<object> action, object state)
{
var completionSource = new TaskCompletionSource<bool>();
actions.Enqueue(new ActionContainer6(action, state, completionSource).Run);
return completionSource.Task;
}

public Task<TResult> ScheduleTask<TResult>(Func<TResult> action)
{
var completionSource = new TaskCompletionSource<TResult>();
actions.Enqueue(new ActionContainer4<TResult>(action, completionSource).Run);
return completionSource.Task;
}

public Task<TResult> ScheduleTask<TResult>(Func<object, TResult> action, object value)
{
var completionSource = new TaskCompletionSource<TResult>();
actions.Enqueue(new ActionContainer3<TResult>(action, value, completionSource).Run);
return completionSource.Task;
}

public void Tick()
{
runs = actions.Count;

while (runs-- > 0 && actions.TryDequeue(out var currentActionContainer))
{
currentActionContainer();
}
}
}
}
12 changes: 12 additions & 0 deletions api/AltV.Net.Async/ActionTickSchedulerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Threading;

namespace AltV.Net.Async
{
public class ActionTickSchedulerFactory : ITickSchedulerFactory
{
public ITickScheduler Create(Thread mainThread)
{
return new ActionTickScheduler();
}
}
}
14 changes: 13 additions & 1 deletion api/AltV.Net.Async/AltAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -200,5 +200,17 @@ public static Task<TResult> Do<TResult>(Func<object, TResult> action, object val
CheckIfAsyncResource();
return AltVAsync.Schedule(action, value);
}

public static void RunOnMainThread(Action action)
{
CheckIfAsyncResource();
AltVAsync.ScheduleNoneTask(action);
}

public static void RunOnMainThread(Action<object> action, object value)
{
CheckIfAsyncResource();
AltVAsync.ScheduleNoneTask(action, value);
}
}
}
41 changes: 29 additions & 12 deletions api/AltV.Net.Async/AltVAsync.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

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;

Expand All @@ -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;
}
Expand All @@ -51,9 +46,9 @@ internal Task Schedule(Action action)
}
}

return taskFactory.StartNew(action);
return scheduler.ScheduleTask(action);
}

internal Task Schedule(Action<object> action, object value)
{
if (Thread.CurrentThread == mainThread)
Expand All @@ -69,7 +64,7 @@ internal Task Schedule(Action<object> action, object value)
}
}

return taskFactory.StartNew(action, value);
return scheduler.ScheduleTask(action, value);
}

internal Task<TResult> Schedule<TResult>(Func<TResult> action)
Expand All @@ -86,9 +81,9 @@ internal Task<TResult> Schedule<TResult>(Func<TResult> action)
}
}

return taskFactory.StartNew(action);
return scheduler.ScheduleTask(action);
}

internal Task<TResult> Schedule<TResult>(Func<object, TResult> action, object value)
{
if (Thread.CurrentThread == mainThread)
Expand All @@ -103,7 +98,29 @@ internal Task<TResult> Schedule<TResult>(Func<object, TResult> 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<object> action, object value)
{
if (Thread.CurrentThread == mainThread)
{
action(value);
return;
}

scheduler.Schedule(action, value);
}
}
}
40 changes: 38 additions & 2 deletions api/AltV.Net.Async/ChannelTickScheduler.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;

namespace AltV.Net.Async
{
internal class ChannelTickScheduler : TickScheduler
internal class ChannelTickScheduler : TaskScheduler , ITickScheduler
{
private readonly Thread mainThread;

Expand All @@ -19,12 +20,17 @@ internal class ChannelTickScheduler : TickScheduler
private readonly ChannelReader<Task> reader;

private readonly ChannelWriter<Task> 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<Task> GetScheduledTasks() => null;
Expand All @@ -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<object> action, object state)
{
taskFactory.StartNew(action, state);
}

public Task ScheduleTask(Action action)
{
return taskFactory.StartNew(action);
}

public Task ScheduleTask(Action<object> action, object state)
{
return taskFactory.StartNew(action, state);
}

public Task<TResult> ScheduleTask<TResult>(Func<TResult> action)
{
return taskFactory.StartNew(action);
}

public Task<TResult> ScheduleTask<TResult>(Func<object, TResult> action, object value)
{
return taskFactory.StartNew(action, value);
}

public void Tick()
{
while (reader.TryRead(out currentTask))
{
Expand Down
2 changes: 1 addition & 1 deletion api/AltV.Net.Async/ChannelTickSchedulerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Loading

0 comments on commit 973beb3

Please sign in to comment.