namespace AIStudio.Tools.Services;
///
/// The pauses of the indexing, e.g., while a backup copies the indexes.
///
///
/// Pauses may overlap, and the indexing goes on once the last one ended. Every run which a pause
/// stopped is noted together with why it was asked for, so that it can be queued again for the same
/// reason once the indexing goes on: a retry the user asked for still signs in to a mailbox again
/// after its server refused a sign-in, cf. DataSourceRunQueue.
///
internal sealed class IndexingPauses
{
private readonly Lock stateLock = new();
///
/// The runs which a pause stopped, each with why it was asked for.
///
private readonly Dictionary stoppedRuns = new(StringComparer.OrdinalIgnoreCase);
private int count;
///
/// Completes once the last pause ended. Null while the indexing is not paused.
///
private TaskCompletionSource? lastPauseEnded;
///
/// Whether the indexing is paused right now.
///
public bool IsPaused
{
get
{
lock (this.stateLock)
return this.count > 0;
}
}
///
/// Begins a pause. From now on, every run which is about to start waits.
///
public void Begin()
{
lock (this.stateLock)
{
if (this.count++ == 0)
this.lastPauseEnded = new(TaskCreationOptions.RunContinuationsAsynchronously);
}
}
///
/// Notes a run which a pause stopped.
///
/// The id of the data source.
/// Why the run was asked for.
public void RecordStoppedRun(string dataSourceId, DataSourceEmbeddingRefreshMode refreshMode)
{
lock (this.stateLock)
this.stoppedRuns.TryAdd(dataSourceId, refreshMode);
}
///
/// Ends a pause.
///
/// The runs which the pauses stopped, once the last pause ended, so the caller queues them again. Otherwise, none.
public IReadOnlyList End()
{
lock (this.stateLock)
{
if (this.count == 0 || --this.count > 0)
return [];
this.lastPauseEnded?.TrySetResult();
this.lastPauseEnded = null;
var runsToQueueAgain = this.stoppedRuns.Select(stoppedRun => new DataSourceRunRequest(stoppedRun.Key, stoppedRun.Value)).ToList();
this.stoppedRuns.Clear();
return runsToQueueAgain;
}
}
///
/// Waits while the indexing is paused.
///
/// Stops the waiting, e.g., because the run waiting here was stopped.
public Task WaitWhilePausedAsync(CancellationToken token)
{
lock (this.stateLock)
return this.lastPauseEnded?.Task.WaitAsync(token) ?? Task.CompletedTask;
}
}