1+ function Get-PSFRunspaceLock {
2+ <#
3+ . SYNOPSIS
4+ Create or retrieve a lock object for runspace use.
5+
6+ . DESCRIPTION
7+ Create or retrieve a lock object for runspace use.
8+ One of the fundamental features in multi-threading is the "lock":
9+ A language feature in many programming languages, that helps marshal access to a resource from multiple threads.
10+ The key goal here: Prevent concurrent access to a resources or process, that cannot be done concurrently.
11+
12+ PowerShell does not have such a feature.
13+
14+ This is where the RunspaceLock feature comes in:
15+ This command generates an object, that can take over the role of the lock-feature in a PowerShell environment!
16+
17+ First create a lock object:
18+ $lock = Get-PSFRunspaceLock -Name 'MyModule.Example'
19+
20+ Then you can obtain the lock calling the Open() method:
21+ $lock.Open()
22+ This will reserve the lock for the current runspace.
23+ If another runspace tries to also call Open(), they will be forced to wait until the current runspace releases the lock.
24+
25+ Finally, to release the lock, call the Close() method:
26+ $lock.Close()
27+
28+ Example implementation:
29+ $lock = Get-PSFRunspaceLock -Name 'MyModule.ExchangeConnect'
30+ $lock.Open()
31+ try { Connect-IPPSSession }
32+ finally { $lock.Close() }
33+
34+ This will guarantee, that only one runspace will call "Connect-IPPSSession" at a time, assuming all run this snippet.
35+
36+ . PARAMETER Name
37+ The name of the runspace-lock.
38+ No matter from which runspace, all instances using the same name utilize the same lock and can block each other.
39+
40+ . PARAMETER Timeout
41+ How long a lock is valid for at most.
42+ By default, a lock is valid for 30 seconds, after which it will expire and be released, in order to prevent permanent lockout / deadlock.
43+ Increase this if more time is needed, setting this to 0 or less will remove the timeout.
44+
45+ . PARAMETER Unmanaged
46+ By default, retrieving a lock with the same name will grant access to the exact same lock.
47+ This makes it easy to use in casual runspace scenarios:
48+ Simply call Get-PSFRunspaceLock in each runspace with the same name and you are good.
49+ By making the lock unmanaged, you remove it from this system - the lock-object will not be tracked by PSFramework,
50+ creating additional instances with the same name will NOT reference the same lock.
51+ In return, you can safely pass in the lock object to whatever runspace you want with a guarantee to not conflict with anything else.
52+ This parameter should generally not be needed for most scenarios.
53+
54+ . EXAMPLE
55+ PS C:\> $lock = Get-PSFRunspaceLock -Name 'MyModule.ExchangeConnect'
56+
57+ Creates a new lock object named 'MyModule.ExchangeConnect'
58+
59+ . EXAMPLE
60+ 1..20 | Invoke-PSFRunspace {
61+ if (-not $global:connected) {
62+ $lock = Get-PSFRunspaceLock -Name MyModule.ExchangeConnect
63+ $lock.Open('5m')
64+ try { Connect-IPPSSession }
65+ finally { $lock.Close() }
66+ $global:connected = $true
67+ }
68+ Get-Label
69+ } -ThrottleLimit 4
70+
71+ In four background runspaces, it will savely connect to Purview and retrieve labels 20 times total, without getting into conflict.
72+ #>
73+ [OutputType ([PSFramework.Runspace.RunspaceLock ])]
74+ [CmdletBinding ()]
75+ param (
76+ [Parameter (Mandatory = $true )]
77+ [string ]
78+ $Name ,
79+
80+ [PsfTimeSpan ]
81+ $Timeout ,
82+
83+ [switch ]
84+ $Unmanaged
85+ )
86+ process {
87+ if ($Unmanaged ) {
88+ $lock = [PSFramework.Runspace.RunspaceLock ]::new($Name )
89+ }
90+ else {
91+ $lock = [PSFramework.Runspace.RunspaceHost ]::GetRunspaceLock($Name )
92+ }
93+ if ($Timeout ) {
94+ $lock.MaxLockTime = $Timeout.Value.TotalMilliseconds
95+ }
96+ $lock
97+ }
98+ }
0 commit comments