-
-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathlock-service.ts
88 lines (72 loc) · 2.74 KB
/
lock-service.ts
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
import * as lockfile from "lockfile";
import * as path from "path";
import { cache } from "../decorators";
export class LockService implements ILockService {
private currentlyLockedFiles: string[] = [];
@cache()
private get defaultLockFilePath(): string {
return this.getAbsoluteLockFilePath("lockfile.lock");
}
private getAbsoluteLockFilePath(relativeLockFilePath: string) {
return path.join(this.$settingsService.getProfileDir(), relativeLockFilePath);
}
private get defaultLockParams(): ILockOptions {
// We'll retry 100 times and time between retries is 100ms, i.e. full wait in case we are unable to get lock will be 10 seconds.
// In case lock is older than the `stale` value, consider it stale and try to get a new lock.
const lockParams: ILockOptions = {
retryWait: 100,
retries: 100,
stale: 30 * 1000,
};
return lockParams;
}
constructor(private $fs: IFileSystem,
private $settingsService: ISettingsService,
private $processService: IProcessService) {
this.$processService.attachToProcessExitSignals(this, () => {
const locksToRemove = _.clone(this.currentlyLockedFiles);
_.each(locksToRemove, lock => {
this.unlock(lock);
});
});
}
public async executeActionWithLock<T>(action: () => Promise<T>, lockFilePath?: string, lockOpts?: ILockOptions): Promise<T> {
const resolvedLockFilePath = await this.lock(lockFilePath, lockOpts);
try {
const result = await action();
return result;
} finally {
this.unlock(resolvedLockFilePath);
}
}
private lock(lockFilePath?: string, lockOpts?: ILockOptions): Promise<string> {
const { filePath, fileOpts } = this.getLockFileSettings(lockFilePath, lockOpts);
this.currentlyLockedFiles.push(filePath);
// Prevent ENOENT error when the dir, where lock should be created, does not exist.
this.$fs.ensureDirectoryExists(path.dirname(filePath));
return new Promise<string>((resolve, reject) => {
lockfile.lock(filePath, fileOpts, (err: Error) => {
err ? reject(new Error(`Timeout while waiting for lock "${filePath}"`)) : resolve(filePath);
});
});
}
private unlock(lockFilePath?: string): void {
const { filePath } = this.getLockFileSettings(lockFilePath);
_.remove(this.currentlyLockedFiles, e => e === lockFilePath);
lockfile.unlockSync(filePath);
}
private getLockFileSettings(filePath?: string, fileOpts?: ILockOptions): { filePath: string, fileOpts: ILockOptions } {
if (filePath && !path.isAbsolute(filePath)) {
filePath = this.getAbsoluteLockFilePath(filePath);
}
filePath = filePath || this.defaultLockFilePath;
fileOpts = fileOpts || this.defaultLockParams;
return {
filePath,
fileOpts
};
}
}
$injector.register("lockService", LockService);
// backwards compatibility
$injector.register("lockfile", LockService);