<commit_before>﻿using System;
using System.Threading;
using System.Threading.Tasks;

namespace Metrics.Utils
{
    /// <summary>
    /// Utility class to schedule an Action to be executed repeatedly according to the interval.
    /// </summary>
    /// <remarks>
    /// The scheduling code is inspired form Daniel Crenna's metrics port
    /// https://github.com/danielcrenna/metrics-net/blob/master/src/metrics/Reporting/ReporterBase.cs
    /// </remarks>
    public sealed class ActionScheduler : Scheduler
    {
        private CancellationTokenSource token;
        private readonly int toleratedConsecutiveFailures;

        public ActionScheduler(int toleratedConsecutiveFailures = 0)
        {
            if (toleratedConsecutiveFailures < -1)
            {
                throw new ArgumentException($"{nameof(toleratedConsecutiveFailures)} must be >= -1");
            }
            this.toleratedConsecutiveFailures = toleratedConsecutiveFailures;
        }

        public void Start(TimeSpan interval, Action action)
        {
            Start(interval, t =>
            {
                if (!t.IsCancellationRequested)
                {
                    action();
                }
            });
        }

        public void Start(TimeSpan interval, Action<CancellationToken> action)
        {
            Start(interval, t =>
            {
                action(t);
                return Task.FromResult(true);
            });
        }

        public void Start(TimeSpan interval, Func<Task> action)
        {
            Start(interval, t => t.IsCancellationRequested ? action() : Task.FromResult(true));
        }

        public void Start(TimeSpan interval, Func<CancellationToken, Task> action)
        {
            if (interval.TotalSeconds == 0)
            {
                throw new ArgumentException("interval must be > 0 seconds", nameof(interval));
            }

            if (this.token != null)
            {
                throw new InvalidOperationException("Scheduler is already started.");
            }

            this.token = new CancellationTokenSource();

            RunScheduler(interval, action, this.token, this.toleratedConsecutiveFailures);
        }

        private static void RunScheduler(TimeSpan interval, Func<CancellationToken, Task> action, CancellationTokenSource token, int toleratedConsecutiveFailures)
        {
            Task.Factory.StartNew(async () =>
            {
                var nbFailures = 0;
                while (!token.IsCancellationRequested)
                {
                    try
                    {
                        await Task.Delay(interval, token.Token).ConfigureAwait(false);
                        try
                        {
                            await action(token.Token).ConfigureAwait(false);
                            nbFailures = 0;
                        }
                        catch (Exception x)
                        {
                            MetricsErrorHandler.Handle(x, "Error while executing action scheduler.");
                            if (toleratedConsecutiveFailures >= 0)
                            {
                                nbFailures++;
                                if (nbFailures > toleratedConsecutiveFailures)<commit_msg>use Task.Run instead of Task.Factory.StartNew<commit_after>﻿using System;
using System.Threading;
using System.Threading.Tasks;

namespace Metrics.Utils
{
    /// <summary>
    /// Utility class to schedule an Action to be executed repeatedly according to the interval.
    /// </summary>
    /// <remarks>
    /// The scheduling code is inspired form Daniel Crenna's metrics port
    /// https://github.com/danielcrenna/metrics-net/blob/master/src/metrics/Reporting/ReporterBase.cs
    /// </remarks>
    public sealed class ActionScheduler : Scheduler
    {
        private CancellationTokenSource token;
        private readonly int toleratedConsecutiveFailures;

        public ActionScheduler(int toleratedConsecutiveFailures = 0)
        {
            if (toleratedConsecutiveFailures < -1)
            {
                throw new ArgumentException($"{nameof(toleratedConsecutiveFailures)} must be >= -1");
            }
            this.toleratedConsecutiveFailures = toleratedConsecutiveFailures;
        }

        public void Start(TimeSpan interval, Action action)
        {
            Start(interval, t =>
            {
                if (!t.IsCancellationRequested)
                {
                    action();
                }
            });
        }

        public void Start(TimeSpan interval, Action<CancellationToken> action)
        {
            Start(interval, t =>
            {
                action(t);
                return Task.FromResult(true);
            });
        }

        public void Start(TimeSpan interval, Func<Task> action)
        {
            Start(interval, t => t.IsCancellationRequested ? action() : Task.FromResult(true));
        }

        public void Start(TimeSpan interval, Func<CancellationToken, Task> action)
        {
            if (interval.TotalSeconds == 0)
            {
                throw new ArgumentException("interval must be > 0 seconds", nameof(interval));
            }

            if (this.token != null)
            {
                throw new InvalidOperationException("Scheduler is already started.");
            }

            this.token = new CancellationTokenSource();

            RunScheduler(interval, action, this.token, this.toleratedConsecutiveFailures);
        }

        private static void RunScheduler(TimeSpan interval, Func<CancellationToken, Task> action, CancellationTokenSource token, int toleratedConsecutiveFailures)
        {
            Task.Run(async () =>
            {
                var nbFailures = 0;
                while (!token.IsCancellationRequested)
                {
                    try
                    {
                        await Task.Delay(interval, token.Token).ConfigureAwait(false);
                        try
                        {
                            await action(token.Token).ConfigureAwait(false);
                            nbFailures = 0;
                        }
                        catch (Exception x)
                        {
                            MetricsErrorHandler.Handle(x, "Error while executing action scheduler.");
                            if (toleratedConsecutiveFailures >= 0)
                            {
                                nbFailures++;
                                if (nbFailures > toleratedConsecutiveFailures)
{'fraction_non_alphanumeric': 0.0825147347740668, 'fraction_numerical': 0.0024180142058334592, 'mean_word_length': 1.4814398200224972, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 35, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5090899', 'n_tokens_mistral': 1615, 'n_tokens_neox': 1541, 'n_words': 351}