@@ -3,53 +3,82 @@ import * as path from "path";
3
3
import { cache } from "../decorators" ;
4
4
5
5
export class LockFile implements ILockFile {
6
+ private currentlyLockedFiles : string [ ] = [ ] ;
6
7
7
8
@cache ( )
8
9
private get defaultLockFilePath ( ) : string {
9
- return path . join ( this . $settingsService . getProfileDir ( ) , "lockfile.lock" ) ;
10
+ return this . getAbsoluteLockFilePath ( "lockfile.lock" ) ;
10
11
}
11
12
12
- private get defaultLockParams ( ) : lockfile . Options {
13
+ private getAbsoluteLockFilePath ( relativeLockFilePath : string ) {
14
+ return path . join ( this . $settingsService . getProfileDir ( ) , relativeLockFilePath ) ;
15
+ }
16
+
17
+ private get defaultLockParams ( ) : ILockFileOptions {
13
18
// 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.
14
- // In case lock is older than 3 minutes , consider it stale and try to get a new lock.
15
- const lockParams : lockfile . Options = {
19
+ // In case lock is older than the `stale` value , consider it stale and try to get a new lock.
20
+ const lockParams : ILockFileOptions = {
16
21
retryWait : 100 ,
17
22
retries : 100 ,
18
- stale : 180 * 1000 ,
23
+ stale : 30 * 1000 ,
19
24
} ;
20
25
21
26
return lockParams ;
22
27
}
23
28
24
29
constructor ( private $fs : IFileSystem ,
25
- private $settingsService : ISettingsService ) {
30
+ private $settingsService : ISettingsService ,
31
+ private $processService : IProcessService ) {
32
+ this . $processService . attachToProcessExitSignals ( this , ( ) => {
33
+ const locksToRemove = _ . clone ( this . currentlyLockedFiles ) ;
34
+ _ . each ( locksToRemove , lock => {
35
+ this . unlock ( lock ) ;
36
+ } ) ;
37
+ } ) ;
26
38
}
27
39
28
- public lock ( lockFilePath ?: string , lockFileOpts ?: lockfile . Options ) : Promise < void > {
40
+ public lock ( lockFilePath ?: string , lockFileOpts ?: ILockFileOptions ) : Promise < string > {
29
41
const { filePath, fileOpts } = this . getLockFileSettings ( lockFilePath , lockFileOpts ) ;
42
+ this . currentlyLockedFiles . push ( filePath ) ;
30
43
31
44
// Prevent ENOENT error when the dir, where lock should be created, does not exist.
32
45
this . $fs . ensureDirectoryExists ( path . dirname ( filePath ) ) ;
33
46
34
- return new Promise < void > ( ( resolve , reject ) => {
47
+ return new Promise < string > ( ( resolve , reject ) => {
35
48
lockfile . lock ( filePath , fileOpts , ( err : Error ) => {
36
- err ? reject ( err ) : resolve ( ) ;
49
+ err ? reject ( new Error ( `Timeout while waiting for lock " ${ filePath } "` ) ) : resolve ( filePath ) ;
37
50
} ) ;
38
51
} ) ;
39
52
}
40
53
54
+ public async executeActionWithLock < T > ( action : ( ) => Promise < T > , lockFilePath ?: string , lockFileOpts ?: ILockFileOptions ) : Promise < T > {
55
+ const resolvedLockFilePath = await this . lock ( lockFilePath , lockFileOpts ) ;
56
+
57
+ try {
58
+ const result = await action ( ) ;
59
+ return result ;
60
+ } finally {
61
+ this . unlock ( resolvedLockFilePath ) ;
62
+ }
63
+ }
64
+
41
65
public unlock ( lockFilePath ?: string ) : void {
42
66
const { filePath } = this . getLockFileSettings ( lockFilePath ) ;
67
+ _ . remove ( this . currentlyLockedFiles , e => e === lockFilePath ) ;
43
68
lockfile . unlockSync ( filePath ) ;
44
69
}
45
70
46
- public check ( lockFilePath ?: string , lockFileOpts ?: lockfile . Options ) : boolean {
71
+ public check ( lockFilePath ?: string , lockFileOpts ?: ILockFileOptions ) : boolean {
47
72
const { filePath, fileOpts } = this . getLockFileSettings ( lockFilePath , lockFileOpts ) ;
48
73
49
74
return lockfile . checkSync ( filePath , fileOpts ) ;
50
75
}
51
76
52
- private getLockFileSettings ( filePath ?: string , fileOpts ?: lockfile . Options ) : { filePath : string , fileOpts : lockfile . Options } {
77
+ private getLockFileSettings ( filePath ?: string , fileOpts ?: ILockFileOptions ) : { filePath : string , fileOpts : ILockFileOptions } {
78
+ if ( filePath && ! path . isAbsolute ( filePath ) ) {
79
+ filePath = this . getAbsoluteLockFilePath ( filePath ) ;
80
+ }
81
+
53
82
filePath = filePath || this . defaultLockFilePath ;
54
83
fileOpts = fileOpts || this . defaultLockParams ;
55
84
0 commit comments