Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify timeout utils #16851

Merged
merged 5 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/connect-popup/e2e/tests/popup-close.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BrowserContext, Page, test } from '@playwright/test';

import { TrezorUserEnvLink } from '@trezor/trezor-user-env-link';
import { addDashesToSpaces, createTimeoutPromise } from '@trezor/utils';
import { addDashesToSpaces, resolveAfter } from '@trezor/utils';

import {
checkHasLogs,
Expand Down Expand Up @@ -146,7 +146,7 @@ test.afterEach(async ({ context: _context }, testInfo) => {
await context.close();
await new Promise(resolve => setTimeout(resolve, 1000));
}
await createTimeoutPromise(WAIT_AFTER_TEST);
await resolveAfter(WAIT_AFTER_TEST);
});

test(`popup closed by user`, async ({ page, context }) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/connect/src/api/composeTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ export default class ComposeTransaction extends AbstractMethod<'composeTransacti
discovery.stop();

if (!discovery.completed) {
await resolveAfter(501).promise; // temporary solution, TODO: immediately resolve will cause "device call in progress"
await resolveAfter(501); // temporary solution, TODO: immediately resolve will cause "device call in progress"
}

const account = discovery.accounts[uiResp.payload];
Expand Down Expand Up @@ -336,7 +336,7 @@ export default class ComposeTransaction extends AbstractMethod<'composeTransacti
// show error view
this.postMessage(createUiMessage(UI.INSUFFICIENT_FUNDS));
// wait few seconds...
await resolveAfter(2000, null).promise;
await resolveAfter(2000);

// and go back to discovery
return 'change-account';
Expand Down
2 changes: 1 addition & 1 deletion packages/connect/src/api/getAccountInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ export default class GetAccountInfo extends AbstractMethod<'getAccountInfo', Req
const account = discovery.accounts[uiResp.payload];

if (!discovery.completed) {
await resolveAfter(501).promise; // temporary solution, TODO: immediately resolve will cause "device call in progress"
await resolveAfter(501); // temporary solution, TODO: immediately resolve will cause "device call in progress"
}

// get account info from backend
Expand Down
6 changes: 3 additions & 3 deletions packages/connect/src/core/onCallFirmwareUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { randomBytes } from 'crypto';

import { createTimeoutPromise } from '@trezor/utils';
import { resolveAfter } from '@trezor/utils';
import { isEqual, isNewer } from '@trezor/utils/src/versionUtils';

import {
Expand Down Expand Up @@ -90,7 +90,7 @@ const waitForReconnectedDevice = async (
}),
);

await createTimeoutPromise(2000);
await resolveAfter(2000);
try {
reconnectedDevice = deviceList.getOnlyDevice();
} catch {
Expand Down Expand Up @@ -460,7 +460,7 @@ export const onCallFirmwareUpdate = async ({

// This delay is crucial see https://github.com/trezor/trezor-firmware/issues/1983
if (device.features.major_version === 1) {
await createTimeoutPromise(2000);
await resolveAfter(2000);
}
reconnectedDevice = await waitForReconnectedDevice(
{ bootloader: true, method: 'auto' },
Expand Down
6 changes: 3 additions & 3 deletions packages/connect/src/device/Device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
Deferred,
TypedEmitter,
createDeferred,
createTimeoutPromise,
isArrayMember,
resolveAfter,
serializeError,
versionUtils,
} from '@trezor/utils';
Expand Down Expand Up @@ -343,7 +343,7 @@ export class Device extends TypedEmitter<DeviceEvents> {
// call only once, right after device creation
async handshake(delay?: number) {
if (delay) {
await createTimeoutPromise(501 + delay);
await resolveAfter(501 + delay);
}

while (true) {
Expand Down Expand Up @@ -384,7 +384,7 @@ export class Device extends TypedEmitter<DeviceEvents> {
this.unreadableError = error?.message;
this.emitLifecycle(DEVICE.CONNECT_UNACQUIRED);
} else {
await createTimeoutPromise(501);
await resolveAfter(501);
continue;
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/connect/src/device/DeviceCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { MessagesSchema as Messages } from '@trezor/protobuf';
import { Assert } from '@trezor/schema-utils';
import { Session, Transport } from '@trezor/transport';
import { createTimeoutPromise, versionUtils } from '@trezor/utils';
import { resolveAfter, versionUtils } from '@trezor/utils';

import { ERRORS } from '../constants';
import { Device } from './Device';
Expand Down Expand Up @@ -561,7 +561,7 @@ export class DeviceCommands {
*/
try {
// UI_EVENT is send right before ButtonAck, make sure that ButtonAck is sent
await createTimeoutPromise(1);
await resolveAfter(1);
await this.device.acquire();
await cancelPrompt(this.device, false);
} catch {
Expand Down
7 changes: 2 additions & 5 deletions packages/connect/src/device/DeviceList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
createDeferred,
getSynchronize,
isNotUndefined,
resolveAfter,
} from '@trezor/utils';

import { ERRORS } from '../constants';
Expand All @@ -26,7 +27,6 @@ import { Device } from './Device';
import { getBridgeInfo } from '../data/transportInfo';
import { ConnectSettings, DeviceUniquePath, StaticSessionId } from '../types';
import { typedObjectKeys } from '../types/utils';
import { abortablePromise } from '../utils/abortablePromise';
import { initLog } from '../utils/debug';

const createAuthPenaltyManager = (priority: number) => {
Expand Down Expand Up @@ -396,10 +396,7 @@ export class DeviceList extends TypedEmitter<DeviceListEvents> implements IDevic
}

private createReconnectDelay(signal: AbortSignal) {
const { promise, resolve } = abortablePromise(signal);
const timeout = setTimeout(resolve, 1000);

return promise.finally(() => clearTimeout(timeout));
return resolveAfter(1000, signal);
}

private scheduleUpgradeCheck(apiType: TransportApiType, initParams: InitParams) {
Expand Down
13 changes: 0 additions & 13 deletions packages/connect/src/utils/abortablePromise.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/suite-desktop-core/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isMacOs } from '@trezor/env-utils';
import { validateIpcMessage } from '@trezor/ipc-proxy';
import type { HandshakeClient } from '@trezor/suite-desktop-api';
import { TimerId } from '@trezor/type-utils';
import { createDeferred, createTimeoutPromise } from '@trezor/utils';
import { createDeferred, resolveAfter } from '@trezor/utils';

import { hangDetect } from './hang-detect';
import { processStatePatch, restartApp } from './libs/app-utils';
Expand Down Expand Up @@ -300,7 +300,7 @@ const init = async () => {
// await quitting all registered modules
Promise.allSettled([quitModules(), quitTorModule(), quitBackgroundModules()]),
// or timeout after 5s
createTimeoutPromise(5000),
resolveAfter(5000),
]);

// global cleanup
Expand Down
4 changes: 2 additions & 2 deletions packages/suite/src/components/suite/WordInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import styled from 'styled-components';
import { Select, variables } from '@trezor/components';
import TrezorConnect, { UI } from '@trezor/connect';
import { bip39 } from '@trezor/crypto-utils';
import { createTimeoutPromise } from '@trezor/utils';
import { resolveAfter } from '@trezor/utils';

import { useTranslation } from 'src/hooks/suite/useTranslation';

Expand Down Expand Up @@ -80,7 +80,7 @@ export const WordInput = memo(() => {
translationString('TR_WORD_DOES_NOT_EXIST', { word: inputValue })
}
onChange={async (item: Option, ref?: SelectInstance<Option, boolean> | null) => {
await createTimeoutPromise(600);
await resolveAfter(600);
TrezorConnect.uiResponse({ type: UI.RECEIVE_WORD, payload: item.value });
ref?.clearValue();
}}
Expand Down
4 changes: 2 additions & 2 deletions packages/suite/src/components/suite/WordInputAdvanced.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import styled from 'styled-components';
import { Button, KEYBOARD_CODE, PinButton } from '@trezor/components';
import TrezorConnect, { DeviceModelInternal, UI } from '@trezor/connect';
import { HELP_CENTER_ADVANCED_RECOVERY_URL } from '@trezor/urls';
import { createTimeoutPromise } from '@trezor/utils';
import { resolveAfter } from '@trezor/utils';

import { DeviceMatrixExplanation, Translation, TrezorLink } from 'src/components/suite';

Expand Down Expand Up @@ -40,7 +40,7 @@ interface WordInputAdvancedProps {

export const WordInputAdvanced = ({ count }: WordInputAdvancedProps) => {
const onSubmit = useCallback(async (value: string) => {
await createTimeoutPromise(600);
await resolveAfter(600);
TrezorConnect.uiResponse({ type: UI.RECEIVE_WORD, payload: value });
}, []);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Network, NetworkAccount, NetworkSymbol } from '@suite-common/wallet-con
import { selectSelectedDevice } from '@suite-common/wallet-core';
import { UnavailableCapabilities } from '@trezor/connect';
import { isDesktop } from '@trezor/env-utils';
import { createTimeoutPromise } from '@trezor/utils';
import { resolveAfter } from '@trezor/utils';

import { openDeferredModal, openModal } from 'src/actions/suite/modalActions';
import { toggleTor } from 'src/actions/suite/suiteActions';
Expand Down Expand Up @@ -111,7 +111,7 @@ export const AddCoinjoinAccountButton = ({ network, selectedAccount }: AddCoinjo
// When Tor was not loaded it means there was an error or user canceled it, stop the coinjoin account activation.
if (!isTorLoaded) return;
}
await createTimeoutPromise(1000); // TODO fix properly: https://github.com/trezor/trezor-suite/issues/6902
await resolveAfter(1000); // TODO fix properly: https://github.com/trezor/trezor-suite/issues/6902
await createAccount();
};

Expand Down
4 changes: 2 additions & 2 deletions packages/transport-bridge/tests/http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getFreePort } from '@trezor/node-utils';
import { AbstractApi } from '@trezor/transport/src/api/abstract';
import { UdpApi } from '@trezor/transport/src/api/udp';
import { bridgeApiCall } from '@trezor/transport/src/utils/bridgeApiCall';
import { createTimeoutPromise } from '@trezor/utils';
import { resolveAfter } from '@trezor/utils';

import { TrezordNode } from '../src/http';

Expand Down Expand Up @@ -703,7 +703,7 @@ describe('http', () => {
client.listen();
// it takes some tome for /listen request to propagate.
// todo: solve later
await createTimeoutPromise(1000);
await resolveAfter(1000);

return {
server,
Expand Down
4 changes: 2 additions & 2 deletions packages/transport/src/api/udp.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import UDP from 'dgram';

import { arrayPartition, createTimeoutPromise, isNotUndefined } from '@trezor/utils';
import { arrayPartition, isNotUndefined, resolveAfter } from '@trezor/utils';

import {
AbstractApi,
Expand Down Expand Up @@ -36,7 +36,7 @@ export class UdpApi extends AbstractApi {

private async listenLoop() {
while (this.listening) {
await createTimeoutPromise(500);
await resolveAfter(500);
if (!this.listening) break;
await this.enumerate(this.listenAbortController.signal);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/transport/src/api/usb.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createDeferred, createTimeoutPromise, getSynchronize } from '@trezor/utils';
import { createDeferred, getSynchronize, resolveAfter } from '@trezor/utils';

import { AbstractApi, AbstractApiConstructorParams, DEVICE_TYPE } from './abstract';
import {
Expand Down Expand Up @@ -277,7 +277,7 @@ export class UsbApi extends AbstractApi {
return res;
}

await createTimeoutPromise(100 * i);
await resolveAfter(100 * i);
}

return this.openInternal(path, first, signal);
Expand Down
4 changes: 2 additions & 2 deletions packages/transport/src/transports/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
bridge as protocolBridge,
v1 as protocolV1,
} from '@trezor/protocol';
import { createTimeoutPromise, versionUtils } from '@trezor/utils';
import { resolveAfter, versionUtils } from '@trezor/utils';

import {
AbstractTransport,
Expand Down Expand Up @@ -146,7 +146,7 @@ export class BridgeTransport extends AbstractTransport {
this.emit('transport-error', response.error);
break;
}
await createTimeoutPromise(1000);
await resolveAfter(1000);
} else {
this.handleDescriptorsChange(response.payload);
}
Expand Down
2 changes: 0 additions & 2 deletions packages/utils/src/createTimeoutPromise.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export * from './createCooldown';
export * from './createDeferred';
export * from './createDeferredManager';
export * from './createLazy';
export * from './createTimeoutPromise';
export * from './extractUrlsFromText';
export * from './getLocaleSeparators';
export * from './getMutex';
Expand Down
14 changes: 9 additions & 5 deletions packages/utils/src/resolveAfter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { createDeferred } from './createDeferred';

export const resolveAfter = <T = void>(msec: number, value?: T) => {
export const resolveAfter = <T = void>(msec: number, signal?: AbortSignal, value?: T) => {
marekrjpolak marked this conversation as resolved.
Show resolved Hide resolved
const { promise, reject, resolve } = createDeferred<T>();
const timeout = setTimeout(resolve, msec, value);

return {
promise: promise.finally(() => clearTimeout(timeout)),
reject,
};
const onAbort = () => reject(signal?.reason);
signal?.addEventListener('abort', onAbort);
if (signal?.aborted) onAbort();
marekrjpolak marked this conversation as resolved.
Show resolved Hide resolved

return promise.finally(() => {
clearTimeout(timeout);
signal?.removeEventListener('abort', onAbort);
});
};
7 changes: 4 additions & 3 deletions packages/utils/tests/resolveAfter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ describe('resolveAfter', () => {
jest.useFakeTimers();

it('resolves after specified time', async () => {
const { promise } = resolveAfter(200, 'foo');
const promise = resolveAfter(200, undefined, 'foo');

jest.advanceTimersByTime(200);

await expect(promise).resolves.toBe('foo');
});

it('rejects if the promise is rejected', async () => {
const { promise, reject } = resolveAfter(200);
const abort = new AbortController();
const promise = resolveAfter(200, abort.signal);

// Reject the promise after 100ms
setTimeout(() => reject(new Error('bar')), 100);
setTimeout(() => abort.abort(new Error('bar')), 100);

jest.advanceTimersByTime(100);

Expand Down
Loading