Please enable JavaScript to view this site.

Thread Lock Checker Help

Navigation: » No topics above this level «

What does Thread Lock Checker do?

Scroll Prev Top Next More

Thread Lock Checker scans your source code looking for common errors when locking critical sections. Before describing what Thread Lock Checker detects we will present a brief outline of how critical sections and locks can be used.

 

Note: For simplicity in this help file, we always use CSingleLock for the example lock type, even though Thread Lock Checker can detect many more lock types than that.

 

 

Critical Sections and locks

Critical sections are used to provide synchronised access to data so that only one part of a program can modify the data at a time an no one can read the data while it is being modified. To make the management of locking and unlocking critical sections easy Microsoft introduced the concept of the single lock and multi lock, represented by the CSingleLock and CMultiLock MFC classes. Other third party libraries also have similar constructs (we'll cover those later).

 

A really important and useful thing about using CSingleLock and CMultiLock to control locks on the stack is that if an exception is thrown the locks are automatically cleaned up by the exception handling chain and all locks are automatically released, preventing the likelihood of an exception causing a deadlock due to a lock being left locked during an exception handling sequence.

 

A feature of the CSingleLock and CMultiLock classes is that their constructor constructs the lock and by default initialises it to be unlocked. Class methods Lock() and Unlock() are provided to lock the object. If the object goes out of scope (or is deleted if it is on the heap) when it is locked, the object automatically unlocks itself.

 

Thus you could have several different ways of expressing the same concept:

 

1. Start with an unlocked lock, do some work, lock the lock, access protected code, unlock the lock, do some work

 

void doWork1()

{

 CSingleLock        lock(&critSect);                // start unlocked

 

 ...

 

 lock.Lock();

 

 ... // access protected resource

 

 lock.Unlock();

 

 ...

}

 

2. Start with an unlocked lock on the heap, do some work, lock the lock, access protected code, unlock the lock, delete the lock, do some work

 

void doWork2()

{

 CSingleLock        *lock;

 

 lock = new CSingleLock(&critSect);                // start unlocked

 

 ...

 

 lock.Lock();

 

 ... // access protected resource

 

 lock->Unlock();

 delete lock;

 

 ...

}

 

3. Start with an unlocked lock on the heap, do some work, lock the lock, access protected code, delete the lock (implicitly unlocking the lock), do some work

 

void doWork3()

{

 CSingleLock        *lock;

 

 lock = new CSingleLock(&critSect);                // start unlocked

 

 ...

 

 lock.Lock();

 

 ... // access protected resource

 

 delete lock;        // implicitly unlock the lock during the delete

 

 ...

}

 

4. do some work, create a local scope, create a locked lock, access protected code, leave local scope implicitly unlocking and destroying the lock, do some work

 

void doWork4()

{

 ...

 

 {

         CSingleLock        lock(&critSect, TRUE);                // start locked

 

         ... // access protected resource

 

         // lock is unlocked at the end of the local scope

 }

 

 ...

}

 

For the most part using CSingleLock and CMultilock with the locking parameter set to its default value (FALSE) is not very useful. This leaves you with a lock that by default is not locked and implies that you will be using the Lock() and Unlock() methods to manage if the lock is locked or unlocked.

 

The real value in the CSingleLock and CMultiLock classes is that you can pass TRUE to the constructor so that the lock is created in the locked state and just let the lock destruction unlock the lock implicitly. Using this method you can ensure that you never leave locks unlocked and never forget to lock a lock. This is demonstrated in doWork4() above.

 

 

Thread Lock Checker

Thread Lock Checker scans your source code looking for common errors when locking critical sections using classes like CSingleLock and CMultiLock.

 

There are four valid ways to initialise a CSingleLock object using its constructor:

 

 CSingleLock        lock(&sect);                        // default unlocked

 

 CSingleLock        lock(&sect, FALSE);                // unlocked

 

 CSingleLock        lock(&sect, TRUE);                // locked

 

 CSingleLock        lock(&sect, lockStatus);        // lockStatus decides if locked or not

 

Of the methods shown above, only one is guaranteed to lock and protect the resouce. The first two do not lock and protect the resource. The last method depends on the variable to lock and protect the resource. Thread Lock Checker detects all of these conditions. By default the locked state (3rd in the list above) is not reported for your attention.

 

There are also many invalid ways to initialise a CSingleLock object that will be accepted by the compiler.

The following is four examples of forgetting to specify the variable name. This results in a lock that is created then immediately destroyed. Even if it gets locked it will be destroyed (and thus unlocked) before it can be used to protect the critical resource.

 

 CSingleLock(&sect);                        // default unlocked

 

 CSingleLock(&sect, FALSE);                // unlocked

 

 CSingleLock(&sect, TRUE);                // locked

 

 CSingleLock(&sect, lockStatus);        // lockStatus decides if locked or not

 

Thread Lock Checker will report all four of these invalid ways of creating a CSingleLock for your attention.