overhaul the coroutine system to avoid boxing

This commit is contained in:
Ellpeck 2020-06-13 02:58:54 +02:00
parent ae6e4924ec
commit b4874dd365
7 changed files with 75 additions and 101 deletions

View file

@ -4,12 +4,14 @@ using System.Collections.Generic;
namespace Coroutine {
public class ActiveCoroutine {
private readonly IEnumerator<IWait> enumerator;
private readonly IEnumerator<Wait> enumerator;
private Wait current;
public bool IsFinished { get; private set; }
public bool WasCanceled { get; private set; }
public FinishCallback OnFinished;
internal ActiveCoroutine(IEnumerator<IWait> enumerator) {
internal ActiveCoroutine(IEnumerator<Wait> enumerator) {
this.enumerator = enumerator;
}
@ -24,8 +26,7 @@ namespace Coroutine {
internal bool Tick(double deltaSeconds) {
if (!this.WasCanceled) {
var curr = this.enumerator.Current;
if (curr != null && curr.Tick(deltaSeconds))
if (this.current.Tick(deltaSeconds))
this.MoveNext();
}
return this.IsFinished;
@ -33,22 +34,24 @@ namespace Coroutine {
internal bool OnEvent(Event evt) {
if (!this.WasCanceled) {
var curr = this.enumerator.Current;
if (curr != null && curr.OnEvent(evt))
if (this.current.OnEvent(evt))
this.MoveNext();
}
return this.IsFinished;
}
private void MoveNext() {
internal bool MoveNext() {
if (!this.enumerator.MoveNext()) {
this.IsFinished = true;
this.OnFinished?.Invoke(this);
return false;
}
this.current = this.enumerator.Current;
return true;
}
internal WaitType GetCurrentType() {
return this.enumerator.Current.GetWaitType();
internal bool IsWaitingForEvent() {
return this.current.IsWaitingForEvent();
}
public delegate void FinishCallback(ActiveCoroutine coroutine);

View file

@ -8,19 +8,23 @@ namespace Coroutine {
private static readonly List<ActiveCoroutine> TickingCoroutines = new List<ActiveCoroutine>();
private static readonly List<ActiveCoroutine> EventCoroutines = new List<ActiveCoroutine>();
public static ActiveCoroutine Start(IEnumerator<IWait> coroutine) {
if (!coroutine.MoveNext())
return null;
public static ActiveCoroutine Start(IEnumerable<Wait> coroutine) {
return Start(coroutine.GetEnumerator());
}
public static ActiveCoroutine Start(IEnumerator<Wait> coroutine) {
var inst = new ActiveCoroutine(coroutine);
var type = inst.GetCurrentType();
if (type == WaitType.Tick)
TickingCoroutines.Add(inst);
else if (type == WaitType.Event)
EventCoroutines.Add(inst);
if (inst.MoveNext()) {
if (inst.IsWaitingForEvent()) {
EventCoroutines.Add(inst);
} else {
TickingCoroutines.Add(inst);
}
}
return inst;
}
public static void InvokeLater(IWait wait, Action action) {
public static void InvokeLater(Wait wait, Action action) {
Start(InvokeLaterImpl(wait, action));
}
@ -29,7 +33,7 @@ namespace Coroutine {
var coroutine = TickingCoroutines[i];
if (coroutine.Tick(deltaSeconds)) {
TickingCoroutines.RemoveAt(i);
} else if (coroutine.GetCurrentType() != WaitType.Tick) {
} else if (coroutine.IsWaitingForEvent()) {
TickingCoroutines.RemoveAt(i);
EventCoroutines.Add(coroutine);
}
@ -41,7 +45,7 @@ namespace Coroutine {
var coroutine = EventCoroutines[i];
if (coroutine.OnEvent(evt)) {
EventCoroutines.RemoveAt(i);
} else if (coroutine.GetCurrentType() != WaitType.Event) {
} else if (!coroutine.IsWaitingForEvent()) {
EventCoroutines.RemoveAt(i);
TickingCoroutines.Add(coroutine);
}
@ -52,7 +56,7 @@ namespace Coroutine {
return TickingCoroutines.Concat(EventCoroutines);
}
private static IEnumerator<IWait> InvokeLaterImpl(IWait wait, Action action) {
private static IEnumerator<Wait> InvokeLaterImpl(Wait wait, Action action) {
yield return wait;
action();
}

View file

@ -1,18 +0,0 @@
namespace Coroutine {
public interface IWait {
WaitType GetWaitType();
bool Tick(double deltaSeconds);
bool OnEvent(Event evt);
}
public enum WaitType {
Tick,
Event
}
}

36
Coroutine/Wait.cs Normal file
View file

@ -0,0 +1,36 @@
using System;
namespace Coroutine {
public struct Wait {
private readonly Event evt;
private double seconds;
public Wait(Event evt) {
this.evt = evt;
this.seconds = 0;
}
public Wait(double seconds) {
this.seconds = seconds;
this.evt = null;
}
public Wait(TimeSpan time) : this(time.TotalSeconds) {
}
internal bool Tick(double deltaSeconds) {
this.seconds -= deltaSeconds;
return this.seconds <= 0;
}
internal bool OnEvent(Event evt) {
return evt == this.evt;
}
internal bool IsWaitingForEvent() {
return this.evt != null;
}
}
}

View file

@ -1,25 +0,0 @@
using System;
namespace Coroutine {
public struct WaitEvent : IWait {
private readonly Event evt;
public WaitEvent(Event evt) {
this.evt = evt;
}
public WaitType GetWaitType() {
return WaitType.Event;
}
public bool Tick(double deltaSeconds) {
throw new NotSupportedException();
}
public bool OnEvent(Event evt) {
return evt == this.evt;
}
}
}

View file

@ -1,26 +0,0 @@
using System;
namespace Coroutine {
public struct WaitSeconds : IWait {
private double seconds;
public WaitSeconds(double seconds) {
this.seconds = seconds;
}
public WaitType GetWaitType() {
return WaitType.Tick;
}
public bool Tick(double deltaSeconds) {
this.seconds -= deltaSeconds;
return this.seconds <= 0;
}
public bool OnEvent(Event evt) {
throw new NotSupportedException();
}
}
}

View file

@ -14,11 +14,11 @@ namespace Test {
CoroutineHandler.Start(EmptyCoroutine());
CoroutineHandler.InvokeLater(new WaitSeconds(10), () => {
CoroutineHandler.InvokeLater(new Wait(10), () => {
Console.WriteLine("Raising test event");
CoroutineHandler.RaiseEvent(TestEvent);
});
CoroutineHandler.InvokeLater(new WaitEvent(TestEvent), () => Console.WriteLine("Test event received"));
CoroutineHandler.InvokeLater(new Wait(TestEvent), () => Console.WriteLine("Test event received"));
var lastTime = DateTime.Now;
while (true) {
@ -29,24 +29,24 @@ namespace Test {
}
}
private static IEnumerator<IWait> WaitSeconds() {
private static IEnumerator<Wait> WaitSeconds() {
Console.WriteLine("First thing " + DateTime.Now);
yield return new WaitSeconds(1);
yield return new Wait(1);
Console.WriteLine("After 1 second " + DateTime.Now);
yield return new WaitSeconds(9);
yield return new Wait(9);
Console.WriteLine("After 10 seconds " + DateTime.Now);
yield return new WaitSeconds(5);
yield return new Wait(5);
Console.WriteLine("After 5 more seconds " + DateTime.Now);
yield return new WaitSeconds(10);
yield return new Wait(10);
Console.WriteLine("After 10 more seconds " + DateTime.Now);
yield return new WaitSeconds(20);
yield return new Wait(20);
Console.WriteLine("First coroutine done");
}
private static IEnumerator<IWait> PrintEvery10Seconds(ActiveCoroutine first) {
private static IEnumerator<Wait> PrintEvery10Seconds(ActiveCoroutine first) {
while (true) {
yield return new WaitSeconds(10);
yield return new Wait(10);
Console.WriteLine("The time is " + DateTime.Now);
if (first.IsFinished) {
Console.WriteLine("By the way, the first coroutine has finished!");
@ -55,7 +55,7 @@ namespace Test {
}
}
private static IEnumerator<IWait> EmptyCoroutine() {
private static IEnumerator<Wait> EmptyCoroutine() {
yield break;
}