-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add file lock to prevent concurrent executions #6557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| *.log | ||
| .DS_Store | ||
| ._.DS_Store | ||
| .lock | ||
| scoop.sublime-workspace | ||
| test/installer/tmp/* | ||
| test/tmp/* | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,48 +6,74 @@ Set-StrictMode -Off | |
| . "$PSScriptRoot\..\lib\commands.ps1" | ||
| . "$PSScriptRoot\..\lib\help.ps1" | ||
|
|
||
| $subCommand = $Args[0] | ||
| $lock_file_path = "$PSScriptRoot\..\.lock" | ||
| $lock_stream = $null | ||
| $lock_show_waiting_message = $true | ||
|
|
||
| # for aliases where there's a local function, re-alias so the function takes precedence | ||
| $aliases = Get-Alias | Where-Object { $_.Options -notmatch 'ReadOnly|AllScope' } | ForEach-Object { $_.Name } | ||
| Get-ChildItem Function: | Where-Object -Property Name -In -Value $aliases | ForEach-Object { | ||
| Set-Alias -Name $_.Name -Value Local:$($_.Name) -Scope Script | ||
| while (-not $lock_stream) { | ||
| try { | ||
| $lock_stream = [System.IO.File]::Open($lock_file_path, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write) | ||
| } catch [System.IO.IOException] { | ||
| # Only deal with ERROR_SHARING_VIOLATION or ERROR_LOCK_VIOLATION. | ||
| $error_code = $_.Exception.HResult -band 0xFFFF | ||
| if ($error_code -notin 32, 33) { | ||
| throw | ||
| } | ||
|
|
||
| if ($lock_show_waiting_message) { | ||
| Write-Host 'Waiting for exclusive access...' | ||
| $lock_show_waiting_message = $false | ||
| } | ||
| Start-Sleep -Seconds 1 | ||
| } | ||
| } | ||
|
|
||
| switch ($subCommand) { | ||
| ({ $subCommand -in @($null, '-h', '--help', '/?') }) { | ||
| exec 'help' | ||
| try { | ||
| $subCommand = $Args[0] | ||
|
|
||
| # for aliases where there's a local function, re-alias so the function takes precedence | ||
| $aliases = Get-Alias | Where-Object { $_.Options -notmatch 'ReadOnly|AllScope' } | ForEach-Object { $_.Name } | ||
| Get-ChildItem Function: | Where-Object -Property Name -In -Value $aliases | ForEach-Object { | ||
| Set-Alias -Name $_.Name -Value Local:$($_.Name) -Scope Script | ||
| } | ||
| ({ $subCommand -in @('-v', '--version') }) { | ||
| Write-Host 'Current Scoop version:' | ||
| if ((Test-GitAvailable) -and (Test-Path "$PSScriptRoot\..\.git") -and ((get_config SCOOP_BRANCH 'master') -ne 'master')) { | ||
| Invoke-Git -Path "$PSScriptRoot\.." -ArgumentList @('--no-pager', 'log', 'HEAD', '-1', '--oneline') | ||
| } else { | ||
| $version = Select-String -Pattern '^## \[(v[\d.]+)\].*?([\d-]+)$' -Path "$PSScriptRoot\..\CHANGELOG.md" | ||
| Write-Host $version.Matches.Groups[1].Value -ForegroundColor Cyan -NoNewline | ||
| Write-Host " - Released at $($version.Matches.Groups[2].Value)" | ||
|
|
||
| switch ($subCommand) { | ||
| ({ $subCommand -in @($null, '-h', '--help', '/?') }) { | ||
| exec 'help' | ||
| } | ||
| Write-Host '' | ||
|
|
||
| Get-LocalBucket | ForEach-Object { | ||
| $bucketLoc = Find-BucketDirectory $_ -Root | ||
| if ((Test-GitAvailable) -and (Test-Path "$bucketLoc\.git")) { | ||
| Write-Host "'$_' bucket:" | ||
| Invoke-Git -Path $bucketLoc -ArgumentList @('--no-pager', 'log', 'HEAD', '-1', '--oneline') | ||
| Write-Host '' | ||
| ({ $subCommand -in @('-v', '--version') }) { | ||
| Write-Host 'Current Scoop version:' | ||
| if ((Test-GitAvailable) -and (Test-Path "$PSScriptRoot\..\.git") -and ((get_config SCOOP_BRANCH 'master') -ne 'master')) { | ||
| Invoke-Git -Path "$PSScriptRoot\.." -ArgumentList @('--no-pager', 'log', 'HEAD', '-1', '--oneline') | ||
| } else { | ||
| $version = Select-String -Pattern '^## \[(v[\d.]+)\].*?([\d-]+)$' -Path "$PSScriptRoot\..\CHANGELOG.md" | ||
| Write-Host $version.Matches.Groups[1].Value -ForegroundColor Cyan -NoNewline | ||
| Write-Host " - Released at $($version.Matches.Groups[2].Value)" | ||
| } | ||
| Write-Host '' | ||
|
|
||
| Get-LocalBucket | ForEach-Object { | ||
| $bucketLoc = Find-BucketDirectory $_ -Root | ||
| if ((Test-GitAvailable) -and (Test-Path "$bucketLoc\.git")) { | ||
| Write-Host "'$_' bucket:" | ||
| Invoke-Git -Path $bucketLoc -ArgumentList @('--no-pager', 'log', 'HEAD', '-1', '--oneline') | ||
| Write-Host '' | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ({ $subCommand -in (commands) }) { | ||
| [string[]]$arguments = $Args | Select-Object -Skip 1 | ||
| if ($null -ne $arguments -and $arguments[0] -in @('-h', '--help', '/?')) { | ||
| exec 'help' @($subCommand) | ||
| } else { | ||
| exec $subCommand $arguments | ||
| ({ $subCommand -in (commands) }) { | ||
| [string[]]$arguments = $Args | Select-Object -Skip 1 | ||
| if ($null -ne $arguments -and $arguments[0] -in @('-h', '--help', '/?')) { | ||
| exec 'help' @($subCommand) | ||
| } else { | ||
| exec $subCommand $arguments | ||
| } | ||
| } | ||
| default { | ||
| warn "scoop: '$subCommand' isn't a scoop command. See 'scoop help'." | ||
| exit 1 | ||
| } | ||
| } | ||
| default { | ||
| warn "scoop: '$subCommand' isn't a scoop command. See 'scoop help'." | ||
| exit 1 | ||
| } | ||
| } finally { | ||
| $lock_stream.Dispose() | ||
| } | ||
|
Comment on lines
+77
to
79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add null check before disposing the lock stream. If an unexpected exception occurs during lock acquisition (before Apply this diff to add a defensive null check: } finally {
- $lock_stream.Dispose()
+ if ($null -ne $lock_stream) {
+ $lock_stream.Dispose()
+ }
}🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tighten lock acquisition error handling to avoid masking non-contention failures
The lock loop works correctly for normal contention, but the generic
catchmeans any failure opening the lock file (e.g. permission error, invalid path) will cause an infinite retry with only a single "Waiting for exclusive access..." line:That makes genuine configuration/IO problems look like normal queueing and leaves the user with a hung
scoopprocess and no actionable error.It would be safer to:
System.IO.IOExceptionfrom a sharing violation), andwarn(or similar) so it doesn’t pollute the success pipeline.For example:
This preserves the 1‑second retry behavior for true contention, but fails fast (with a clear error) on misconfiguration or other unexpected IO problems, and keeps the waiting message off the success pipeline.
🤖 Prompt for AI Agents