// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System; using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Bindables; using osu.Framework.Development; using osu.Framework.Graphics.Containers; using osu.Framework.Threading; namespace osu.Game.Online { /// /// A component which requires a constant polling process. /// public abstract class PollingComponent : CompositeDrawable // switch away from Component because InternalChildren are used in usages. { private double? lastTimePolled; private ScheduledDelegate scheduledPoll; private bool pollingActive; /// /// The time in milliseconds to wait between polls. /// Setting to zero stops all polling. /// public readonly Bindable TimeBetweenPolls = new Bindable(); /// /// /// /// The initial time in milliseconds to wait between polls. Setting to zero stops all polling. protected PollingComponent(double timeBetweenPolls = 0) { TimeBetweenPolls.BindValueChanged(_ => { scheduledPoll?.Cancel(); pollIfNecessary(); }); TimeBetweenPolls.Value = timeBetweenPolls; } protected override void LoadComplete() { base.LoadComplete(); pollIfNecessary(); } /// /// Immediately performs a . /// public void PollImmediately() { lastTimePolled = Time.Current - TimeBetweenPolls.Value; scheduleNextPoll(); } /// /// Performs a poll. Implement but do not call this. /// protected virtual Task Poll() { return Task.CompletedTask; } private void doPoll() { Debug.Assert(ThreadSafety.IsUpdateThread); scheduledPoll = null; pollingActive = true; Poll().ContinueWith(_ => pollComplete()); } /// /// Call when a poll operation has completed. /// private void pollComplete() { lastTimePolled = Time.Current; pollingActive = false; if (scheduledPoll == null) pollIfNecessary(); } private void pollIfNecessary() { // we must be loaded so we have access to clock. if (!IsLoaded) return; // there's already a poll process running. if (pollingActive) return; // don't try polling if the time between polls hasn't been set. if (TimeBetweenPolls.Value == 0) return; if (!lastTimePolled.HasValue) { Scheduler.AddOnce(doPoll); return; } if (Time.Current - lastTimePolled.Value > TimeBetweenPolls.Value) { Scheduler.AddOnce(doPoll); return; } // not enough time has passed since the last poll. we do want to schedule a poll to happen, though. scheduleNextPoll(); } private void scheduleNextPoll() { scheduledPoll?.Cancel(); double lastPollDuration = lastTimePolled.HasValue ? Time.Current - lastTimePolled.Value : 0; scheduledPoll = Scheduler.AddDelayed(doPoll, Math.Max(0, TimeBetweenPolls.Value - lastPollDuration)); } } }