Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test a lock with out acquiring it?

I have objects, they get locks. I want to test if they are locked without acquiring a lock. The idea is if I TryEnter() then i have to Exit() if true to only check the lock correctly.

Seems like a really basic question, how is it done?

like image 455
QueueHammer Avatar asked Feb 18 '10 23:02

QueueHammer


4 Answers

Because the lock statement is equivalent to:

System.Threading.Monitor.Enter(x);
try {
   ...
}
finally {
   System.Threading.Monitor.Exit(x);
}

Can you just do this?

bool ObjectWasUnlocked(object x)
{
   if(System.Threading.Monitor.TryEnter(x))
   {
       System.Threading.Monitor.Exit(x);
       return true;
   }
   else
   {
       return false;
   }
}

Note that I'm naming this function "ObjectWasUnlocked" as opposed to "ObjectIsUnlocked". There is no guarantee that it will still be unlocked when the function has returned.

like image 179
Andrew Shepherd Avatar answered Nov 14 '22 14:11

Andrew Shepherd


What possible information can you get from knowing the lock was unlocked back when you looked at it? By the time you make a decision based on that information, the lock may be already taken.

like image 37
Remus Rusanu Avatar answered Nov 14 '22 12:11

Remus Rusanu


I was wondering the same thing while trying to audit my code for correct locking. I came up with a method using a second thread. If the lock is available to the calling thread, but unavailable to a second thread, it must be held by the first.

    /// <summary>
/// Utiltity for checking if a lock has already been acquired.
/// WARNING: This test isn't actually thread-safe, 
/// it's only really useful for unit tests
/// </summary>
private static bool ObjectIsAlreadyLockedByThisThread(object lockObject)
{
    if (!Monitor.TryEnter(lockObject))
    {
        // another thread has the lock
        return false;
    }

    Monitor.Exit(lockObject);

    bool? LockAvailable = null;

    var T = new Thread(() =>
    {
        if (Monitor.TryEnter(lockObject))
        {
            LockAvailable = true;
            Monitor.Exit(lockObject);
        }
        else
        {
            LockAvailable = false;
        }
    });

    T.Start();

    T.Join();

    return !LockAvailable.Value;
}

// Tests:
public static void TestLockedByThisThread()
{
    object MyLock = new object();
    lock (MyLock)
    {
        bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock);

        Debug.WriteLine(WasLocked); // prints "True"
    }
}

public static void TestLockedByOtherThread()
{
    object MyLock = new object();

    var T = new Thread(() =>
    {
        lock (MyLock)
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
        }
    });

    T.Start();
    Thread.Sleep(TimeSpan.FromSeconds(1));

    bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock);

    T.Join();

    Debug.WriteLine(WasLocked); // prints "False"
}

public static void TestNotLocked()
{
    object MyLock = new object();

    bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock);

    Debug.WriteLine(WasLocked); // prints "False"
}

I wouldn't use this in production code - there's a race condition that could blow up. However, my unit tests are mostly single threaded, so this was useful.

like image 21
GranBurguesa Avatar answered Nov 14 '22 13:11

GranBurguesa


Here is a related question

Checking whether the current thread owns a lock

The conclusion there was 'you can't'

like image 1
Phil Avatar answered Nov 14 '22 14:11

Phil