Analyzing Program Performance With Sun WorkShop

Assertions Recognized by LockLint

LockLint recognizes some assertions as relevant to the state of threads and locks. (For more information, see the assert man page.)

Assertions may be made only within a function definition, where a statement is allowed.


Note -

ASSERT() is used in kernel and driver code, whereas assert() is used in user (application) code. For simplicity's sake, this document uses assert() to refer to either one, unless explicitly stated otherwise.


Making Sure All Locks Are Released

assert(NO_LOCKS_HELD);

LockLint recognizes this assertion to mean that, when this point in the code is reached, no locks should be held by the thread executing this test. Violations are reported during analysis. A routine that blocks might want to use such an assertion to ensure that no locks are held when a thread blocks or exits.

The assertion also clearly serves as a reminder to someone modifying the code that any locks acquired must be released at that point.

It is really only necessary to use this assertion in leaf-level functions that block. If a function blocks only inasmuch as it calls another function that blocks, the caller need not contain this assertion as long as the callee does. Therefore this assertion probably sees its heaviest use in versions of libraries (for example, libc) written specifically for LockLint (like lint libraries).

The file synch.h defines NO_LOCKS_HELD as 1 if it has not already been otherwise defined, causing the assertion to succeed; that is, the assertion is effectively ignored at runtime. You can override this default runtime meaning by defining NO_LOCKS_HELD before you include either note.h or synch.h (which may be included in either order). For example, if a body of code uses only two locks called a and b, the following definition would probably suffice:


#define NO_LOCKS_HELD (!MUTEX_HELD(&a) && !MUTEX_HELD(&b))
#include <note.h>
#include <synch.h>

Doing so does not affect LockLint's testing of the assertion; that is, LockLint still complains if any locks are held (not just a or b).

Making Sure No Other Threads Are Running

assert(NO_COMPETING_THREADS);

LockLint recognizes this assertion to mean that, when this point in the code is reached, no other threads should be competing with the one running this code. Violations (based on information provided by certain NOTE-style assertions) are reported during analysis. Any function that accesses variables without holding their protecting locks (operating under the assumption that no other relevant threads are out there touching the same data), should be so marked.

By default, this assertion is ignored at runtime--that is, it always succeeds. No generic runtime meaning for NO_COMPETING_THREADS is possible, since the notion of which threads compete involves knowledge of the application. For example, a driver might make such an assertion to say that no other threads are running in this driver for the same device. Because no generic meaning is possible, synch.h defines NO_COMPETING_THREADS as 1 if it has not already been otherwise defined.

However, you can override the default meaning for NO_COMPETING_THREADS by defining it before including either note.h or synch.h (which may be included in either order). For example, if the program keeps a count of the number of running threads in a variable called num_threads, the following definition might suffice:


#define NO_COMPETING_THREADS (num_threads == 1)
#include <note.h>
#include <synch.h>

Doing so does not affect LockLint's testing of the assertion.

Asserting Lock State

assert(MUTEX_HELD(lock_expr) && ...);

This assertion is widely used within the kernel. It performs runtime checking if assertions are enabled. The same capability exists in user code.

This code does roughly the same thing during LockLint analysis as it does when the code is actually run with assertions enabled; that is, it reports an error if the executing thread does not hold the lock as described.


Note -

The thread library performs a weaker test, only checking that some thread holds the lock. LockLint performs the stronger test.


LockLint recognizes the use of MUTEX_HELD(), RW_READ_HELD(), RW_WRITE_HELD(), and RW_LOCK_HELD() macros, and negations thereof. Such macro calls may be combined using the && operators. For example, the following assertion causes LockLint to check that a mutex is not held and that a readers-writer lock is write-held:


assert(p && !MUTEX_HELD(&p->mtx) && RW_WRITE_HELD(&p->rwlock));

LockLint also recognizes expressions like:

MUTEX_HELD(&foo) == 0