Multithreaded Programming Guide

Scenario: Checking a Program With LockLint

A program can run efficiently but still contain potential problems. One such problem occurs when two threads try to access the same data simultaneously. This can lead to:

Here's how you can use LockLint to see if data is adequately protected.

Figure 8-7 The LockLint Usage Flowchart

Graphic

  1. Compile the program with LockLint instrumentation.

    The compiler has an option to produce a version of the program that LockLint can use for analysis.

  2. Create a LockLint shell and load the instrumented program.

    You can use this shell as you would any other, including running scripts.

  3. Save the executable's state.

    LockLint is designed to run iteratively. You run it over and over, making progressively stronger assertions about the data it is analyzing, until you find a problem or are satisfied that the data is safe.


    Note -

    Analyzing the program with LockLint changes its state; that is, once you've done an analysis, you can't add further assertions. By saving and restoring the state, you can run the analysis over and over, with different assertions about the program's data.


  4. Analyze the program.

    The analyze command performs consistency checks on the program's data.

  5. Search for unsafe data.

    Having run the analysis, you can look for unprotected elements.

    The held=[] indicates that variables did not have locks consistently held on them while they were accessed. An asterisk preceding the held=[] indicates that these variables were written to. An asterisk, therefore, means that LockLint "believes" the data is not safe.


    Example 8-1 Fragment of Initial LockLint Output

    $ lock_lint analyze
    $ lock_lint vars -h | grep held
    :arrow_cursor								*held={ }
    :bottom_row								*held={ }
    :box_height								*held={ }
    :box_width								*held={ }
    :box_x								*held={ }
    :busy_cursor   *held={ }
    :c_text *held={ }
    :calc_mandel    *held={ }
    :calc_type  *held={ }
    :canvas *held={ }
    :canvas_proc/drag   *held={ }
    :canvas_proc/x  *held={ }
    [. . . ]
    :gap								*held={ }
    :gc								*held={ }
    :next_row								*held={ }
    :now.tv_sec								held={ }
    :now.tv_usec								held={ }
    :p_text								*held={ }
    :panel								*held={ }
    :picture_cols								*held={ }
    :picture_id								*held={ }
    :picture_rows								*held={ }
    :picture_state								*held={ }
    :pw								*held={ }
    :ramp.blue								*held={ }
    :ramp.green								*held={ }
    :ramp.red								*held={ }
    :rectangle_selected	*held={ }
    :row_inc								*held={ }
    :run_button								*held={ }
    [ . . . ]

    However, this analysis is limited in its usefulness because many of the variables displayed do not need to be protected (such as variables that are not written to, except when they're initialized). By excluding some data from consideration, and having LockLint repeat its analyses, you can narrow your search to the unprotected variables that you are interested in.

  6. Restore the program to its saved state.

    To be able to run the analysis again, pop the state back to what it was before the program was last analyzed.

  7. Refine the analysis by excluding some data.

    For example, you can ignore variables that aren't written to--since they don't change, they won't cause data races. And you can ignore places where the variables are initialized (if they're not visible to other threads).

    You can ignore the variables that you know are safe by making assertions about them. In the code example below, the following is done:

    • Initialization functions are ignored because no data is overwritten at initialization.

    • Some variables are asserted to be read-only.

    For illustration, this is done on the command line in verbose mode. After you become familiar with the command syntax, you can use aliases and shell scripts to make the task easier.

    $ lock_lint ignore CreateXStuff run_proc canvas_proc main
    $ lock_lint assert read only bottom_row
    $ lock_lint assert read only calc_mandel
    etc.
  8. Analyze the program again, and search for unsafe data.

    The list of unsafe data is considerably reduced.


    Example 8-2 Unsafe Data Reported by LockLint

    $ lock_lint vars -h | grep held
    :bottom_row								held={ }
    :calc_mandel								held={ }
    :colors								held={ }
    :corner_i								held={ }
    :corner_r								held={ }
    :display								held={ }
    :drawable								held={ }
    :frame								held={ }
    :gap								held={ }
    :gc								held={ }
    :next_row								held={
    mandel_display.c:next_row_lock }
    :picture_cols								held={ }
    :picture_id								held={ }
    :picture_rows								*held={ }
    :picture_state								*held={ }
    :row_inc								held={ }

    Only two variables were written to (picture_rows and picture_state) and are flagged by LockLint as inconsistently protected.

    The analysis also flags the variable next_row, which the calculator threads use to find the next chunk of work to be done. However, as the analysis states, this variable is consistently protected.

    Alter your source code to properly protect picture_rows and picture_state.