Cancel in-flight attachment downloads on suspend/resume

This commit is contained in:
Scott Nonnenberg 2023-01-24 11:17:00 -08:00 committed by GitHub
parent 5dfdde998b
commit fbdfaf3962
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 1 deletions

View file

@ -1733,12 +1733,14 @@ export async function startApp(): Promise<void> {
window.Whisper.events.on('powerMonitorSuspend', () => {
log.info('powerMonitor: suspend');
server?.cancelInflightRequests('powerMonitorSuspend');
suspendTasksWithTimeout();
});
window.Whisper.events.on('powerMonitorResume', () => {
log.info('powerMonitor: resume');
server?.checkSockets();
server?.cancelInflightRequests('powerMonitorResume');
resumeTasksWithTimeout();
});

View file

@ -815,6 +815,7 @@ export type ConfirmCodeOptionsType = Readonly<{
export type WebAPIType = {
startRegistration(): unknown;
finishRegistration(baton: unknown): void;
cancelInflightRequests: (reason: string) => void;
cdsLookup: (options: CdsLookupOptionsType) => Promise<CDSResponseType>;
confirmCode: (
options: ConfirmCodeOptionsType
@ -1042,6 +1043,8 @@ export type TopLevelType = {
initialize: (options: InitializeOptionsType) => WebAPIConnectType;
};
type InflightCallback = (error: Error) => unknown;
// We first set up the data that won't change during this session of the app
export function initialize({
url,
@ -1152,6 +1155,29 @@ export function initialize({
},
});
const inflightRequests = new Set<(error: Error) => unknown>();
function registerInflightRequest(request: InflightCallback) {
inflightRequests.add(request);
}
function unregisterInFlightRequest(request: InflightCallback) {
inflightRequests.delete(request);
}
function cancelInflightRequests(reason: string) {
const logId = `cancelInflightRequests/${reason}`;
log.warn(`${logId}: Cancelling ${inflightRequests.size} requests`);
for (const request of inflightRequests) {
try {
request(new Error(`${logId}: Cancelled!`));
} catch (error: unknown) {
log.error(
`${logId}: Failed to cancel request: ${toLogFormat(error)}`
);
}
}
inflightRequests.clear();
log.warn(`${logId}: Done`);
}
let fetchForLinkPreviews: linkPreviewFetch.FetchFn;
if (proxyUrl) {
const agent = new ProxyAgent(proxyUrl);
@ -1163,6 +1189,7 @@ export function initialize({
// Thanks, function hoisting!
return {
authenticate,
cancelInflightRequests,
cdsLookup,
checkAccountExistence,
checkSockets,
@ -2391,11 +2418,25 @@ export function initialize({
abortSignal: abortController.signal,
});
return getStreamWithTimeout(stream, {
const streamPromise = getStreamWithTimeout(stream, {
name: `getAttachment(${cdnKey})`,
timeout: GET_ATTACHMENT_CHUNK_TIMEOUT,
abortController,
});
// Add callback to central store that would reject a promise
const { promise: cancelPromise, reject } = explodePromise<Uint8Array>();
const inflightRequest = (error: Error) => {
reject(error);
abortController.abort();
};
registerInflightRequest(inflightRequest);
try {
return Promise.race([streamPromise, cancelPromise]);
} finally {
unregisterInFlightRequest(inflightRequest);
}
}
type PutAttachmentResponseType = ServerAttachmentType & {