views:

609

answers:

6

How can I find from mutex handle in C# that a mutex is acquired?

When mutex.WaitOne(timeout) timeouts, it returns false. However, how can I find that from the mutex handle? (Maybe using p/invoke.)

UPDATE:

public class InterProcessLock : IDisposable
{
    readonly Mutex mutex;

    public bool IsAcquired { get; private set; }

    public InterProcessLock(string name, TimeSpan timeout)
    {
        bool created;
        var security = new MutexSecurity();
        security.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
        mutex = new Mutex(false, name, out created, security);
        IsAcquired = mutex.WaitOne(timeout);
    }

    #region IDisposable Members

    public void Dispose()
    {
        if (IsAcquired)
        {
            mutex.ReleaseMutex();
            IsAcquired = false;
        }
    }

    #endregion
}

Currently, I am using my own property IsAcquired to determine whether I should release a mutex. Not essential but clearer, would be not to use a secondary copy of the information represented by IsAcquired property, but rather to ask directly the mutex whether it is acquired by me. Since calling mutex.ReleaseMutex() throws an exception if it is not acquired by me.

(By acquired state I mean that the mutex is in not-signaled state when I am owning the mutex.)

(EDIT: I have added IsAcquired = false; thanks to mattdekrey's post.)

A: 

.NET Mutex class is native mutex wrapper, which gives the same possibilities, as native mutex API (except waiting for number of waitable objects of different type). If you want to acquire mutex without blocking, call mutex.WaitOne(0). Using PInvoke, you can call WaitForSingleObject, with the same result.

Alex Farber
Thanks, but I don't want to acquire the mutex if it is not already acquired. Is there any function for that?
TN
No, there is no such way both in .NET mutex and native API. Mutex may be acquired only if it is not acquired by any other thread. Possibly you need some other synchronization type. Define your requirements, maybe some other synchronization type, like event or semaphore, meets them.
Alex Farber
I put the answer into the post (since my answer is longer).
TN
+2  A: 

Well, it's not exactly what you're asking for, but I think it would solve your problem: why not just add some error handling specifically for the exception that occurs if the Mutex is aquired by someone else?

public void Dispose()
{
    if (IsAcquired)
        try
        { mutex.ReleaseMutex(); }
        catch (System.Threading.SynchronizationLockException)
        {
            // Handle the exception, assuming you need to do anything.
            // All other exceptions would still be passed up the stack.
        }
}
TenshiNoK
Yes, thanks, also a solution, but I am just trying to solve the problem "better" way, if possible:)
TN
From a security point of view, this is the only way to prevent a race condition. Any checking you could do in a multithreaded environment would be defeated as you could do all the checking in the world, then the thread acquires just as you release causing an exception. This is probably the safest pattern.
Spence
Not sure. Since, I think that using interprocess lock among threads in a same process is not a good idea. So, in case that one thread acquires a global process lock when another thread is just releasing it, I want to throw an exception.
TN
A: 

If you're really trying to do an inter-process lock, as the name implies, you will want a way to detect if the Mutex has actually been acquired anyway, correct? I'm not sure how your code that uses your InterProcessLock would be ensured to be locked if there was no IsAcquired property. (Also, to protect against programmers who accidentally call Dispose twice, I'd set the IsAcquired to false in your Dispose method.)

I've implemented the same thing myself (because I much prefer the using block to a try-finally just to release the mutex) and instead threw an exception when the timeout was exceeded, which, if I'm remembering the project correctly, did not call the Dispose method.

Edit: Added benefit of throwing the exception in the constructor: your critical section is also completely avoided, and you can perform error handling in the catch block, which could include the same method call your critical section had, anyway, though I personally would consider that a bad practice.

Upon further reflection, rather than using try ... catch as specified in another answer, you could use the following on your dispose:

public void Dispose()
{
    if (IsAcquired)
    {
        lock (mutex) 
        {
            mutex.ReleaseMutex();
            IsAcquired = false;
        }
    }
}

It feels a little ironic to lock a mutex, but there you have it. While I totally agree that you shouldn't rely on Dispose being called because of the documentation with the IDisposable interface, I think it is incredibly convenient to have an inter-process critical section indicated by a using() { } block.

mattdekrey
If programmers call `Dispose()` twice, it will throw an exception. So they can find that there is a bug in their code. Earlier, I was also throwing an exception in the constructor. But later I have changed that, since programmers may decide in certain situations to run the code even if the lock is not acquired. (For instance, there are independent processes and maybe one got stuck, so the second is waiting a while and then it will run despite this.)
TN
The IDisposable design documentation specifies that "If an object's Dispose method is called more than once, the object mustignore all calls after the first one. The object must not throw anexception if its Dispose method is called multiple times." http://msdn.microsoft.com/en-us/library/system.idisposable.dispose.aspx
mattdekrey
+1 Ah, ok. Thank you for the information. I have not read that:(
TN
+7  A: 

The reason there is no clean way to do this is because it is not a good idea and the reason is because race conditions are -very- easy to introduce when you rely on this type of logic. So your design needs to change.

First, you should not acquire a lock in a constructor. Turn this class into a factory that returns a properly initialized mutex object. That way you can know if you acquired the lock or not.

DO NOT rely on Dispose to release locks, this is asking for deadlock ridden code that is hard to maintain. Use a try/finally block to ensure it is released.

Timeouts are a bit sketchy. Only use timeouts when not acquiring the lock would be considered normal operation. Not being able to acquire the lock is usually a bug and merely avoiding it with timeouts hides the bug. If you need timeouts, consider using an event (maybe AutoResetEvent), this may be more appropriate.

Karl Strings
Karl - I agree with all of your statements except the constructor/dispose parts; Dispose is not the same as a destructor - the using statement is implemented by a try/finally block when it comes down to the IL. (As stated below, I think the constructor would be fine if it ALWAYS threw an exception when it failed to acquire the lock to guarantee that the critical section did not run.)
mattdekrey
Matt - http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx: "Do not assume that Dispose will be called." You are right, as long as the devs scope it in a using block or manually call dispose this would be fine. But it's awkward. Many people will assume a finalizer (or more likely just have no clue...) and let it fall off scope. This won't work with locks because the GC may or may not collect the object right away, causing weirdness in the locking. It creates hard to find bugs. It will work, but it adds "do it this way" process overhead for a simple lock. Best to make locks act like locks.
Karl Strings
Hmm, I haven't read that. I wonder why that isn't mentioned on the IDisposable.Dispose documentation page. +1 - I think yours should be accepted as the answer.
mattdekrey
Karl, I may not understand properly - if I add a `Release()` method that will call `Dispose()`, will it be ok? Next, how can I use `AutoResetEvent` to synchronize processes?
TN
I am saying to not use the dispose interface at all. Have a static method that creates the mutex objects you need (factory pattern). Then use those mutexes directly rather than wrapping it.I miss quoted, it is not an AutoResetEvent, it is an EventWaitHandle. You can create or wait on a system event until another process signals it. You may want to clarify what problem you are trying to solve, I think you may get better answers.
Karl Strings
I am sorry, maybe I have a brain problem:) I still don't understand:( You said: 'First, you should not acquire a lock in a constructor.' Could you provide a rationale for that? Since, it is similar to streams/files. Files are also used for inter-process locking. **My current problem:** I have system where multiple processes runs 'randomly' (based on external events). I would like to avoid them run at same time. So they are acquiring a lock. (But if a process have a problem - runs longer, the next waiting process should report that and continue - without killing the locked process.)
TN
1) Acquiring locks in a constructor. Your cleanup logic will get complicated - there is no good way to release the lock. Streams/Files do not acquire locks in a constructor. You may be confused as to what a lock is and what it does for you. 2) These types of problems are difficult to solve and it will become a maintenance issue. it is better to write a service application that controls the processes or write a service that combines all the functionality of the other processes. Central management is much simpler to debug and maintain.
Karl Strings
**1, Your cleanup logic will get complicated - there is no good way to release the lock.** - Calling `Release` or `Dispose` is simple. Or what complication you see there? **2, Streams/Files do not acquire locks in a constructor.** No, but they are allocating a system handle as well. What is the difference between locking a file and locking a mutex? **3, Central management is much simpler to debug and maintain.** I can't agree more:) Centralized vs. distributed systems...
TN
+1  A: 
David Basarab
What for? This is just a check whether the mutex exists. Not whether it is acquired.
TN
@TN - See my edit.
David Basarab
Thank you for the hint that passing closures is safer against user mistakes than `IDisposable.Dispose` or a custom `Release method`.
TN
+1  A: 

As you may found, there are no public members on Mutex class: http://msdn.microsoft.com/en-us/library/system.threading.mutex_members.aspx

There is also no public native functions for that: http://msdn.microsoft.com/en-us/library/ms686360%28v=VS.85%29.aspx

However, there are some undocumented/unsupported functions especially in ntdll.dll. These allow accessing system objects. However, these functions may change or not be available in future versions of operating system.

So, the answer is: It is not possible using conventional means.

lioil