fix(task-store): prevent EPERM crash in lock release by tracking ownership in memory
This commit is contained in:
parent
dd58783f5e
commit
89cb3f8dbf
@ -13,10 +13,15 @@ function sleepSync(ms: number): void {
|
|||||||
Atomics.wait(arr, 0, 0, ms);
|
Atomics.wait(arr, 0, 0, ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fsErrorCode(err: unknown): string | undefined {
|
||||||
|
return (err as NodeJS.ErrnoException).code;
|
||||||
|
}
|
||||||
|
|
||||||
export class TaskStore {
|
export class TaskStore {
|
||||||
private readonly tasksFile: string;
|
private readonly tasksFile: string;
|
||||||
private readonly lockFile: string;
|
private readonly lockFile: string;
|
||||||
private readonly taktDir: string;
|
private readonly taktDir: string;
|
||||||
|
private lockOwned = false;
|
||||||
|
|
||||||
constructor(private readonly projectDir: string) {
|
constructor(private readonly projectDir: string) {
|
||||||
this.taktDir = path.join(projectDir, '.takt');
|
this.taktDir = path.join(projectDir, '.takt');
|
||||||
@ -94,10 +99,10 @@ export class TaskStore {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(this.lockFile, String(process.pid), { encoding: 'utf-8', flag: 'wx' });
|
fs.writeFileSync(this.lockFile, String(process.pid), { encoding: 'utf-8', flag: 'wx' });
|
||||||
|
this.lockOwned = true;
|
||||||
return;
|
return;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const nodeErr = err as NodeJS.ErrnoException;
|
if (fsErrorCode(err) !== 'EEXIST') {
|
||||||
if (nodeErr.code !== 'EEXIST') {
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,8 +125,8 @@ export class TaskStore {
|
|||||||
try {
|
try {
|
||||||
pidRaw = fs.readFileSync(this.lockFile, 'utf-8').trim();
|
pidRaw = fs.readFileSync(this.lockFile, 'utf-8').trim();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const nodeErr = err as NodeJS.ErrnoException;
|
const code = fsErrorCode(err);
|
||||||
if (nodeErr.code === 'ENOENT') {
|
if (code === 'ENOENT' || code === 'EPERM') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
@ -139,8 +144,7 @@ export class TaskStore {
|
|||||||
try {
|
try {
|
||||||
fs.unlinkSync(this.lockFile);
|
fs.unlinkSync(this.lockFile);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const nodeErr = err as NodeJS.ErrnoException;
|
if (fsErrorCode(err) !== 'ENOENT') {
|
||||||
if (nodeErr.code !== 'ENOENT') {
|
|
||||||
log.debug('Failed to remove stale lock, retrying.', { lockFile: this.lockFile, error: String(err) });
|
log.debug('Failed to remove stale lock, retrying.', { lockFile: this.lockFile, error: String(err) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,11 +155,11 @@ export class TaskStore {
|
|||||||
process.kill(pid, 0);
|
process.kill(pid, 0);
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const nodeErr = err as NodeJS.ErrnoException;
|
const code = fsErrorCode(err);
|
||||||
if (nodeErr.code === 'ESRCH') {
|
if (code === 'ESRCH') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (nodeErr.code === 'EPERM') {
|
if (code === 'EPERM') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
@ -163,19 +167,14 @@ export class TaskStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private releaseLock(): void {
|
private releaseLock(): void {
|
||||||
|
if (!this.lockOwned) return;
|
||||||
|
this.lockOwned = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const holder = fs.readFileSync(this.lockFile, 'utf-8').trim();
|
|
||||||
if (holder !== String(process.pid)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fs.unlinkSync(this.lockFile);
|
fs.unlinkSync(this.lockFile);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const nodeErr = err as NodeJS.ErrnoException;
|
if (fsErrorCode(err) === 'ENOENT') return;
|
||||||
if (nodeErr.code === 'ENOENT') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.debug('Failed to release tasks lock.', { lockFile: this.lockFile, error: String(err) });
|
log.debug('Failed to release tasks lock.', { lockFile: this.lockFile, error: String(err) });
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user