MCPcopy Create free account
hub / github.com/tiann/hapi / acquireRunnerLock

Function acquireRunnerLock

cli/src/persistence.ts:228–264  ·  view source on GitHub ↗
(
  maxAttempts: number = 5,
  delayIncrementMs: number = 200
)

Source from the content-addressed store, hash-verified

226 * Returns the file handle to hold for the runner's lifetime, or null if locked.
227 */
228export async function acquireRunnerLock(
229 maxAttempts: number = 5,
230 delayIncrementMs: number = 200
231): Promise<FileHandle | null> {
232 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
233 try {
234 // 'wx' ensures we only create if it doesn't exist (atomic lock acquisition)
235 const fileHandle = await open(configuration.runnerLockFile, 'wx');
236 // Write PID to lock file for debugging
237 await fileHandle.writeFile(String(process.pid));
238 return fileHandle;
239 } catch (error: any) {
240 if (error.code === 'EEXIST') {
241 // Lock file exists, check if process is still running
242 try {
243 const lockPid = readFileSync(configuration.runnerLockFile, 'utf-8').trim();
244 if (lockPid && !isNaN(Number(lockPid))) {
245 if (!isProcessAlive(Number(lockPid))) {
246 // Process doesn't exist, remove stale lock
247 unlinkSync(configuration.runnerLockFile);
248 continue; // Retry acquisition
249 }
250 }
251 } catch {
252 // Can't read lock file, might be corrupted
253 }
254 }
255
256 if (attempt === maxAttempts) {
257 return null;
258 }
259 const delayMs = attempt * delayIncrementMs;
260 await new Promise(resolve => setTimeout(resolve, delayMs));
261 }
262 }
263 return null;
264}
265
266/**
267 * Release runner lock by closing handle and deleting lock file

Callers 1

startRunnerFunction · 0.90

Calls 2

isProcessAliveFunction · 0.90
openFunction · 0.50

Tested by

no test coverage detected