using System; using System.Threading.Tasks; using CoCoL; namespace Duplicati.GUI.TrayIcon; /// /// A class that delays the execution of actions. /// public class ProcessBasedActionDelay : IDisposable { /// /// The channel that sends the delayed actions, buffer avoids deadlocks if multiple events are queued before starting. /// private readonly IChannel m_inboundActionChannel = Channel.Create(name: "UI Action", buffersize: 500); /// /// The channel that sends the start signal. /// private readonly IChannel m_initializedChannel = Channel.Create(name: "UI Initializer"); /// /// Reference to the task running /// private readonly Task m_task; /// /// Initializes a new instance of the class. /// public ProcessBasedActionDelay() { m_task = RunProcessor(m_inboundActionChannel.AsReadOnly(), m_initializedChannel.AsReadOnly()); } /// /// Runs the processor process, which pauses until a ready signal is received. /// /// The channel with actions to be delayed. /// The channel that sends the start signal. /// The task running the processor process. private static Task RunProcessor(IReadChannelEnd inboundChannel, IReadChannelEnd initializedChannel) => AutomationExtensions.RunTask(new { inboundChannel, initializedChannel }, async (self) => { // Wait for initialization await self.initializedChannel.ReadAsync(); while (true) { var action = await self.inboundChannel.ReadAsync(); action(); } }); /// /// Adds a new task to the processor /// /// The action to execute public void ExecuteAction(Action action) // Note: WriteNoWait() is used to avoid waiting for the action to be read, // as this would cause deadlocks if called from within the processor. // The buffer size should be sufficient to allow for a reasonable number of actions to be queued. => m_inboundActionChannel.WriteNoWait(action); /// /// Signals the start of the processor /// public void SignalStart() => m_initializedChannel.TryWrite(true); /// /// Disposes the object /// public void Dispose() { m_inboundActionChannel.Retire(); m_initializedChannel.Retire(); } }