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