flock.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. /** Lazy POSIX flock entry; importing it does not load a native addon. */
  2. import { createRequire } from 'node:module'
  3. import { dirname, join } from 'node:path'
  4. import { getSystemErrorName } from 'node:util'
  5. interface FlockBinding {
  6. tryLock(fd: number, callback: (errno: number) => void): void
  7. }
  8. let binding: FlockBinding | undefined
  9. function loadBinding(): FlockBinding {
  10. if (binding) return binding
  11. const { platform, arch } = process
  12. if (platform !== 'linux' && platform !== 'darwin') {
  13. throw Object.assign(new Error(`flock is not supported on ${platform}-${arch}`), {
  14. code: 'ERR_FLOCK_UNSUPPORTED_PLATFORM',
  15. syscall: 'flock',
  16. })
  17. }
  18. let filename = 'system.node'
  19. if (platform === 'linux') {
  20. // Node's report types omit the libc field supplied by Linux reports.
  21. const report = process.report.getReport() as { header: { glibcVersionRuntime?: string } }
  22. filename = join(report.header.glibcVersionRuntime ? 'glibc' : 'musl', filename)
  23. }
  24. const require = createRequire(import.meta.url)
  25. const manifest = require.resolve(`@deepseek-ai/node-addon-system-${platform}-${arch}/package.json`)
  26. binding = require(join(dirname(manifest), 'bin', filename)) as FlockBinding
  27. return binding
  28. }
  29. /**
  30. * Attempt an exclusive, nonblocking POSIX flock on the caller's descriptor.
  31. * The syscall runs in asynchronous work, so acquisition can occur after this
  32. * call returns. Keep fd open until the promise settles; the binding never
  33. * opens, duplicates, or closes it. Closing the locked descriptor releases the
  34. * lock once all descriptors for its open file description are closed.
  35. * @param fd - Open file descriptor to lock; ownership remains with the caller.
  36. * @returns A promise resolving to void on acquisition. Contention rejects with
  37. * EAGAIN/EWOULDBLOCK; other syscall failures also reject. Syscall errors carry
  38. * code, positive errno, and syscall='flock'. Native setup errors, unsupported
  39. * platforms, and addon loading failures reject; importing alone does not load it.
  40. */
  41. export async function tryLockExclusive(fd: number): Promise<void> {
  42. const errno = await new Promise<number>((resolve) => {
  43. loadBinding().tryLock(fd, resolve)
  44. })
  45. if (errno === 0) return
  46. const code = getSystemErrorName(-errno)
  47. throw Object.assign(new Error(`${code}: flock failed`), {
  48. code,
  49. errno,
  50. syscall: 'flock',
  51. })
  52. }