views:

603

answers:

6

Suppose I have the following code:

public class SomeClass()
{
    private readonly object _lock = new object();

    public void SomeMethodA()
    {
     lock (_lock)
     {
      SomeHelperMethod();

      //do something that requires lock on _lock
     }
    }

    public void SomeMethodB()
    {
     lock (_lock)
     {
      SomeHelperMethod();

      //do something that requires lock on _lock
     }
    }

    private void SomeHelperMethod()
    {
     lock (_lock)
     {
      //do something that requires lock on _lock
     }
    }
}

Locking inside SomeHelperMethod seems redundant and wasteful, since all callers have already taken a lock. However, simply removing the lock from SomeHelperMethod seems dangerous since we might later refactor the code and forget to lock the _lock object prior to calling SomeHelperMethod.

Ideally I could work around this by asserting that the current thread owns a lock on _lock inside SomeHelperMethod:

private void SomeHelperMethod()
{
    Debug.Assert(Monitor.HasLock(_lock));

    //do something that requires lock on _lock
}

But there does not appear to be such a method. Monitor.TryEnter does not help because locks are re-entrant. Therefore, if the current thread already owns the lock, TryEnter will still succeed and return true. The only time it will fail is if another thread owns the lock and the call times out.

So, does such a method exist? If not, why? It does not seem dangerous at all to me, since it merely tells you whether the current thread (not another thread) owns a lock or not.

+1  A: 

OK, I now understand your problem. Conceptually I am now thinking of a Semaphore object. You can easily check the semaphore value (a value greater or equal than 0 means the resource is available). Also, this does not actually lock the resource that being done by incrementing or decrementing the semaphore value. The Semaphore object is present in the .NET framework but i did not use it so I cannot provide a proper example. If necessary I can build a simple example and post it here. Let me know.

Alex.

L.E. I am unsure now if you actually have control to the synchronization mechanisms in the application. If you only have access to an object that could be locked or not, my solution could prove (once again) of no use.

AlexDrenea
The Semaphore APIs do not enable this scenario. Moreover, this is far less efficient than using a simple monitor.
Kent Boogaart
A: 

I wish that was possible, that would make my code much, much cleaner.

Jonathan Allen
+1  A: 

There's a fundamental problem with your request. Suppose there were a IsLockHeld() method and you'd use it like this:

if (not IsLockHeld(someLockObj)) then DoSomething()

This cannot work by design. Your thread could be pre-empted between the if() statement and the method call. Allowing another thread to run and lock the object. Now DoSomething() will run, even though the lock is held.

The only way to avoid this is to take the lock and see if it worked. Monitor.TryEnter().

You see the exact same pattern at work in Windows. There is no way to check if a file is locked by another process, other than trying to open the file. For the exact same reason.

I'll upvote Quassnoi's answer now...

Hans Passant
But that's not how I'm using it. I'm using it as an assertion - as a way of knowing that my code is buggy. If the lock isn't held, there's a bug. I'm not using it to choose one code path over another.
Kent Boogaart
How could that possibly be a problem? It your code is right, Monitor.TryEnter() will always return false and not take the lock. If it returns True, you throw an exception to crash your program.
Hans Passant
No it won't return false. Just try it. Locks are reentrant so it will succeed and return true.
Kent Boogaart
Yes, you're right. You'll need a wrapper for Monitor.Enter, that's ugly.
Hans Passant
A: 

Not the best thing to do but ...

bool OwnsLock(object someLockObj)
{
  if (Monitor.TryEnter(someLockObj))
  {
    Monitor.Exit();
    return false;
  }
  return true;
}

Debug.Assert(OwnsLock(_someLockObject), "_someLockObject should be locked when this method is called")
Jorge Córdoba
Locking is re-entrant so this code does not work. The monitor will successfully take a lock even if the calling code already has the lock.
Kent Boogaart
+2  A: 

Locking or re-locking are effectively free. The downside of this low cost is that you don't have as many features as the other synchronisation mechanisms. I would simply lock whenever a lock is vital, as you have above.

If you desparately want to omit 'unnecessary' locks without the risk involved with potential refactoring, add comments. If someone changes your code and it breaks, there is a comment there that explains why. If they still can't figure it out, that's their problem, not yours. A further alternative is to create a class for use as a locking object that contains a boolean you can flick on and off. However, the overheads introduced by doing this (including try/finally blocks, which are not free), are probably not worth it.

Zooba
Essentially you're saying what I suspected: there is no way to do this. Whilst not an ideal solution, this is the best answer.
Kent Boogaart
A: 

I have no experience whatsoever with .NET programming, but in my own code (Delphi) I have once implemented such assertions by having custom Acquire() and Release() methods in a critical section class, with a private member variable for the id of the thread having acquired the cs. After EnterCriticalSection() the return value of GetCurrentThreadID was written, and before LeaveCriticalSection() the field value was reset.

But I don't know whether .NET allows a similar thing, or whether you could implement this in your program...

mghie