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 { namespace Coroutine {
public class ActiveCoroutine { public class ActiveCoroutine {
private readonly IEnumerator<IWait> enumerator; private readonly IEnumerator<Wait> enumerator;
private Wait current;
public bool IsFinished { get; private set; } public bool IsFinished { get; private set; }
public bool WasCanceled { get; private set; } public bool WasCanceled { get; private set; }
public FinishCallback OnFinished; public FinishCallback OnFinished;
internal ActiveCoroutine(IEnumerator<IWait> enumerator) { internal ActiveCoroutine(IEnumerator<Wait> enumerator) {
this.enumerator = enumerator; this.enumerator = enumerator;
} }
@ -24,8 +26,7 @@ namespace Coroutine {
internal bool Tick(double deltaSeconds) { internal bool Tick(double deltaSeconds) {
if (!this.WasCanceled) { if (!this.WasCanceled) {
var curr = this.enumerator.Current; if (this.current.Tick(deltaSeconds))
if (curr != null && curr.Tick(deltaSeconds))
this.MoveNext(); this.MoveNext();
} }
return this.IsFinished; return this.IsFinished;
@ -33,22 +34,24 @@ namespace Coroutine {
internal bool OnEvent(Event evt) { internal bool OnEvent(Event evt) {
if (!this.WasCanceled) { if (!this.WasCanceled) {
var curr = this.enumerator.Current; if (this.current.OnEvent(evt))
if (curr != null && curr.OnEvent(evt))
this.MoveNext(); this.MoveNext();
} }
return this.IsFinished; return this.IsFinished;
} }
private void MoveNext() { internal bool MoveNext() {
if (!this.enumerator.MoveNext()) { if (!this.enumerator.MoveNext()) {
this.IsFinished = true; this.IsFinished = true;
this.OnFinished?.Invoke(this); this.OnFinished?.Invoke(this);
return false;
} }
this.current = this.enumerator.Current;
return true;
} }
internal WaitType GetCurrentType() { internal bool IsWaitingForEvent() {
return this.enumerator.Current.GetWaitType(); return this.current.IsWaitingForEvent();
} }
public delegate void FinishCallback(ActiveCoroutine coroutine); 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> TickingCoroutines = new List<ActiveCoroutine>();
private static readonly List<ActiveCoroutine> EventCoroutines = new List<ActiveCoroutine>(); private static readonly List<ActiveCoroutine> EventCoroutines = new List<ActiveCoroutine>();
public static ActiveCoroutine Start(IEnumerator<IWait> coroutine) { public static ActiveCoroutine Start(IEnumerable<Wait> coroutine) {
if (!coroutine.MoveNext()) return Start(coroutine.GetEnumerator());
return null; }
public static ActiveCoroutine Start(IEnumerator<Wait> coroutine) {
var inst = new ActiveCoroutine(coroutine); var inst = new ActiveCoroutine(coroutine);
var type = inst.GetCurrentType(); if (inst.MoveNext()) {
if (type == WaitType.Tick) if (inst.IsWaitingForEvent()) {
TickingCoroutines.Add(inst); EventCoroutines.Add(inst);
else if (type == WaitType.Event) } else {
EventCoroutines.Add(inst); TickingCoroutines.Add(inst);
}
}
return inst; return inst;
} }
public static void InvokeLater(IWait wait, Action action) { public static void InvokeLater(Wait wait, Action action) {
Start(InvokeLaterImpl(wait, action)); Start(InvokeLaterImpl(wait, action));
} }
@ -29,7 +33,7 @@ namespace Coroutine {
var coroutine = TickingCoroutines[i]; var coroutine = TickingCoroutines[i];
if (coroutine.Tick(deltaSeconds)) { if (coroutine.Tick(deltaSeconds)) {
TickingCoroutines.RemoveAt(i); TickingCoroutines.RemoveAt(i);
} else if (coroutine.GetCurrentType() != WaitType.Tick) { } else if (coroutine.IsWaitingForEvent()) {
TickingCoroutines.RemoveAt(i); TickingCoroutines.RemoveAt(i);
EventCoroutines.Add(coroutine); EventCoroutines.Add(coroutine);
} }
@ -41,7 +45,7 @@ namespace Coroutine {
var coroutine = EventCoroutines[i]; var coroutine = EventCoroutines[i];
if (coroutine.OnEvent(evt)) { if (coroutine.OnEvent(evt)) {
EventCoroutines.RemoveAt(i); EventCoroutines.RemoveAt(i);
} else if (coroutine.GetCurrentType() != WaitType.Event) { } else if (!coroutine.IsWaitingForEvent()) {
EventCoroutines.RemoveAt(i); EventCoroutines.RemoveAt(i);
TickingCoroutines.Add(coroutine); TickingCoroutines.Add(coroutine);
} }
@ -52,7 +56,7 @@ namespace Coroutine {
return TickingCoroutines.Concat(EventCoroutines); 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; yield return wait;
action(); 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.Start(EmptyCoroutine());
CoroutineHandler.InvokeLater(new WaitSeconds(10), () => { CoroutineHandler.InvokeLater(new Wait(10), () => {
Console.WriteLine("Raising test event"); Console.WriteLine("Raising test event");
CoroutineHandler.RaiseEvent(TestEvent); 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; var lastTime = DateTime.Now;
while (true) { while (true) {
@ -29,24 +29,24 @@ namespace Test {
} }
} }
private static IEnumerator<IWait> WaitSeconds() { private static IEnumerator<Wait> WaitSeconds() {
Console.WriteLine("First thing " + DateTime.Now); Console.WriteLine("First thing " + DateTime.Now);
yield return new WaitSeconds(1); yield return new Wait(1);
Console.WriteLine("After 1 second " + DateTime.Now); Console.WriteLine("After 1 second " + DateTime.Now);
yield return new WaitSeconds(9); yield return new Wait(9);
Console.WriteLine("After 10 seconds " + DateTime.Now); 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); 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); Console.WriteLine("After 10 more seconds " + DateTime.Now);
yield return new WaitSeconds(20); yield return new Wait(20);
Console.WriteLine("First coroutine done"); Console.WriteLine("First coroutine done");
} }
private static IEnumerator<IWait> PrintEvery10Seconds(ActiveCoroutine first) { private static IEnumerator<Wait> PrintEvery10Seconds(ActiveCoroutine first) {
while (true) { while (true) {
yield return new WaitSeconds(10); yield return new Wait(10);
Console.WriteLine("The time is " + DateTime.Now); Console.WriteLine("The time is " + DateTime.Now);
if (first.IsFinished) { if (first.IsFinished) {
Console.WriteLine("By the way, the first coroutine has finished!"); 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; yield break;
} }