Friday, February 12, 2016

PowerShell : Getting started with MutEx

After setting up the context for the use case of the MutEx in previous post, it is time to do our homework on the topic.


Theory


MutEx as per the MSDN documentation is:
"A synchronization primitive that grants exclusive access to the shared resource to only one thread. If a thread acquires a mutex, the second thread that wants to acquire that mutex is suspended until the first thread releases the mutex."


Now there are two types of MutEx in .NET world:
  • Local mutexes (which are unnamed), can only be used by any thread in our process that has reference to the Mutex Object.
  • Named system mutexes, visible throughout the Operating system and hence can be used as an interprocess synchronization mechanism. Now on a sever which is running Terminal services and hence multiple terminal sessions, the named system mutex can have two levels of visibility.
    • If the mutex name begins with prefix "Local\", it is only visible in the Terminal session where it was created. (Default one, if no prefix specified).
    • If the mutex name begins with prefix "Global\", it is visible in all the Terminal sessions running on the server.


Practical


So we have cleared the theoretical aspects out, now let's take a look at how to create a MutEx (Named Local mutex) object and see it in action. Below are the step :
  1. First step is to create the Mutex Object using the constructor here. Note that the default visibility for the Named mutex is 'Local'.


    $createdNew = $False # Stores Boolean value if the current PowerShell Process gets a lock on the Mutex
    # Create the Mutex Object usin the constructuor -> Mutex Constructor (Boolean, String, Boolean)
    $mutex = New-Object -TypeName System.Threading.Mutex($true, "MutexName1", [ref]$createdNew)

  2. Now the variable $createdNew will have $True if the current PowerShell process ,where you ran this code got a lock on the MutEx object. You can open another PowerShell instance and verify that the only the first process has $CreateNew set to True




    $mutex will contain the MutEx object in it.
  3. So in your project which spans across multiple PowerShell scripts, the very first step will be try to acquire the lock during the creation of the Mutex Object. If you get the lock on the MutEx then very well, go ahead and use the shared resource say a config file. But if you don't get the lock on the MutEx then you have to call the WaitOne() method on the MutEx object.

    There are multiple method overloads but we can simply use the WaitOne() method, which blocks the current PowerShell process until it receives the lock.

    See below, if I release the MutEx from the first process the second one gets the lock post it.


  4. Now, In your code once you have the MutEx lock , you can go ahead and use the shared resource. But remember to release the MutEx by calling the ReleaseMutex() method.

    If you don't  release the mutex in your code (say your function) then when some other process tries to get a lock on the MutEx an AbandonedMutexException is thrown.


Note - If you plan to use MutEx in your code then PowerShell should be running in Single Threaded ApartmentState (STA). Explained by MVP Oisin here.
PowerShell v3 onwards both the Console and ISE by default run in STA mode.

Summary


Now since we have the basics of working with a MutEx explained. Now it is time to summarize how the Invoke-ActionWithMutex & Get-AzureStackDeploymentStatus function work. (See this post if you don't know where this is coming from).

The Get-AzureStackDeploymentStatus function is straight enough, it calls for reading the XML file 'C:\ProgramData\Microsoft\AzureStackAzureStackDeploymentStatus.xml'. See below:


function Get-AzureStackDeploymentStatus
{
    [CmdletBinding()]
    param()
    if (-not (Test-Path $statusFilePath)) {
        Update-AzureStackDeploymentStatus $StatusTemplate | Out-Null
    }

    Invoke-ActionWithMutex -Action {
        [xml](Get-Content $statusFilePath)
    }
}

I have tried explaining how the Invoke-ActionWithMutex function works in below screenshot, it should be straight enough to understand.



Note - To invoke the Scriptblock in Line 24 , Invoke-Command is used to ensure that the ApartmentState for the runspace is STA.

I see MutEx as a powerful technique that can be used in our deployment workflows, I intend to do few more posts after I have explored them a bit more. Meanwhile take a look at some very good posts in the below section :)

Resources :


MutEx Class - Read up the MSDN documentation to grasp the details of this.
https://msdn.microsoft.com/en-us/library/system.threading.mutex%28v=vs.110%29.aspx

Excellent post by MVP Boe Prox on using MutEx to write data to same log file (uses runspaces with MutEx too).
http://learn-powershell.net/2014/09/30/using-mutexes-to-write-data-to-the-same-logfile-across-processes-with-powershell/

Lee Holmes post on enforcing single user access to custom PSRemoting endpoint using MutEx.
http://www.leeholmes.com/blog/2011/08/24/enforcing-single-user-access-to-powershell-remoting/

2 comments:

  1. Hi, what about updating the same file over the network from separate machine ? will using Mutex work?

    ReplyDelete
    Replies
    1. MutExes are limited to current session only. So this won't work if users are connecting using different machines.

      Delete