Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions Destine.Samples/ElctricCarWithResources.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;

namespace Destine.Samples
{
public class ElctricCarWithResources
{
private readonly World _world;
private readonly string _name;
private readonly ResourceManager _batteryChargingStation;
private readonly uint _drivingTime;
private readonly uint _chargeDuration;

public static void Run()
{
Console.WriteLine("ElectricCarWithResources");
var world = new World {SimEndCondition = world1 => world1.CurrentTime == 15};
var batteryCharingStationManager = new ResourceManager(2);
var cars = new List<ElctricCarWithResources>();
foreach (var i in Enumerable.Range(0, 3))
{
var car = new ElctricCarWithResources(world, $"Car {i}", batteryCharingStationManager, (uint)i * 2, 5);
cars.Add(car);
world.Process(car.Process());

}

world.OnWorldTick = (time) =>
{
Console.WriteLine($"-- TICK {time}| ");
batteryCharingStationManager.PrintStatus();
//Console.WriteLine($" - Bag has stuffs: {batteryCharingStationManager._resources.OutputAvailable()}");
};
world.Run();
Console.ReadLine();
}

public ElctricCarWithResources(World world, string name, ResourceManager batteryChargingStation,
uint drivingTime, uint chargeDuration)
{
Console.WriteLine($"Created {name}");
_world = world;
_name = name;
_batteryChargingStation = batteryChargingStation;
_drivingTime = drivingTime;
_chargeDuration = chargeDuration;
}

public async Task Process()
{
await _world.Timeout(_drivingTime);
Console.WriteLine($"{_name} arriving at {_world.CurrentTime}");

var batteryStation = await _batteryChargingStation.Request();
Console.WriteLine($"{_name} starting to charge at Time {_world.CurrentTime}, stations {batteryStation.GetHashCode()}");
await _world.Timeout(_chargeDuration);

Console.WriteLine($"{_name} leaving the bcs at {_world.CurrentTime}");
batteryStation.Release();
}
}
}
9 changes: 5 additions & 4 deletions Destine.Samples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ class Program
{
static void Main(string[] args)
{
SimpleExample.Run();
//SimpleExample.Run();

BasicCar.Run();
//BasicCar.Run();

ElectricCar.Run();

//ElectricCar.Run();

ElctricCarWithResources.Run();
}
}
}
1 change: 1 addition & 0 deletions Destine/Destine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<ItemGroup>
<PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" />
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="4.9.0" />
</ItemGroup>

</Project>
27 changes: 27 additions & 0 deletions Destine/Resource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Destine
{
public class Resource
{
private readonly ResourceManager _resourceManager;

public Resource(ResourceManager resourceManager)
{
_resourceManager = resourceManager;
//Console.WriteLine($"Resource created: {this.GetHashCode()}");
}

public void Release()
{
_resourceManager.ReturnResource(this);
}

public override int GetHashCode()
{
return base.GetHashCode() / 10000;
}
}
}
64 changes: 64 additions & 0 deletions Destine/ResourceManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using Nito.AsyncEx;

namespace Destine
{
public class ResourceManager
{
private readonly AsyncCollection<Resource> _buff;
private readonly ConcurrentBag<Resource> _internalBag;

private readonly HashSet<Resource> _debugMasterResourceSet;

public ResourceManager(int resourceCount = 1)
{
_debugMasterResourceSet = new HashSet<Resource>();

_internalBag = new ConcurrentBag<Resource>();
_buff = new AsyncCollection<Resource>(_internalBag);
for (var i = 0; i < resourceCount; i++)
{
QueueResource(new Resource(this));
}
Console.WriteLine($"ResourceManager constructed with internal bag {BagContents()}");

}

public async Task<Resource> Request()
{
return await _buff.TakeAsync();
}

public void ReturnResource(Resource r)
{
// ugly to mis-use the using/dispose pattern
QueueResource(r);
Console.WriteLine($"New Resource Queued {BagContents()}");
}

private void QueueResource(Resource r)
{
_buff.Add(r);
_debugMasterResourceSet.Add(r);
}

public void PrintStatus()
{
Console.WriteLine(BagContents());
}

private string BagContents()
{
var ret = $"Bag | size: {_internalBag.Count} | this: {this.GetHashCode() / 1000}";
return ret;
}
}
}
27 changes: 21 additions & 6 deletions Destine/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ namespace Destine
public class World
{
public Func<World, bool> SimEndCondition = world => false;
private readonly Clock clock;
public uint CurrentTime => clock.CurrentTick;
public Action<uint> OnWorldTick { get; set; }

private readonly Clock clock;
private bool _simDone = false;
private Dictionary<TaskCompletionSource<bool>, uint> timeouts = new Dictionary<TaskCompletionSource<bool>, uint>();
/// <summary>
/// Contains all the Timeout events that processes have configured. Key'ed to cancellation sources (set true on timeout), valued to when (clock) the timeout should get triggered
/// </summary>
private readonly Dictionary<TaskCompletionSource<bool>, uint> timeouts = new Dictionary<TaskCompletionSource<bool>, uint>();

private List<Task> processes = new List<Task>();
private readonly List<Task> processes = new List<Task>();

public World()
{
Expand All @@ -33,6 +37,7 @@ public bool Tick()
return false;

clock.Tick();
OnWorldTick(CurrentTime);
CheckTimeouts();

if (SimEndCondition != null && SimEndCondition.Invoke(this))
Expand All @@ -45,8 +50,15 @@ public bool Tick()

public void Run()
{
CheckTimeouts();
while (Tick())
{
if (SimEndCondition == null && processes.TrueForAll(task => task.Status == TaskStatus.RanToCompletion))
{
Console.WriteLine("No more processes, ending world");
_simDone = true;

}
}
}

Expand All @@ -69,7 +81,7 @@ public Task Timeout(uint duration, CancellationTokenSource cts = null)
var tcs = new TaskCompletionSource<bool>();
timeouts[tcs] = CurrentTime + duration;
var ctts = new CancellationTokenTaskSource<bool>(cts.Token);
return Task.WhenAny(tcs.Task, ctts.Task).ContinueWith(task => timeouts.Remove(tcs), TaskContinuationOptions.ExecuteSynchronously); // todo: refactor/cleanup wiht CheckTimeouts
return Task.WhenAny(tcs.Task, ctts.Task).ContinueWith(task => timeouts.Remove(tcs), TaskContinuationOptions.ExecuteSynchronously); // todo: refactor/cleanup with CheckTimeouts
}

private void CheckTimeouts()
Expand All @@ -84,8 +96,11 @@ private void CheckTimeouts()
}
}

toRemove.ForEach(tcs => tcs.SetResult(true));
toRemove.ForEach(tcs => timeouts.Remove(tcs));
toRemove.ForEach(tcs =>
{
tcs.SetResult(true);
timeouts.Remove(tcs);
});
}
}
}