Expanded upon EventScheduler

- Events are now sorted
- Thread waits until either a new event is scheduled or an existing
event is ready before continuing.
This commit is contained in:
Mitchell Kutchuk 2015-06-26 10:43:52 -07:00
parent 01b3742763
commit 2db4df9b5d
5 changed files with 102 additions and 15 deletions

View File

@ -5,6 +5,7 @@ namespace TrueCraft.API.Server
public interface IEventScheduler public interface IEventScheduler
{ {
void ScheduleEvent(DateTime when, Action<IMultiplayerServer> action); void ScheduleEvent(DateTime when, Action<IMultiplayerServer> action);
void Update(); void Start();
void Stop();
} }
} }

View File

@ -1,17 +1,23 @@
using System; using System;
using TrueCraft.API.Server; using TrueCraft.API.Server;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TrueCraft.API.Logging;
namespace TrueCraft namespace TrueCraft
{ {
public class EventScheduler : IEventScheduler public class EventScheduler : IEventScheduler
{ {
// TODO: This could be done more efficiently if the list were kept sorted private List<ScheduledEvent> Events { get; set; }
private IList<ScheduledEvent> Events { get; set; }
private object EventLock = new object(); private object EventLock = new object();
private IMultiplayerServer Server { get; set; } private IMultiplayerServer Server { get; set; }
private SemaphoreSlim Sem = new SemaphoreSlim(0, 1);
private CancellationTokenSource Cancel;
public EventScheduler(IMultiplayerServer server) public EventScheduler(IMultiplayerServer server)
{ {
Events = new List<ScheduledEvent>(); Events = new List<ScheduledEvent>();
@ -24,30 +30,99 @@ namespace TrueCraft
{ {
Events.Add(new ScheduledEvent { When = when, Action = action }); Events.Add(new ScheduledEvent { When = when, Action = action });
} }
if (Sem.CurrentCount == 0)
Sem.Release();
} }
public void Update() public void Start()
{ {
Cancel = new CancellationTokenSource();
Thread scheduleThread = new Thread(Update);
scheduleThread.IsBackground = true;
scheduleThread.Start();
}
public void Stop()
{
Cancel.Cancel();
}
private void Update()
{
while (true)
{
if (Cancel.IsCancellationRequested)
break;
try
{
ScheduledEvent? nextEvent = null;
lock (EventLock) lock (EventLock)
{ {
var start = DateTime.Now; var evts = Events.ToList();
for (int i = 0; i < Events.Count; i++) evts.Sort();
DateTime now = DateTime.Now;
for (int i = 0; i < evts.Count; i++)
{ {
var e = Events[i]; ScheduledEvent evt = evts[i];
if (e.When <= start)
{ if (evt.When < now)
e.Action(Server); break;
Events.RemoveAt(i);
evts.RemoveAt(i);
i--; i--;
evt.Action(Server);
} }
if (evts.Count > 0)
nextEvent = evts.First();
Events = evts;
}
var tasks = new List<Task> { Sem.WaitAsync(Cancel.Token) };
if (nextEvent != null)
{
TimeSpan ts = nextEvent.Value.When - DateTime.Now;
if (ts < TimeSpan.Zero)
continue;
tasks.Add(Task.Delay(ts, Cancel.Token));
}
Task.WhenAny(tasks).Wait();
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
Server.Log(LogCategory.Error, "Scheduler Error", ex.ToString());
} }
} }
} }
private struct ScheduledEvent private struct ScheduledEvent : IComparable<ScheduledEvent>
{ {
public DateTime When; public DateTime When;
public Action<IMultiplayerServer> Action; public Action<IMultiplayerServer> Action;
public int CompareTo(ScheduledEvent other)
{
if (When > other.When)
return 1;
if (When == other.When)
return 0;
return -1;
}
} }
} }
} }

View File

@ -113,6 +113,8 @@ namespace TrueCraft
Listener.Start(); Listener.Start();
EndPoint = (IPEndPoint)Listener.LocalEndpoint; EndPoint = (IPEndPoint)Listener.LocalEndpoint;
Scheduler.Start();
SocketAsyncEventArgs args = new SocketAsyncEventArgs(); SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += AcceptClient; args.Completed += AcceptClient;
@ -135,6 +137,8 @@ namespace TrueCraft
w.Save(); w.Save();
foreach (var c in Clients) foreach (var c in Clients)
DisconnectClient(c); DisconnectClient(c);
Scheduler.Stop();
} }
public void AddWorld(IWorld world) public void AddWorld(IWorld world)
@ -294,7 +298,6 @@ namespace TrueCraft
{ {
if (ShuttingDown) if (ShuttingDown)
return; return;
Scheduler.Update();
foreach (var manager in EntityManagers) foreach (var manager in EntityManagers)
{ {
manager.Update(); manager.Update();

View File

@ -9,6 +9,8 @@
<AssemblyName>TrueCraft</AssemblyName> <AssemblyName>TrueCraft</AssemblyName>
<ProductVersion>8.0.30703</ProductVersion> <ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<Optimize>false</Optimize> <Optimize>false</Optimize>
@ -16,11 +18,13 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<ConsolePause>false</ConsolePause> <ConsolePause>false</ConsolePause>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Release</OutputPath> <OutputPath>bin\Release</OutputPath>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
@ -75,6 +79,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
</Project> </Project>

3
TrueCraft/app.config Normal file
View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>