There are limitations to LockLint's powers of analysis. At the root of many of its difficulties is the fact that LockLint doesn't know the values of your variables.
LockLint solves some of these problems by ignoring the likely cause or making simplifying assumptions. You can avoid some other problems by using conditionally compiled code in the application. Towards this end, the compiler always defines the preprocessor macro __lock_lint when you compile with the -Zll option. You can use this macro to make your code less ambiguous.
LockLint has trouble deducing:
Which functions your function pointers point to. There are some assignments LockLint cannot deduce (see "declare"). The declare subcommand can be used to add new possible assignments to the function pointer.
When LockLint sees a call through a function pointer, it tests that call path for every possible value of that function pointer. If you know or suspect that some calling sequences are never executed, use the disallow and reallow subcommands to specify which sequences are executed.
Whether or not you locked a lock in code like this:
if (x) pthread_mutex_lock(&lock1);
In this case, two execution paths are created, one holding the lock, and one not holding the lock, which will probably cause the generation of a side effect message at the unlock call. You may be able to work around this problem by using the __lock_lint macro to force LockLint to treat a lock as unconditionally taken. For example:
#ifdef __lock_lint pthread_mutex_lock(&lock1); #else if (x) pthread_mutex_lock(&lock1); #endif
LockLint has no problem analyzing code like this:
if (x) { pthread_mutex_lock(&lock1); foo(); pthread_mutex_unlock(&lock1); }
In this case, there is only one execution path, along which the lock is acquired and released, causing no side effects.
Whether or not a lock was acquired in code like this:
rc = pthread_mutex_trylock(&lock1); if (rc) ...
Which lock is being locked in code like this:
pthread_mutex_t* lockp; pthread_mutex_lock(lockp);
In such cases, the lock call is ignored.
Which variables and locks are being used in code where elements of a structure are used (see "Lock Inversions"):
struct foo* p; pthread_mutex_lock(p->lock); p->bar = 0;
Which element of an array is being accessed. This is treated analogously to the previous case; the index is ignored.
Anything about longjmps.
When you would exit a loop or break out of a recursion (so it just stops proceeding down a path as soon as it finds itself looping or after one recursion).
Some other LockLint difficulties:
LockLint only analyzes the use of mutex locks and readers-writer locks. LockLint performs limited consistency checks of mutex locks as used with condition variables. However, semaphores and condition variables are not recognized as locks by LockLint. Even with this analysis, there are limits to what LockLint can make sense of.
There are situations where LockLint thinks two different variables are the same variable, or that a single variable is two different variables. (See "Lock Inversions".)
It is possible to share automatic variables between threads (via pointers), but LockLint assumes that automatics are unshared, and generally ignores them (the only situation in which they are of interest to LockLint is when they are function pointers).
LockLint complains about any functions that are not consistent in their side effects on locks. #ifdef's and assertions must be used to give LockLint a simpler view of functions that may or may not have such a side effect.
During analysis, LockLint may produce messages about a lock operation called rw_upgrade. Such a call does not really exist, but LockLint rewrites code like
if (rw_tryupgrade(&lock1)) { ... }
if () { rw_tryupgrade(&lock1); ... }
such that, wherever rw_tryupgrade() occurs, LockLint always assumes it succeeds.
One of the errors LockLint flags is an attempt to acquire a lock that is already held. However, if the lock is unnamed (for example, foo::lock), this error is suppressed, since the name refers not to a single lock but to a set of locks. However, if the unnamed lock always refers to the same lock, use the declare one subcommand so that LockLint can report this type of potential deadlock.
If you have constructed your own locks out of these locks (for example, recursive mutexes are sometimes built from ordinary mutexes), LockLint will not know about them. Generally you can use #ifdef to make it appear to LockLint as though an ordinary mutex is being manipulated. For recursive locks, use an unnamed lock for this deception, since errors won't be generated when it is recursively locked. For example:
void get_lock() { #ifdef __lock_lint struct bogus *p; pthread_mutex_lock(p->lock); #else <the real recursive locking code> #endif }