Gaëtan Renaudeau
7 years ago
8 changed files with 111 additions and 55 deletions
@ -0,0 +1,51 @@ |
|||||
|
// @flow
|
||||
|
import createSemaphore from 'semaphore' |
||||
|
import type Transport from '@ledgerhq/hw-transport' |
||||
|
import CommNodeHid from '@ledgerhq/hw-transport-node-hid' |
||||
|
|
||||
|
// all open to device must use openDevice so we can prevent race conditions
|
||||
|
// and guarantee we do one device access at a time. It also will handle the .close()
|
||||
|
// NOTE optim: in the future we can debounce the close & reuse the same transport instance.
|
||||
|
|
||||
|
type WithDevice = (devicePath: string) => <T>(job: (Transport<*>) => Promise<T>) => Promise<T> |
||||
|
|
||||
|
const semaphorePerDevice = {} |
||||
|
|
||||
|
export const withDevice: WithDevice = devicePath => { |
||||
|
const { FORK_TYPE } = process.env |
||||
|
if (FORK_TYPE !== 'devices') { |
||||
|
console.warn( |
||||
|
`deviceAccess is only expected to be used in process 'devices'. Any other usage may lead to race conditions. (Got: '${FORK_TYPE}')`, |
||||
|
) |
||||
|
} |
||||
|
const sem = |
||||
|
semaphorePerDevice[devicePath] || (semaphorePerDevice[devicePath] = createSemaphore(1)) |
||||
|
return job => |
||||
|
takeSemaphorePromise(sem, async () => { |
||||
|
const t = await CommNodeHid.open(devicePath) |
||||
|
try { |
||||
|
const res = await job(t) |
||||
|
// $FlowFixMe
|
||||
|
return res |
||||
|
} finally { |
||||
|
t.close() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function takeSemaphorePromise<T>(sem, f: () => Promise<T>): Promise<T> { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
sem.take(() => { |
||||
|
f().then( |
||||
|
r => { |
||||
|
sem.leave() |
||||
|
resolve(r) |
||||
|
}, |
||||
|
e => { |
||||
|
sem.leave() |
||||
|
reject(e) |
||||
|
}, |
||||
|
) |
||||
|
}) |
||||
|
}) |
||||
|
} |
Loading…
Reference in new issue