using System;
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Channels;
using termsync.Tools;
using System.Collections.Generic;
namespace termsync
{
///
/// A Write container that aquires a mutex on `Terminal`, preventing other writes while held.
///
public sealed class TermLock : IDisposable
{
private readonly CancellationTokenSource cancel;
private readonly AsyncMutex GlobalMutex;
private IDisposable held_lock = null;
internal TermLock(AsyncMutex mutex, CancellationToken token=default)
{
GlobalMutex = mutex;
cancel = CancellationTokenSource.CreateLinkedTokenSource(token);
}
internal async Task Acquire()
{
held_lock = await GlobalMutex.AcquireAsync(cancel.Token);
}
///
/// Write a line with lock held.
///
public Task WriteLine(string line)
{
return Terminal.WriteLineAndWait(line, cancel.Token);
}
public void Dispose()
{
cancel.Cancel();
cancel.Dispose();
if(held_lock!=null)
{
held_lock.Dispose();
held_lock = null;
}
}
~TermLock()
{
if (held_lock != null)
{
held_lock.Dispose();
held_lock = null;
}
}
}
public sealed class TermStage : IAsyncDisposable
{
private readonly AsyncMutex mutex = new AsyncMutex();
private readonly List lines = new List();
private readonly CancellationTokenSource cancel;
private readonly AsyncMutex globalMutex;
private readonly CancellationToken originalToken;
internal TermStage(AsyncMutex globalM, CancellationToken token)
{
originalToken = token;
globalMutex = globalM;
cancel = CancellationTokenSource.CreateLinkedTokenSource(token);
}
public async Task WriteLine(string line)
{
using (await mutex.AcquireAsync(cancel.Token))
{
lines.Add(line);
}
}
public async ValueTask DisposeAsync()
{
await mutex.AcquireAsync(cancel.Token);
cancel.Cancel();
mutex.Dispose();
cancel.Dispose();
using (await globalMutex.AcquireAsync(originalToken))
{
foreach (var line in lines)
{
await Terminal.WriteLineAndWait(line, originalToken);
}
}
lines.Clear();
}
}
//TODO: WriteLine staging that doesn't block until read to commit all.
///
/// Terminal control global state.
///
public static partial class Terminal
{
#region Sync
private static readonly CancellationTokenSource CancelAll = new CancellationTokenSource();
private static ChannelReader Input;
private static ChannelWriter Output;
private static readonly AsyncMutex ConsoleMutex = new AsyncMutex();
#endregion
#region Buffer
private static readonly List InputBuffer = new List();
///
/// The place in the that the user is writing to.
///
private static int InputAt = -1;
public static string Prompt { get; private set; } = "> ";
private static bool WriteLineOnFlush = true;
#endregion
#region Control
private static readonly AsyncMutex UserWriteMutex = new AsyncMutex();
private static readonly AsyncMutex UserReadMutex = new AsyncMutex();
///
/// Acquire global Write lock mutex.
///
public static async Task Lock()
{
var l = new TermLock(UserWriteMutex, CancelAll.Token);
await l.Acquire();
return l;
}
///
/// Create a staging container that will write all lines at once on DisposeAsync().
///
public static TermStage Stage()
{
return new TermStage(UserWriteMutex, CancelAll.Token);
}
private static async Task