// OpenTween - Client of Twitter // Copyright (c) 2018 kim_upsilon (@kim_upsilon) // All rights reserved. // // This file is part of OpenTween. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General public License as published by the Free // Software Foundation; either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License // for more details. // // You should have received a copy of the GNU General public License along // with this program. If not, see , or write to // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, // Boston, MA 02110-1301, USA. #nullable enable using System; using System.Threading; using System.Threading.Tasks; namespace OpenTween { /// /// コールバック先の関数を 未満の頻度で呼ばないよう制御するタイマー /// /// /// lodash の _.throttle() に相当する機能となっている /// public class ThrottleTimer : IDisposable { private readonly ITimer throttlingTimer; private readonly Func timerCallback; private readonly object lockObject = new(); private bool calledSinceLastInvoke; private bool refreshTimerEnabled; public TimeSpan Interval { get; } public ThrottleTimer(Func timerCallback, TimeSpan interval) { this.timerCallback = timerCallback; this.Interval = interval; this.throttlingTimer = this.CreateTimer(this.Execute); this.calledSinceLastInvoke = false; this.refreshTimerEnabled = false; } protected virtual ITimer CreateTimer(Func callback) => new AsyncTimer(callback); public async Task Call() { bool startTimer; lock (this.lockObject) { this.calledSinceLastInvoke = true; if (this.refreshTimerEnabled) { startTimer = false; } else { startTimer = true; this.refreshTimerEnabled = true; } } if (startTimer) { await this.Invoke().ConfigureAwait(false); this.throttlingTimer.Change(dueTime: this.Interval, period: Timeout.InfiniteTimeSpan); } } private async Task Execute() { bool invoke; lock (this.lockObject) { if (this.calledSinceLastInvoke) { invoke = true; } else { invoke = false; this.refreshTimerEnabled = false; } } if (invoke) { await this.Invoke().ConfigureAwait(false); this.throttlingTimer.Change(dueTime: this.Interval, period: Timeout.InfiniteTimeSpan); } } private async Task Invoke() { await Task.Run(async () => { lock (this.lockObject) this.calledSinceLastInvoke = false; await this.timerCallback().ConfigureAwait(false); }); } public void Dispose() => this.throttlingTimer.Dispose(); public static ThrottleTimer Create(Func callback, TimeSpan wait) => new(callback, wait); } }