What Is a Good Pattern For Using a Global Mutex in C#

What is a good pattern for using a Global Mutex in C#?

I want to make sure this is out there, because it's so hard to get right:

using System.Runtime.InteropServices;   //GuidAttribute
using System.Reflection; //Assembly
using System.Threading; //Mutex
using System.Security.AccessControl; //MutexAccessRule
using System.Security.Principal; //SecurityIdentifier

static void Main(string[] args)
{
// get application GUID as defined in AssemblyInfo.cs
string appGuid =
((GuidAttribute)Assembly.GetExecutingAssembly().
GetCustomAttributes(typeof(GuidAttribute), false).
GetValue(0)).Value.ToString();

// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format( "Global\\{{{0}}}", appGuid );

// Need a place to store a return value in Mutex() constructor call
bool createdNew;

// edited by Jeremy Wiebe to add example of setting up security for multi-user usage
// edited by 'Marc' to work also on localized systems (don't use just "Everyone")
var allowEveryoneRule =
new MutexAccessRule( new SecurityIdentifier( WellKnownSidType.WorldSid
, null)
, MutexRights.FullControl
, AccessControlType.Allow
);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);

// edited by MasonGZhwiti to prevent race condition on security settings via VanNguyen
using (var mutex = new Mutex(false, mutexId, out createdNew, securitySettings))
{
// edited by acidzombie24
var hasHandle = false;
try
{
try
{
// note, you may want to time out here instead of waiting forever
// edited by acidzombie24
// mutex.WaitOne(Timeout.Infinite, false);
hasHandle = mutex.WaitOne(5000, false);
if (hasHandle == false)
throw new TimeoutException("Timeout waiting for exclusive access");
}
catch (AbandonedMutexException)
{
// Log the fact that the mutex was abandoned in another process,
// it will still get acquired
hasHandle = true;
}

// Perform your work here.
}
finally
{
// edited by acidzombie24, added if statement
if(hasHandle)
mutex.ReleaseMutex();
}
}
}

What is the correct pattern for using a global mutex in WPF?

It looks like the big issue is that when Startup is called by default it calls Show() on the main form.

Since this means the function returns, wrapping the Startup call in the mutex like the original example I linked, the lock on the mutex is released shortly after startup. If you either change the way Statup opens the main from from Show() to ShowDialog() it works as expected.

What is the correct pattern for using a global mutex in WPF?

It looks like the big issue is that when Startup is called by default it calls Show() on the main form.

Since this means the function returns, wrapping the Startup call in the mutex like the original example I linked, the lock on the mutex is released shortly after startup. If you either change the way Statup opens the main from from Show() to ShowDialog() it works as expected.

C# Mutex handling

A Mutex is "owned" by a single process, and in the event the process crashes or closes the Mutex will be released.

If the process crashes and the Mutex is freed then it is considered "Abandoned", which is a way to signal that the original process no longer owns it -- but also didn't expliticly release it.

What I'm not quite understanding in your question or code is the handling of an abandoned Mutex. The helper will only return a Mutex if it was not previously abandoned. In the event it was abandoned the code successfully retrieves the Mutex, then releases it and returns without providing a Mutex at all.

That might be the intention, but it's somewhat hard to understand based on the wording of the question. If the helper is intended to reset and return the Mutex then the handling of the AbandonedMutexException doesn't look correct, as it will always return null.

Use a system wide Mutex

Try something like this:

     System.Threading.Mutex _mutex = null;
bool mutexWasCreated = false;
public Form1()
{
new Thread(threadSafeWorkBetween2Instances).Start();
}
void threadSafeWorkBetween2Instances()
{
if(!_mutex.TryOpenExisting("GlobalsystemWideMutex"))
{
// Create a Mutex object that represents the system
// mutex named with
// initial ownership for this thread, and with the
// specified security access. The Boolean value that
// indicates creation of the underlying system object
// is placed in mutexWasCreated.
//
_mutex = new Mutex(true, "GlobalsystemWideMutex", out
mutexWasCreated, mSec);

if(!mutexWasCreated )
{
//report error
}
}
while (true)
{
try
{
bool acquired = _mutex.WaitOne(5000); //obtain a lock - timeout after n number of seconds
if(acquired)
{
try
{
//DO THREADSAFE WORK HERE!!!
//Write to the same file
}
catch (Exception e)
{

}
finally
{
_mutex.ReleaseMutex(); //release
break;
}
}
else
{
Thread.Sleep(1000); // wait for n number of seconds before retrying
}
}
catch { } //Create mutex for the first time

}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
try { _mutex.ReleaseMutex(); } catch { } //release
}

Usage of Mutex in c#

The problem here is that all your callers are using a different mutex; you need the locking object to be shared, usually by making it a field. For example, and switching to a simpler lock metaphor:

private readonly object syncLock = new object();
public void ThreadSafeMethod() {
lock(syncLock) {
/* critical code */
}
}

or using the mutex:

private readonly Mutex m = new Mutex();
public void ThreadSafeMethod() {
m.WaitOne();
try {
/* critical code */
} finally {
m.ReleaseMutex();
}
}

Using Named Mutex

I had a similar issue recently.

The design of the Mutex class is a bit weird/different from the normal classes in .NET.

Using OpenMutex to check for an existing Mutex is not really nice as you have to catch an exception.

A better approach is to use the

Mutex(bool initiallyOwned, string name, out bool createdNew) 

constructor, and check the value returned by createdNew.

Cross-process mutex instantiation

Nobody owns a Mutex, unless it's acquired by somebody by calling its method WaitOne. Passing initiallyOwned: true is an attempt to acquire immediately a newly created Mutex, but it's problematic. From the documentation:

You should not use this constructor to request initial ownership, unless you can be certain that the thread will create the named mutex.

There is also an overload that accepts three parameters:

public Mutex (bool initiallyOwned, string name, out bool createdNew);

...but it's simpler to just create the Mutex without initial ownership, and later acquire it when you are ready to be blocked (in case it happens to be acquired by someone else).

mutex = new Mutex(initiallyOwned: false, APP_NAME);
mutex.WaitOne(); // Potentially blocking


Related Topics



Leave a reply



Submit