using Godot;
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
///
/// Provides defer-like behavior that's easier to work with in C#
///
public partial class defer_manager : Node
{
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
}
private readonly ConcurrentQueue ActionQueue = new();
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
while (ActionQueue.TryDequeue(out Action a))
{
a?.Invoke();
}
}
public void DeferForget(Action action)
{
ActionQueue.Enqueue(action);
}
public async Task DeferAsync(Action action)
{
using SemaphoreSlim slim = new(0);
ActionQueue.Enqueue(() =>
{
action?.Invoke();
slim.Release();
}
);
await slim.WaitAsync();
}
public async Task DeferAsync(Func func)
{
if (func is null)
throw new Exception("Cannot defer null function");
using SemaphoreSlim slim = new(0);
T[] box = { default };
ActionQueue.Enqueue(() =>
{
box[0] = func.Invoke();
slim.Release();
}
);
await slim.WaitAsync();
return box[0];
}
public void Defer(Action action)
{
using SemaphoreSlim slim = new(0);
ActionQueue.Enqueue(() =>
{
action?.Invoke();
slim.Release();
}
);
slim.WaitAsync();
}
public T Defer(Func func)
{
if (func is null)
throw new Exception("Cannot defer null function");
using SemaphoreSlim slim = new(0);
T[] box = { default };
ActionQueue.Enqueue(() =>
{
box[0] = func.Invoke();
slim.Release();
}
);
slim.WaitAsync();
return box[0];
}
}