Modules/businessdev.ALbuild.Core/Public/Lock-ALbuildCacheEntry.ps1
|
function Lock-ALbuildCacheEntry { <# .SYNOPSIS Acquires the cache locks that cover a prunable cache entry. .DESCRIPTION A cache entry may not be removed while another build is extracting into it. Get-BcArtifact locks the LEAF it extracts into (<version>\<country>, <version>\platform), not the version folder, so covering an entry means locking the entry itself plus every immediate child. A lock Windows refuses to create (see New-ALbuildCacheMutex) is reported like a busy one, so the caller leaves the entry in place. All-or-nothing: if any lock cannot be taken within the timeout, the ones already held are released before returning, so a caller can never hold half a lock set. The lock name comes from Get-ALbuildCacheLockName, which is the single definition both sides must agree on - a second, copied definition would drift and then silently delete a folder out from under a reader. .PARAMETER Path The cache entry to cover (typically a <type>\<version> folder). .PARAMETER TimeoutSeconds How long to wait per lock. Default 5. .EXAMPLE $locks = Lock-ALbuildCacheEntry -Path $versionFolder if ($locks.Acquired) { try { Remove-Item ... } finally { Unlock-ALbuildCacheEntry -Locks $locks } } .OUTPUTS PSCustomObject with Acquired, BusyPath and Mutexes. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path, [int] $TimeoutSeconds = 5 ) $paths = @($Path) + @( Get-ChildItem -LiteralPath $Path -Directory -Force -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName } ) $held = [System.Collections.Generic.List[object]]::new() foreach ($p in $paths) { # A lock Windows refuses counts as busy: removal is the one place where going on without it # could delete a folder out from under a reader. One quick retry; a prune walks many entries. $mutex = New-ALbuildCacheMutex -Path $p -RetryCount 1 -RetryDelaySeconds 1 $got = $false if ($mutex) { try { $got = $mutex.WaitOne([TimeSpan]::FromSeconds($TimeoutSeconds)) } catch [System.Threading.AbandonedMutexException] { $got = $true } # prior holder crashed; we own it } if (-not $got) { if ($mutex) { $mutex.Dispose() } foreach ($h in $held) { $h.ReleaseMutex(); $h.Dispose() } return [PSCustomObject]@{ Acquired = $false; BusyPath = $p; Mutexes = @() } } $held.Add($mutex) } return [PSCustomObject]@{ Acquired = $true; BusyPath = $null; Mutexes = $held.ToArray() } } |