2022-07-07 08:27:20 -04:00
|
|
|
const { promisify } = require('util')
|
|
|
|
const OError = require('@overleaf/o-error')
|
|
|
|
const Lockfile = require('lockfile')
|
2020-02-19 06:14:37 -05:00
|
|
|
const Errors = require('./Errors')
|
2022-07-07 08:27:20 -04:00
|
|
|
const fsPromises = require('fs/promises')
|
2020-02-19 06:14:37 -05:00
|
|
|
const Path = require('path')
|
2017-09-22 11:19:33 -04:00
|
|
|
|
2022-07-07 08:27:20 -04:00
|
|
|
const LOCK_OPTS = {
|
|
|
|
pollPeriod: 1000, // 1s between each test of the lock
|
|
|
|
wait: 15000, // 15s maximum time to spend trying to get the lock
|
|
|
|
stale: 5 * 60 * 1000, // 5 mins time until lock auto expires
|
|
|
|
}
|
|
|
|
|
|
|
|
const PromisifiedLockfile = {
|
|
|
|
lock: promisify(Lockfile.lock),
|
|
|
|
unlock: promisify(Lockfile.unlock),
|
|
|
|
}
|
|
|
|
|
|
|
|
async function acquire(path) {
|
|
|
|
try {
|
|
|
|
await PromisifiedLockfile.lock(path, LOCK_OPTS)
|
|
|
|
} catch (err) {
|
|
|
|
if (err.code === 'EEXIST') {
|
|
|
|
throw new Errors.AlreadyCompilingError('compile in progress')
|
|
|
|
} else {
|
|
|
|
const dir = Path.dirname(path)
|
|
|
|
const [statLock, statDir, readdirDir] = await Promise.allSettled([
|
|
|
|
fsPromises.lstat(path),
|
|
|
|
fsPromises.lstat(dir),
|
|
|
|
fsPromises.readdir(dir),
|
|
|
|
])
|
|
|
|
OError.tag(err, 'unable to get lock', {
|
|
|
|
statLock: unwrapPromiseResult(statLock),
|
|
|
|
statDir: unwrapPromiseResult(statDir),
|
|
|
|
readdirDir: unwrapPromiseResult(readdirDir),
|
|
|
|
})
|
|
|
|
throw err
|
2020-02-19 06:14:37 -05:00
|
|
|
}
|
2022-07-07 08:27:20 -04:00
|
|
|
}
|
|
|
|
return new Lock(path)
|
2020-02-19 06:14:37 -05:00
|
|
|
}
|
2022-07-07 08:27:20 -04:00
|
|
|
|
|
|
|
class Lock {
|
|
|
|
constructor(path) {
|
|
|
|
this._path = path
|
|
|
|
}
|
|
|
|
|
|
|
|
async release() {
|
|
|
|
await PromisifiedLockfile.unlock(this._path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function unwrapPromiseResult(result) {
|
|
|
|
if (result.status === 'fulfilled') {
|
|
|
|
return result.value
|
|
|
|
} else {
|
|
|
|
return result.reason
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = { acquire }
|