@@ -3,53 +3,82 @@ import * as path from "path";
33import { cache } from "../decorators" ;
44
55export class LockFile implements ILockFile {
6+ private currentlyLockedFiles : string [ ] = [ ] ;
67
78 @cache ( )
89 private get defaultLockFilePath ( ) : string {
9- return path . join ( this . $settingsService . getProfileDir ( ) , "lockfile.lock" ) ;
10+ return this . getAbsoluteLockFilePath ( "lockfile.lock" ) ;
1011 }
1112
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 {
1318 // 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 = {
1621 retryWait : 100 ,
1722 retries : 100 ,
18- stale : 180 * 1000 ,
23+ stale : 30 * 1000 ,
1924 } ;
2025
2126 return lockParams ;
2227 }
2328
2429 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+ } ) ;
2638 }
2739
28- public lock ( lockFilePath ?: string , lockFileOpts ?: lockfile . Options ) : Promise < void > {
40+ public lock ( lockFilePath ?: string , lockFileOpts ?: ILockFileOptions ) : Promise < string > {
2941 const { filePath, fileOpts } = this . getLockFileSettings ( lockFilePath , lockFileOpts ) ;
42+ this . currentlyLockedFiles . push ( filePath ) ;
3043
3144 // Prevent ENOENT error when the dir, where lock should be created, does not exist.
3245 this . $fs . ensureDirectoryExists ( path . dirname ( filePath ) ) ;
3346
34- return new Promise < void > ( ( resolve , reject ) => {
47+ return new Promise < string > ( ( resolve , reject ) => {
3548 lockfile . lock ( filePath , fileOpts , ( err : Error ) => {
36- err ? reject ( err ) : resolve ( ) ;
49+ err ? reject ( new Error ( `Timeout while waiting for lock " ${ filePath } "` ) ) : resolve ( filePath ) ;
3750 } ) ;
3851 } ) ;
3952 }
4053
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+
4165 public unlock ( lockFilePath ?: string ) : void {
4266 const { filePath } = this . getLockFileSettings ( lockFilePath ) ;
67+ _ . remove ( this . currentlyLockedFiles , e => e === lockFilePath ) ;
4368 lockfile . unlockSync ( filePath ) ;
4469 }
4570
46- public check ( lockFilePath ?: string , lockFileOpts ?: lockfile . Options ) : boolean {
71+ public check ( lockFilePath ?: string , lockFileOpts ?: ILockFileOptions ) : boolean {
4772 const { filePath, fileOpts } = this . getLockFileSettings ( lockFilePath , lockFileOpts ) ;
4873
4974 return lockfile . checkSync ( filePath , fileOpts ) ;
5075 }
5176
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+
5382 filePath = filePath || this . defaultLockFilePath ;
5483 fileOpts = fileOpts || this . defaultLockParams ;
5584
0 commit comments