Semaphores
Semaphores are similar to Mutexes, in that they let you control thread synchronisation across different processes, whether they be child-processes; processes running on the current login session; or every process running on the system. The difference being that a Semaphore allows you to control the number of processes that can enter the Semaphore at once, unlike a Mutex which is just one at a time.
Note
When the -Count
for a Semaphore is set to 1 they're basically the same as Mutexes.
Creating a Semaphore
To create a Semaphore in Pode you can use New-PodeSemaphore
, this will either create a Semaphore or retrieve an existing Semaphore if one already exists within the selected scope.
A Semaphore will need a -Name
and a -Scope
. You can also optionally supply a -Count
which is the number of processes allowed to enter the Semaphore at once - the default be 1. The default scope is Self, but other options are Local or Global:
Scope | Description |
---|---|
Self | The current process, or child processes |
Local | All processes for the current login session on Windows, or the the same as Self on Unix |
Global | All processes on the system, across every session |
The following example will create a new global Semaphore, which allows 2 processes to enter at once:
Start-PodeServer {
New-PodeSemaphore -Name 'ExampleSemaphore' -Scope Global -Count 2
}
Once created, you can use the Semaphore in Use-PodeSemaphore
, Enter-PodeSemaphore
, and Exit-PodeSemaphore
.
Using a Semaphore
To use a Semaphore after creating one you can use Use-PodeSemaphore
to enter a Semaphore; invoke a scriptblock; and then exit a Semaphore to free it up for another process - depending on how many processes can enter the Semaphore at once.
Below are 2 scripts for creating 2 Pode servers, similar to the example in Mutexes. Both create the same global Semaphore, and then use te Semaphore to invoke a scriptblock. In 1 server the scriptblock sleeps for 10 seconds, and in the other for 2 seconds. If you run both servers and call the first server's Route, and then the second, the second won't block while the first is still running. However, if you were to call the second server's Route twice in different terminals the first call would return after 2 seconds, but the second call would return after 4 seconds - as only 2 processes can enter the Semaphore at once.
-
First server
Start-PodeServer { Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http New-PodeSemaphore -Name 'GlobalSemaphore' -Scope Global -Count 2 Add-PodeRoute -Method Get -Path '/sleep' -ScriptBlock { Use-PodeSemaphore -Name 'GlobalSemaphore' -ScriptBlock { Start-Sleep -Seconds 10 } } }
-
Second server
Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http New-PodeSemaphore -Name 'GlobalSemaphore' -Scope Global -Count 2 Add-PodeRoute -Method Get -Path '/sleep' -ScriptBlock { Use-PodeSemaphore -Name 'GlobalSemaphore' -ScriptBlock { Start-Sleep -Seconds 2 } } }
Enter / Exit
You can have more advanced control over the using of Semaphores via Enter-PodeSemaphore
and Exit-PodeSemaphore
. Using these functions you can enter a Semaphore, run some logic, and then exit the Semaphore - but you don't have to do it all in the same function or scriptblock.
Enter-PodeSemaphore
can be used to initially place enter a Semaphore, and then you must call Exit-PodeSemaphore
later on to exit the Semaphore:
Start-PodeServer -Threads 2 {
Add-PodeEndpoint -Address * -Port 8090 -Protocol Http
New-PodeSemaphore -Name 'GlobalSemaphore' -Scope Global -Count 2
Add-PodeRoute -Method Get -Path '/sleep' -ScriptBlock {
try {
Enter-PodeSemaphore -Name 'GlobalSemaphore'
Start-Sleep -Seconds 10
}
finally {
Exit-PodeSemaphore -Name 'GlobalSemaphore'
}
}
}
Exit-PodeSemaphore
also allows you to exit the Semaphore X number of times via -ReleaseCount
.
Timeout
When using a Semaphore by default Pode will wait indefinitely for the Semaphore to be released before entering it. You can use the -Timeout
parameter to specify a number of milliseconds to timeout after if a Semaphore fails be released.
Start-PodeServer -Threads 2 {
Add-PodeEndpoint -Address * -Port 8090 -Protocol Http
New-PodeSemaphore -Name 'GlobalSemaphore' -Scope Global -Count 2
Add-PodeRoute -Method Get -Path '/sleep' -ScriptBlock {
Use-PodeSemaphore -Name 'GlobalSemaphore' -Timeout 2000 -ScriptBlock {
Start-Sleep -Seconds 2
}
}
}