Skip to content
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
24 changes: 16 additions & 8 deletions src/webusb_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ enum EndpointCommand {
}

struct PendingTransfer {
deadline: Instant,
deadline: Option<Instant>,
response: Option<TransferResponse>,
}

Expand Down Expand Up @@ -383,7 +383,7 @@ fn handle_endpoint_command<DIR: nusb::transfer::EndpointDirection>(

endpoint.submit(buffer);
pending.push_back(PendingTransfer {
deadline: Instant::now() + timeout,
deadline: transfer_deadline(timeout),
response: Some(response),
});
}
Expand Down Expand Up @@ -417,7 +417,10 @@ fn cancel_pending_transfers(pending: &mut VecDeque<PendingTransfer>) {
fn expire_timed_out_transfers(pending: &mut VecDeque<PendingTransfer>) {
let now = Instant::now();
for pending_transfer in pending {
if pending_transfer.deadline <= now {
if pending_transfer
.deadline
.is_some_and(|deadline| deadline <= now)
{
if let Some(response) = pending_transfer.response.take() {
let _ = response.send(Err(format!(
"{:?}",
Expand All @@ -434,16 +437,21 @@ fn next_wait(pending: &VecDeque<PendingTransfer>) -> Duration {
pending
.iter()
.filter(|pending_transfer| pending_transfer.response.is_some())
.map(|pending_transfer| {
pending_transfer
.deadline
.saturating_duration_since(Instant::now())
})
.filter_map(|pending_transfer| pending_transfer.deadline)
.map(|deadline| deadline.saturating_duration_since(Instant::now()))
.min()
.map(|timeout| timeout.min(ENDPOINT_POLL_TIMEOUT))
.unwrap_or(ENDPOINT_POLL_TIMEOUT)
}

fn transfer_deadline(timeout: Duration) -> Option<Instant> {
if timeout.is_zero() {
None
} else {
Some(Instant::now() + timeout)
}
}

#[napi(object)]
pub struct UsbEndpoint {
#[napi(writable = false)]
Expand Down
24 changes: 24 additions & 0 deletions test/webusb.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,30 @@ describe('Transfers', () => {
await device.claimInterface(0);
});

it('does not cancel transfer IN by default', async () => {
const pending = { status: 'pending' };
const transfer = device.transferIn(3, b2.byteLength).then(
value => ({ status: 'fulfilled', value }),
reason => ({ status: 'rejected', reason })
);

const result = await Promise.race([
transfer,
new Promise(resolve => setTimeout(() => resolve(pending), 1200)),
]);

assert.notEqual(result.status, 'rejected', result.reason && result.reason.message);

if (result.status === 'pending') {
await device.transferOut(4, b2);
const completed = await transfer;

assert.equal(completed.status, 'fulfilled', completed.reason && completed.reason.message);
assert.equal(completed.value.status, 'ok');
assert.equal(completed.value.data.byteLength, b2.byteLength);
}
});

it('should transfer OUT', async () => {
const transferResult = await device.transferOut(4, b2);

Expand Down
16 changes: 9 additions & 7 deletions tsc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { nativeGetDevices, nativeFindDeviceByIds, nativeFindDeviceBySerial, UsbD
/**
* Hidden
*/
const DEFAULT_TIMEOUT = 1000;
const DEFAULT_TRANSFER_TIMEOUT = 0;
const DEFAULT_CONTROL_TRANSFER_TIMEOUT = 1000;
const DEFAULT_ISOCHRONOUS_TRANSFER_TIMEOUT = 1000;

/**
* Hidden
Expand All @@ -24,7 +26,7 @@ const toUint8Array = (data: BufferSource): Uint8Array => {
/**
* Hidden
*/
UsbDevice.prototype.controlTransferIn = async function (setup: USBControlTransferParameters, length: number, timeout = DEFAULT_TIMEOUT): Promise<USBInTransferResult> {
UsbDevice.prototype.controlTransferIn = async function (setup: USBControlTransferParameters, length: number, timeout = DEFAULT_CONTROL_TRANSFER_TIMEOUT): Promise<USBInTransferResult> {
const res = await this.nativeControlTransferIn(setup, timeout, length);
return {
data: res ? new DataView(res.buffer) : undefined,
Expand All @@ -35,7 +37,7 @@ UsbDevice.prototype.controlTransferIn = async function (setup: USBControlTransfe
/**
* Hidden
*/
UsbDevice.prototype.controlTransferOut = async function (setup: USBControlTransferParameters, data?: BufferSource, timeout = DEFAULT_TIMEOUT): Promise<USBOutTransferResult> {
UsbDevice.prototype.controlTransferOut = async function (setup: USBControlTransferParameters, data?: BufferSource, timeout = DEFAULT_CONTROL_TRANSFER_TIMEOUT): Promise<USBOutTransferResult> {
const res = await this.nativeControlTransferOut(setup, timeout, data === undefined ? undefined : toUint8Array(data));
return {
bytesWritten: res,
Expand All @@ -46,7 +48,7 @@ UsbDevice.prototype.controlTransferOut = async function (setup: USBControlTransf
/**
* Hidden
*/
UsbDevice.prototype.transferIn = async function (endpointNumber: number, length: number, timeout = DEFAULT_TIMEOUT): Promise<USBInTransferResult> {
UsbDevice.prototype.transferIn = async function (endpointNumber: number, length: number, timeout = DEFAULT_TRANSFER_TIMEOUT): Promise<USBInTransferResult> {
const res = await this.nativeTransferIn(endpointNumber, timeout, length);
return {
data: res ? new DataView(res.buffer) : undefined,
Expand All @@ -57,7 +59,7 @@ UsbDevice.prototype.transferIn = async function (endpointNumber: number, length:
/**
* Hidden
*/
UsbDevice.prototype.transferOut = async function (endpointNumber: number, data: BufferSource, timeout = DEFAULT_TIMEOUT): Promise<USBOutTransferResult> {
UsbDevice.prototype.transferOut = async function (endpointNumber: number, data: BufferSource, timeout = DEFAULT_TRANSFER_TIMEOUT): Promise<USBOutTransferResult> {
const res = await this.nativeTransferOut(endpointNumber, timeout, toUint8Array(data));
return {
bytesWritten: res,
Expand All @@ -68,15 +70,15 @@ UsbDevice.prototype.transferOut = async function (endpointNumber: number, data:
/**
* Hidden
*/
UsbDevice.prototype.isochronousTransferIn = async function (endpointNumber: number, packetLengths: number[], timeout = DEFAULT_TIMEOUT): Promise<USBIsochronousInTransferResult> {
UsbDevice.prototype.isochronousTransferIn = async function (endpointNumber: number, packetLengths: number[], timeout = DEFAULT_ISOCHRONOUS_TRANSFER_TIMEOUT): Promise<USBIsochronousInTransferResult> {
const res = await this.nativeIsochronousTransferIn(endpointNumber, packetLengths, timeout);
return res;
}

/**
* Hidden
*/
UsbDevice.prototype.isochronousTransferOut = async function (endpointNumber: number, data: BufferSource, packetLengths: number[], timeout = DEFAULT_TIMEOUT): Promise<USBIsochronousOutTransferResult> {
UsbDevice.prototype.isochronousTransferOut = async function (endpointNumber: number, data: BufferSource, packetLengths: number[], timeout = DEFAULT_ISOCHRONOUS_TRANSFER_TIMEOUT): Promise<USBIsochronousOutTransferResult> {
const res = await this.nativeIsochronousTransferOut(endpointNumber, toUint8Array(data), packetLengths, timeout);
return res;
}
Expand Down
Loading