io.flutter.utils.AsyncRateLimiter.java Source code

Java tutorial

Introduction

Here is the source code for io.flutter.utils.AsyncRateLimiter.java

Source

/*
 * Copyright 2018 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
package io.flutter.utils;

import com.google.common.util.concurrent.RateLimiter;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Alarm;

import javax.swing.*;
import java.util.concurrent.CompletableFuture;

import static io.flutter.utils.AsyncUtils.whenCompleteUiThread;

/**
 * Rate limiter that issues requests asynchronously on the ui thread
 * ensuring framesPerSecond rate is not exceeded and that no more than 1
 * request is issued at a time.
 * <p>
 * Methods from this class must only be invoked from the main UI thread.
 */
public class AsyncRateLimiter implements Disposable {
    private final RateLimiter rateLimiter;
    private final Alarm requestScheduler;
    private final Computable<CompletableFuture<?>> callback;
    private CompletableFuture<?> pendingRequest;
    /**
     * A request has been scheduled to run but is not yet pending.
     */
    private boolean requestScheduledButNotStarted;

    public AsyncRateLimiter(double framesPerSecond, Computable<CompletableFuture<?>> callback) {
        this.callback = callback;
        rateLimiter = RateLimiter.create(framesPerSecond);
        requestScheduler = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this);
    }

    public void scheduleRequest() {
        if (requestScheduledButNotStarted) {
            // No need to schedule a request if one has already been scheduled but
            // hasn't yet actually started executing.
            return;
        }

        if (pendingRequest != null && !pendingRequest.isDone()) {
            // Wait for the pending request to be done before scheduling the new
            // request. The existing request has already started so may return state
            // that is now out of date.
            requestScheduledButNotStarted = true;
            whenCompleteUiThread(pendingRequest, (Object ignored, Throwable error) -> {
                pendingRequest = null;
                requestScheduledButNotStarted = false;
                scheduleRequest();
            });
            return;
        }

        if (rateLimiter.tryAcquire()) {
            // Safe to perform the request immediately.
            performRequest();
        } else {
            // Track that we have scheduled a request and then schedule the request
            // to occur once the rate limiter is available.
            requestScheduledButNotStarted = true;
            requestScheduler.addRequest(() -> {
                rateLimiter.acquire();

                Runnable doRun = () -> {
                    requestScheduledButNotStarted = false;
                    performRequest();
                };
                final Application app = ApplicationManager.getApplication();
                if (app == null || app.isUnitTestMode()) {
                    // This case existing to support unittesting.
                    SwingUtilities.invokeLater(doRun);
                } else {
                    app.invokeLater(doRun);
                }
            }, 0);
        }
    }

    private void performRequest() {
        pendingRequest = callback.compute();
    }

    @Override
    public void dispose() {
    }
}