Skip to content

Commit

Permalink
PortableTimer
Browse files Browse the repository at this point in the history
  • Loading branch information
muratcakir committed Sep 28, 2022
1 parent 6990b24 commit d12f27c
Showing 1 changed file with 111 additions and 0 deletions.
111 changes: 111 additions & 0 deletions src/Smartstore/Threading/PortableTimer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using System.Diagnostics;

namespace Smartstore.Threading
{
public class PortableTimer : IDisposable
{
private readonly object _stateLock = new();
private readonly Func<CancellationToken, Task> _onTick;
private readonly CancellationTokenSource _cancel = new();
private readonly Timer _timer;

private bool _running;
private bool _disposed;

public PortableTimer(Func<CancellationToken, Task> onTick)
{
_onTick = Guard.NotNull(onTick, nameof(onTick));

using (ExecutionContext.SuppressFlow())
{
_timer = new(_ => OnTick(), null, Timeout.Infinite, Timeout.Infinite);
}
}

public void Start(TimeSpan interval)
{
if (interval < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(interval));
}

lock (_stateLock)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(PortableTimer));
}

_timer.Change(interval, interval);
}
}

async void OnTick()
{
try
{
lock (_stateLock)
{
if (_disposed)
{
return;
}

// There's a little bit of raciness here, but it's needed to support the
// current API, which allows the tick handler to reenter and set the next interval.

if (_running)
{
Monitor.Wait(_stateLock);

if (_disposed)
{
return;
}
}

_running = true;
}

if (!_cancel.Token.IsCancellationRequested)
{
ContextState.StartAsyncFlow();
await _onTick(_cancel.Token);
}
}
catch (OperationCanceledException tcx)
{
Debug.WriteLine("The timer was canceled during invocation: {0}", tcx);
}
finally
{
lock (_stateLock)
{
_running = false;
Monitor.PulseAll(_stateLock);
}
}
}

public void Dispose()
{
_cancel.Cancel();

lock (_stateLock)
{
if (_disposed)
{
return;
}

while (_running)
{
Monitor.Wait(_stateLock);
}

_timer.Dispose();
_disposed = true;
}
}
}
}

0 comments on commit d12f27c

Please sign in to comment.