forked from kfastov/tgcli
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathstore-lock.js
More file actions
159 lines (145 loc) · 3.83 KB
/
Copy pathstore-lock.js
File metadata and controls
159 lines (145 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import fs from 'fs';
import path from 'path';
function lockPayload() {
return JSON.stringify({
pid: process.pid,
startedAt: new Date().toISOString(),
});
}
export function readStoreLock(storeDir) {
const lockPath = path.join(storeDir, 'LOCK');
try {
const raw = fs.readFileSync(lockPath, 'utf8');
return {
exists: true,
path: lockPath,
info: raw.trim(),
};
} catch (error) {
if (error.code === 'ENOENT') {
return { exists: false, path: lockPath, info: null };
}
throw error;
}
}
function isPidAlive(pid) {
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
function parseLockPid(raw) {
try {
const parsed = JSON.parse(raw);
return typeof parsed?.pid === 'number' ? parsed.pid : null;
} catch {
return null;
}
}
function removeStaleLockFile(lockPath, pid, label) {
try {
fs.unlinkSync(lockPath);
} catch (error) {
if (error?.code === 'ENOENT') {
return;
}
const details = error?.message ? `: ${error.message}` : '';
throw new Error(`Found stale ${label} for dead pid ${pid}, but could not remove ${lockPath}${details}`);
}
}
function getAliveReadLocks(storeDir) {
let entries;
try {
entries = fs.readdirSync(storeDir);
} catch {
return [];
}
const alive = [];
for (const name of entries) {
if (!name.startsWith('LOCK.read.')) continue;
const filePath = path.join(storeDir, name);
let raw;
try { raw = fs.readFileSync(filePath, 'utf8').trim(); } catch { continue; }
const pid = parseLockPid(raw);
if (!pid) continue;
if (isPidAlive(pid)) {
alive.push({ name, pid });
} else {
removeStaleLockFile(filePath, pid, 'read lock');
}
}
return alive;
}
export function acquireStoreLock(storeDir, _retried = false) {
const lockPath = path.join(storeDir, 'LOCK');
fs.mkdirSync(storeDir, { recursive: true });
// Check for alive read locks before acquiring write lock
const aliveReaders = getAliveReadLocks(storeDir);
if (aliveReaders.length > 0) {
const pids = aliveReaders.map(r => r.pid).join(', ');
throw new Error(`Store has active readers (pids: ${pids}), cannot acquire write lock`);
}
try {
const fd = fs.openSync(lockPath, 'wx');
fs.writeFileSync(fd, lockPayload());
fs.closeSync(fd);
} catch (error) {
if (error.code === 'EEXIST') {
const info = readStoreLock(storeDir);
const pid = parseLockPid(info.info);
if (pid && !isPidAlive(pid) && !_retried) {
removeStaleLockFile(lockPath, pid, 'store lock');
return acquireStoreLock(storeDir, true);
}
const details = info.info ? ` (${info.info})` : '';
throw new Error(`Store is locked by another process${details}`);
}
throw error;
}
let released = false;
return () => {
if (released) {
return;
}
released = true;
try {
fs.unlinkSync(lockPath);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
};
}
export function acquireReadLock(storeDir) {
fs.mkdirSync(storeDir, { recursive: true });
// Check for alive write lock
const writeLock = readStoreLock(storeDir);
if (writeLock.exists) {
const pid = parseLockPid(writeLock.info);
if (pid && !isPidAlive(pid)) {
removeStaleLockFile(writeLock.path, pid, 'store lock');
} else {
const details = writeLock.info ? ` (${writeLock.info})` : '';
throw new Error(`Store is locked by a writer${details}`);
}
}
const readLockPath = path.join(storeDir, `LOCK.read.${process.pid}`);
fs.writeFileSync(readLockPath, lockPayload());
let released = false;
return () => {
if (released) {
return;
}
released = true;
try {
fs.unlinkSync(readLockPath);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
};
}