TimeTravel

This commit is contained in:
2022-01-31 23:13:39 +01:00
parent 80570d8895
commit c2dcb49016
78 changed files with 2294 additions and 363 deletions

View File

@@ -0,0 +1,217 @@
using AsyncEvent;
using FileTime.Core.Command;
using FileTime.Core.Models;
namespace FileTime.Core.Timeline
{
public class TimeRunner
{
private readonly CommandExecutor _commandExecutor;
private readonly List<ParallelCommands> _commandsToRun = new();
private readonly object _guard = new();
private bool _resourceIsInUse;
private readonly List<Thread> _commandRunners = new();
private bool _enableRunning = true;
public bool EnableRunning
{
get
{
bool result = true;
RunWithLock(() => result = _enableRunning);
return result;
}
set
{
RunWithLock(() => _enableRunning = value);
}
}
public IReadOnlyList<ReadOnlyParallelCommands> ParallelCommands { get; private set; } = new List<ReadOnlyParallelCommands>().AsReadOnly();
public AsyncEventHandler<AbsolutePath> RefreshContainer { get; } = new AsyncEventHandler<AbsolutePath>();
public event EventHandler? CommandsChanged;
public TimeRunner(CommandExecutor commandExecutor)
{
_commandExecutor = commandExecutor;
}
public async Task AddCommand(ICommand command, ParallelCommands? batch = null, bool toNewBatch = false)
{
await RunWithLockAsync(async () =>
{
ParallelCommands batchToAdd;
if (_commandsToRun.Count == 0)
{
batchToAdd = new ParallelCommands(PointInTime.CreateEmpty());
_commandsToRun.Add(batchToAdd);
}
else if (toNewBatch)
{
batchToAdd = new ParallelCommands(_commandsToRun.Last().Result);
_commandsToRun.Add(batchToAdd);
}
else if (batch != null && _commandsToRun.Contains(batch))
{
batchToAdd = batch;
}
else
{
batchToAdd = _commandsToRun[0];
}
await batchToAdd.AddCommand(command);
await RefreshCommands();
if (_commandRunners.Count == 0)
{
StartCommandRunner();
}
});
UpdateReadOnlyCommands();
}
public async Task TryStartCommandRunner()
{
await RunWithLockAsync(() =>
{
if (_commandRunners.Count == 0 && _commandsToRun.Count > 0)
{
StartCommandRunner();
}
});
}
private void StartCommandRunner()
{
if (_enableRunning)
{
RunCommands();
}
}
private void RunCommands()
{
if (_commandsToRun.Count > 0)
{
foreach (var command in _commandsToRun[0].Commands)
{
if (command.CanRun == CanCommandRun.True || (command.CanRun == CanCommandRun.Forceable && command.ForceRun))
{
var thread = new Thread(new ParameterizedThreadStart(RunCommand));
thread.Start(command);
}
else
{
break;
}
}
}
}
private void RunCommand(object? arg)
{
CommandTimeState? commandToRun = null;
try
{
if (arg is CommandTimeState commandToRun2)
{
commandToRun = commandToRun2;
_commandExecutor.ExecuteCommandAsync(commandToRun.Command, this).Wait();
}
}
finally
{
DisposeCommandThread(Thread.CurrentThread, commandToRun).Wait();
}
}
private async Task DisposeCommandThread(Thread thread, CommandTimeState? command)
{
await RunWithLockAsync(() =>
{
if (command != null)
{
_commandsToRun[0].Remove(command);
}
_commandRunners.Remove(thread);
});
await UpdateReadOnlyCommands();
await TryStartCommandRunner();
}
public async Task Refresh()
{
await RunWithLockAsync(async () =>
{
await RefreshCommands(PointInTime.CreateEmpty());
});
await UpdateReadOnlyCommands();
}
private async Task RefreshCommands(PointInTime? fullStartTime = null)
{
var curretnTime = fullStartTime ?? _commandsToRun[0].Result;
var startIndex = fullStartTime == null ? 1 : 0;
for (var i = startIndex; i < _commandsToRun.Count; i++)
{
curretnTime = await _commandsToRun[i].RefreshResult(curretnTime);
}
}
private async Task UpdateReadOnlyCommands()
{
await RunWithLockAsync(() =>
{
ParallelCommands = _commandsToRun.ConvertAll(c => new ReadOnlyParallelCommands(c)).AsReadOnly();
});
CommandsChanged?.Invoke(this, EventArgs.Empty);
}
private async Task RunWithLockAsync(Action action)
{
await RunWithLockAsync(() => { action(); return Task.CompletedTask; });
}
private async Task RunWithLockAsync(Func<Task> func)
{
while (true)
{
lock (_guard)
{
if (!_resourceIsInUse)
{
_resourceIsInUse = true;
break;
}
}
await Task.Delay(1);
}
try
{
await func();
}
finally
{
lock (_guard)
{
_resourceIsInUse = false;
}
}
}
private void RunWithLock(Action action) => RunWithLockAsync(action).Wait();
}
}