You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/Shedulling/AsyncShedullerImpl.cs

300 lines
12 KiB

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using ZeroLevel.Services.Logging;
namespace ZeroLevel.Services.Shedulling
{
internal class AsyncShedullerImpl
: IAsyncSheduller
{
private readonly DateTimeAsyncSheduller _asyncSheduller;
public AsyncShedullerImpl()
{
_asyncSheduller = new DateTimeAsyncSheduller();
}
#region One time events
public long RemindAsyncAfter(TimeSpan timespan, Func<long, Task> callback)
{
return _asyncSheduller.Push(timespan, callback);
}
public long RemindAsyncAt(DateTime date, Func<long, Task> callback)
{
return _asyncSheduller.Push(date, callback);
}
#endregion
#region Repitable behaviour
private readonly ConcurrentDictionary<long, ExpiredAsyncObject> _repitableAsyncActions = new ConcurrentDictionary<long, ExpiredAsyncObject>();
/// <summary>
/// Исполняет действие раз в период, при этом период перерасчитывается по переданной функции при каждом пересоздании задачи
/// </summary>
/// <param name="nextEventPeriodCalcFunction">Функция для расчета следующего периода</param>
/// <param name="callback">Действие</param>
/// <returns>Идентификатор задания</returns>
public long RemindAsyncEveryNonlinearPeriod(Func<TimeSpan> nextEventPeriodCalcFunction,
Func<long, Task> callback,
bool breakWherError = false)
{
return RemindAsyncEveryNonlinearPeriod(nextEventPeriodCalcFunction, nextEventPeriodCalcFunction, callback, breakWherError);
}
/// <summary>
/// Исполняет действие раз в период, при этом период перерасчитывается по переданной функции при каждом пересоздании задачи
/// </summary>
/// <param name="firstEventPeriodCalcFunction">Функция для расчета периода до первого исполнения</param>
/// <param name="nextEventPeriodCalcFunction">Функция для расчета периода до последующих исполнений</param>
/// <param name="callback">Действие</param>
/// <returns>Идентификатор задания</returns>
public long RemindAsyncEveryNonlinearPeriod(Func<TimeSpan> firstEventPeriodCalcFunction,
Func<TimeSpan> nextEventPeriodCalcFunction,
Func<long, Task> callback,
bool breakWherError = false)
{
var obj = new ExpiredAsyncObject { ExpirationDate = DateTime.Now.AddMilliseconds(firstEventPeriodCalcFunction().TotalMilliseconds) };
_repitableAsyncActions.TryAdd(obj.Key, obj);
obj.Callback = async (key) =>
{
try
{
await callback(key);
}
catch (Exception ex)
{
Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler");
if (breakWherError)
return;
}
ExpiredAsyncObject repObj;
if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj))
{
_asyncSheduller.Push(repObj.Reset(DateTime.Now.AddMilliseconds(nextEventPeriodCalcFunction().TotalMilliseconds)));
}
};
return _asyncSheduller.Push(obj);
}
/// <summary>
/// Исполняет действие раз в период, при этом дата перерасчитывается по переданной функции при каждом пересоздании задачи
/// </summary>
/// <param name="nextEventDateCalcFunction">Функция для расчета следующей даты</param>
/// <param name="callback">Действие</param>
/// <returns>Идентификатор задания</returns>
public long RemindAsyncEveryNonlinearDate(Func<DateTime, DateTime> nextEventDateCalcFunction,
Func<long, Task> callback,
bool breakWherError = false)
{
return RemindAsyncEveryNonlinearDate(nextEventDateCalcFunction, nextEventDateCalcFunction, callback, breakWherError);
}
public long RemindAsyncEveryNonlinearDate(DateTime firstTime,
Func<DateTime, DateTime> nextEventDateCalcFunction,
Func<long, Task> callback,
bool breakWherError = false)
{
var obj = new ExpiredAsyncObject { ExpirationDate = firstTime };
_repitableAsyncActions.TryAdd(obj.Key, obj);
obj.Callback = async (key) =>
{
try
{
await callback(key);
}
catch (Exception ex)
{
Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler");
if (breakWherError)
return;
}
ExpiredAsyncObject repObj;
if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj))
{
var nextDate = nextEventDateCalcFunction(obj.ExpirationDate);
if (DateTime.Compare(nextDate, DateTime.MinValue) == 0)
{
Remove(repObj.Key);
}
else
{
_asyncSheduller.Push(repObj.Reset(nextDate));
}
}
};
return _asyncSheduller.Push(obj);
}
/// <summary>
/// Исполняет действие раз в период, при этом дата перерасчитывается по переданной функции при каждом пересоздании задачи
/// </summary>
/// <param name="firstEventDateCalcFunction">Функция для расчет даты первого запуска</param>
/// <param name="nextEventDateCalcFunction">Функция для расчета следующей даты</param>
/// <param name="callback">Действие</param>
/// <returns>Идентификатор задания</returns>
public long RemindAsyncEveryNonlinearDate(Func<DateTime, DateTime> firstEventDateCalcFunction,
Func<DateTime, DateTime> nextEventDateCalcFunction,
Func<long, Task> callback,
bool breakWherError = false)
{
var obj = new ExpiredAsyncObject { ExpirationDate = firstEventDateCalcFunction(DateTime.Now) };
_repitableAsyncActions.TryAdd(obj.Key, obj);
obj.Callback = async (key) =>
{
try
{
await callback(key);
}
catch (Exception ex)
{
Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler");
if (breakWherError)
return;
}
ExpiredAsyncObject repObj;
if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj))
{
var nextDate = nextEventDateCalcFunction(obj.ExpirationDate);
if (DateTime.Compare(nextDate, DateTime.MinValue) == 0)
{
Remove(repObj.Key);
}
else
{
_asyncSheduller.Push(repObj.Reset(nextDate));
}
}
};
return _asyncSheduller.Push(obj);
}
/// <summary>
/// Исполняет действие раз в указанный период
/// </summary>
/// <param name="timespan">Период</param>
/// <param name="callback">Действие</param>
/// <returns>Идентификатор задания</returns>
public long RemindAsyncEvery(TimeSpan timespan,
Func<long, Task> callback,
bool breakWherError = false)
{
return RemindAsyncEvery(timespan, timespan, callback, breakWherError);
}
/// <summary>
/// Исполняет действие раз в указанный период
/// </summary>
/// <param name="first">Период до первого выполнения</param>
/// <param name="next">Период</param>
/// <param name="callback">Действие</param>
/// <returns>Идентификатор задания</returns>
public long RemindAsyncEvery(TimeSpan first,
TimeSpan next,
Func<long, Task> callback,
bool breakWherError = false)
{
var obj = new ExpiredAsyncObject { ExpirationDate = DateTime.Now.AddMilliseconds(first.TotalMilliseconds) };
_repitableAsyncActions.TryAdd(obj.Key, obj);
obj.Callback = async (key) =>
{
try
{
await callback(key).ConfigureAwait(false);
}
catch (Exception ex)
{
Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler");
if (breakWherError)
return;
}
ExpiredAsyncObject repObj;
if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj))
{
_asyncSheduller.Push(repObj.Reset(DateTime.Now.AddMilliseconds(next.TotalMilliseconds)));
}
};
return _asyncSheduller.Push(obj);
}
public long RemindAsyncWhile(TimeSpan period,
Func<long, Task<bool>> callback,
Action continueWith = null,
bool breakWherError = false)
{
var obj = new ExpiredAsyncObject { ExpirationDate = DateTime.Now.AddMilliseconds(period.TotalMilliseconds) };
_repitableAsyncActions.TryAdd(obj.Key, obj);
obj.Callback = async (key) =>
{
bool success = false;
try
{
success = await callback(key);
}
catch (Exception ex)
{
Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler");
if (breakWherError)
return;
}
if (success)
{
Remove(obj.Key);
if (continueWith != null)
continueWith();
}
else
{
ExpiredAsyncObject repObj;
if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj))
{
_asyncSheduller.Push(repObj.Reset(DateTime.Now.AddMilliseconds(period.TotalMilliseconds)));
}
}
};
return _asyncSheduller.Push(obj);
}
#endregion
#region Sheduller control
public void Pause()
{
_asyncSheduller.Pause();
}
public void Resume()
{
_asyncSheduller.Resume();
}
public void Clean()
{
_asyncSheduller.Clean();
}
public bool Remove(long key)
{
var success = _asyncSheduller.Remove(key);
if (_repitableAsyncActions.ContainsKey(key))
{
ExpiredAsyncObject rem;
return _repitableAsyncActions.TryRemove(key, out rem);
}
return success;
}
#endregion
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_asyncSheduller.Dispose();
}
}
#endregion
}
}

Powered by TurnKey Linux.