Skip to content

Instantly share code, notes, and snippets.

@AndrewSav
Last active September 4, 2019 01:09
Show Gist options
  • Save AndrewSav/99f2b741410dc34c11348eccf3d609c7 to your computer and use it in GitHub Desktop.
Save AndrewSav/99f2b741410dc34c11348eccf3d609c7 to your computer and use it in GitHub Desktop.
One Writer Multiple readers locking for Powershell. Readers and Writers should agree on `$maxReaders` parameter before hand and use the same one. Call `Lock-Read` or `Lock-Write` to lock and then `Unlock-Read` or `Unlock-Write` to unlock.
function WaitAnyMutexes($mutexes, $name, $timeout) {
try {
$result = [Threading.WaitHandle]::WaitAny($mutexes,$timeout)
if ($result -ne [Threading.WaitHandle]::WaitTimeout) {
return @{clean=$true; mutex=$mutexes[$result]; index=$result};
} else {
return $null
}
} catch [System.Threading.AbandonedMutexException]{
return @{clean=$false; mutex=$_.Exception.Mutex; index=$_.Exception.MutexIndex}
}
}
function WaitAllMutexes([Threading.Mutex[]]$mutexes, $name, $timeout) {
if ($timeout -ne [Threading.Timeout]::Infinite) {
$until = (Get-Date) + [Timespan]::FromMilliseconds($timeout);
}
$r = $mutexes | ForEach-Object {
if ($timeout -eq [Threading.Timeout]::Infinite) {
$t = [Threading.Timeout]::Infinite
} else {
$t = ($until - (Get-Date)).Milliseconds
if ($t -lt 0) { $t = 0}
}
try {
return $_.WaitOne($t)
} catch [System.Threading.AbandonedMutexException] {
return $true
}
}
if (($r | Where-Object {!$_}).Count -gt 0) {
return $null
} else {
return $mutexes
}
}
function Lock-Read {
Param(
[parameter(Mandatory=$true)] [string]$name,
[parameter(Mandatory=$true)] [int]$maxReaders
)
#$waitCycleMs = 1000 * 60
$mutexes = @(0..($maxReaders-1) | Foreach-Object {
New-Object -TypeName system.threading.mutex($false, "$name-reader-$_")
})
$result = $maxReaders
do {
$result = WaitAnyMutexes $mutexes $name 0
if (!$result) {
"Lock $name is active, wait for release (read) ..." | Write-Host
$result = WaitAnyMutexes $mutexes $name ([Threading.Timeout]::Infinite)
#$result = WaitAnyMutexes $mutexes $name #$waitCycleMs
} else {
return $result.mutex
}
} until ($result)
$verb = if ($result.clean) { "released" } else { "abandoned" }
"Lock $name-$($result.index) has been $verb, moving on (read) ..." | Write-Host
return $result.mutex
}
function Lock-Write {
Param(
[parameter(Mandatory=$true)] [string]$name,
[parameter(Mandatory=$true)] [int]$maxReaders
)
#$waitCycleMs = 1000 * 60
$writer = New-Object -TypeName system.threading.mutex($false, "$name")
$mutexes = @($writer) + @(0..($maxReaders-1) | Foreach-Object {
New-Object -TypeName system.threading.mutex($false, "$name-reader-$_")
})
do {
$result = WaitAllMutexes $mutexes $name 0
if (!$result) {
"Lock $name is active, wait for release (write) ..." | Write-Host
$result = WaitAllMutexes $mutexes $name ([Threading.Timeout]::Infinite)
#$result = WaitAllMutexes $mutexes $name $waitCycleMs
} else {
return $result
}
} until ($result)
"Lock $name has been released, moving on (write) ..." | Write-Host
return $result
}
function Unlock-Read {
Param(
[parameter(Mandatory=$true)] [Threading.Mutex]$lock
)
$lock.ReleaseMutex()
}
function Unlock-Write {
Param(
[parameter(Mandatory=$true)] [Threading.Mutex[]]$lock
)
$lock.ReleaseMutex()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment