CatchScheduler<TException>
using System.Reactive.Disposables;
using System.Runtime.CompilerServices;
namespace System.Reactive.Concurrency
{
internal sealed class CatchScheduler<TException> : SchedulerWrapper where TException : Exception
{
private class CatchSchedulerLongRunning : ISchedulerLongRunning
{
private readonly ISchedulerLongRunning _scheduler;
private readonly Func<TException, bool> _handler;
public CatchSchedulerLongRunning(ISchedulerLongRunning scheduler, Func<TException, bool> handler)
{
_scheduler = scheduler;
_handler = handler;
}
public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
{
return this._scheduler.ScheduleLongRunning<(CatchSchedulerLongRunning, Action<TState, ICancelable>, TState)>((this, action, state), (Action<(CatchSchedulerLongRunning, Action<TState, ICancelable>, TState), ICancelable>)delegate((CatchSchedulerLongRunning scheduler, Action<TState, ICancelable> action, TState state) tuple, ICancelable cancel) {
try {
tuple.action(tuple.state, cancel);
} catch (TException val) when () {
}
});
}
}
private sealed class CatchSchedulerPeriodic : ISchedulerPeriodic
{
private sealed class PeriodicallyScheduledWorkItem<TState> : IDisposable
{
private IDisposable _cancel;
private bool _failed;
private readonly Func<TState, TState> _action;
private readonly CatchSchedulerPeriodic _catchScheduler;
public PeriodicallyScheduledWorkItem(CatchSchedulerPeriodic scheduler, TState state, TimeSpan period, Func<TState, TState> action)
{
_catchScheduler = scheduler;
_action = action;
Disposable.SetSingle(ref _cancel, scheduler._scheduler.SchedulePeriodic<(PeriodicallyScheduledWorkItem<TState>, TState)>((this, state), period, (Func<(PeriodicallyScheduledWorkItem<TState>, TState), (PeriodicallyScheduledWorkItem<TState>, TState)>)(((PeriodicallyScheduledWorkItem<TState> this, TState state) tuple) => tuple.this?.Tick(tuple.state) ?? default((PeriodicallyScheduledWorkItem<TState>, TState)))));
}
public void Dispose()
{
Disposable.TryDispose(ref _cancel);
}
private (PeriodicallyScheduledWorkItem<TState> this, TState state) Tick(TState state)
{
if (!_failed)
try {
return (this, _action(state));
} catch (TException val) {
TException arg = (TException)val;
_failed = true;
if (!_catchScheduler._handler(arg))
throw;
Disposable.TryDispose(ref _cancel);
return default((PeriodicallyScheduledWorkItem<TState>, TState));
}
return default((PeriodicallyScheduledWorkItem<TState>, TState));
}
}
private readonly ISchedulerPeriodic _scheduler;
private readonly Func<TException, bool> _handler;
public CatchSchedulerPeriodic(ISchedulerPeriodic scheduler, Func<TException, bool> handler)
{
_scheduler = scheduler;
_handler = handler;
}
public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
{
return new PeriodicallyScheduledWorkItem<TState>(this, state, period, action);
}
}
private readonly Func<TException, bool> _handler;
public CatchScheduler(IScheduler scheduler, Func<TException, bool> handler)
: base(scheduler)
{
_handler = handler;
}
protected override Func<IScheduler, TState, IDisposable> Wrap<TState>(Func<IScheduler, TState, IDisposable> action)
{
return delegate(IScheduler self, TState state) {
try {
return action(GetRecursiveWrapper(self), state);
} catch (TException val) when () {
return Disposable.Empty;
}
};
}
public CatchScheduler(IScheduler scheduler, Func<TException, bool> handler, ConditionalWeakTable<IScheduler, IScheduler> cache)
: base(scheduler, cache)
{
_handler = handler;
}
protected override SchedulerWrapper Clone(IScheduler scheduler, ConditionalWeakTable<IScheduler, IScheduler> cache)
{
return new CatchScheduler<TException>(scheduler, _handler, cache);
}
protected override bool TryGetService(IServiceProvider provider, Type serviceType, out object service)
{
service = provider.GetService(serviceType);
if (service != null) {
if (serviceType == typeof(ISchedulerLongRunning))
service = new CatchSchedulerLongRunning((ISchedulerLongRunning)service, _handler);
else if (serviceType == typeof(ISchedulerPeriodic)) {
service = new CatchSchedulerPeriodic((ISchedulerPeriodic)service, _handler);
}
}
return true;
}
}
}