xref: /aosp_15_r20/external/perfetto/ui/src/base/async_limiter.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2024 The Android Open Source Project
2*6dbdd20aSAndroid Build Coastguard Worker//
3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*6dbdd20aSAndroid Build Coastguard Worker//
7*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*6dbdd20aSAndroid Build Coastguard Worker//
9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard Workerimport {Deferred, defer} from './deferred';
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Workertype Callback = () => Promise<void>;
18*6dbdd20aSAndroid Build Coastguard Worker
19*6dbdd20aSAndroid Build Coastguard Workerinterface Task {
20*6dbdd20aSAndroid Build Coastguard Worker  deferred: Deferred<void>;
21*6dbdd20aSAndroid Build Coastguard Worker  work: Callback;
22*6dbdd20aSAndroid Build Coastguard Worker}
23*6dbdd20aSAndroid Build Coastguard Worker
24*6dbdd20aSAndroid Build Coastguard Worker/**
25*6dbdd20aSAndroid Build Coastguard Worker * A tiny task queue management utility that ensures async tasks are not
26*6dbdd20aSAndroid Build Coastguard Worker * executed concurrently.
27*6dbdd20aSAndroid Build Coastguard Worker *
28*6dbdd20aSAndroid Build Coastguard Worker * If a task is run while a previous one is still running, it is enqueued and
29*6dbdd20aSAndroid Build Coastguard Worker * run after the first task completes.
30*6dbdd20aSAndroid Build Coastguard Worker *
31*6dbdd20aSAndroid Build Coastguard Worker * If multiple tasks are enqueued, only the latest task is run.
32*6dbdd20aSAndroid Build Coastguard Worker */
33*6dbdd20aSAndroid Build Coastguard Workerexport class AsyncLimiter {
34*6dbdd20aSAndroid Build Coastguard Worker  private readonly taskQueue: Task[] = [];
35*6dbdd20aSAndroid Build Coastguard Worker  private isRunning: boolean = false;
36*6dbdd20aSAndroid Build Coastguard Worker
37*6dbdd20aSAndroid Build Coastguard Worker  /**
38*6dbdd20aSAndroid Build Coastguard Worker   * Schedule a task to be run.
39*6dbdd20aSAndroid Build Coastguard Worker   *
40*6dbdd20aSAndroid Build Coastguard Worker   * @param work An async function to schedule.
41*6dbdd20aSAndroid Build Coastguard Worker   * @returns A promise that resolves when either the task has finished
42*6dbdd20aSAndroid Build Coastguard Worker   * executing, or after the task has silently been discarded because a newer
43*6dbdd20aSAndroid Build Coastguard Worker   * task was scheduled.
44*6dbdd20aSAndroid Build Coastguard Worker   */
45*6dbdd20aSAndroid Build Coastguard Worker  schedule(work: Callback): Promise<void> {
46*6dbdd20aSAndroid Build Coastguard Worker    const deferred = defer<void>();
47*6dbdd20aSAndroid Build Coastguard Worker    this.taskQueue.push({work, deferred});
48*6dbdd20aSAndroid Build Coastguard Worker
49*6dbdd20aSAndroid Build Coastguard Worker    if (!this.isRunning) {
50*6dbdd20aSAndroid Build Coastguard Worker      this.isRunning = true;
51*6dbdd20aSAndroid Build Coastguard Worker      this.runTaskQueue().finally(() => (this.isRunning = false));
52*6dbdd20aSAndroid Build Coastguard Worker    }
53*6dbdd20aSAndroid Build Coastguard Worker
54*6dbdd20aSAndroid Build Coastguard Worker    return deferred;
55*6dbdd20aSAndroid Build Coastguard Worker  }
56*6dbdd20aSAndroid Build Coastguard Worker
57*6dbdd20aSAndroid Build Coastguard Worker  private async runTaskQueue(): Promise<void> {
58*6dbdd20aSAndroid Build Coastguard Worker    let task: Task | undefined;
59*6dbdd20aSAndroid Build Coastguard Worker
60*6dbdd20aSAndroid Build Coastguard Worker    while ((task = this.taskQueue.shift())) {
61*6dbdd20aSAndroid Build Coastguard Worker      if (this.taskQueue.length > 0) {
62*6dbdd20aSAndroid Build Coastguard Worker        task.deferred.resolve();
63*6dbdd20aSAndroid Build Coastguard Worker      } else {
64*6dbdd20aSAndroid Build Coastguard Worker        try {
65*6dbdd20aSAndroid Build Coastguard Worker          await task.work();
66*6dbdd20aSAndroid Build Coastguard Worker          task.deferred.resolve();
67*6dbdd20aSAndroid Build Coastguard Worker        } catch (e) {
68*6dbdd20aSAndroid Build Coastguard Worker          task.deferred.reject(e);
69*6dbdd20aSAndroid Build Coastguard Worker        }
70*6dbdd20aSAndroid Build Coastguard Worker      }
71*6dbdd20aSAndroid Build Coastguard Worker    }
72*6dbdd20aSAndroid Build Coastguard Worker  }
73*6dbdd20aSAndroid Build Coastguard Worker}
74