.ds RH How It Works
.bp 
.ce
.ps 16 
How Race Coverage Works
.ps 12
.XS
How Race Coverage Works
.XE
.sp
.LP
.NH 1
How Race Coverage Works.
.LP
The current implementation has some minor support for a future
extension: measuring races in groups of routines, rather than in a
single routine.  For that reason, this discussion talks about "race
groups".  When you see that term, think "routine".
.LP
When any routine of a race group is entered, the condition count is
incremented if another thread is currently in that race group.  A
thread leaves the race group when the routine returns or calls another
routine.  (In the case of a call, the thread reenters the race group
when it returns from the call.) Thus, the count is incremented only if
two threads are executing the immediate body of a race group routine
at the same time.
.LP
Here's what GCT does, in more detail:
.IP (1)
GCT inserts the macro GCT_RACE_GROUP_CHECK just before the first
statement of a routine.  
This might be the result of that instrumentation:
.ne 10
.nf

	main()
	{  
	   char myvar = complicated_routine();

	   GCT_RACE_GROUP_CHECK(23, 1); myvar++;
	   ...
	}

.fi
Notice that the macro is placed after any variable declarations, even
if those declarations call many functions.  If thread 1 is
executing the declarations and thread 2 is in the body of a
function, a bug is less likely to be triggered than if both are in the body,
so we count only the latter case.
.sp
GCT_RACE_GROUP_CHECK checks whether the race group (1, in this case) is
racing.  If so, it increments the appropriate condition count (23, in
this case).  There is one condition count for every race group.
.sp
The default definition of this GCT_RACE_GROUP_CHECK simply checks whether any
bit is set:
.nf

/* Test whether another thread is in the same race group. */
#define GCT_RACING(group)\
    (Gct_group_table[(group)])

#define GCT_RACE_GROUP_CHECK(index, group)\\
    (_G(index, GCT_RACING(group)))

.fi
_G is a standard GCT macro that increments the condition count if the
second argument is non-zero.  
Gct_group_table is an array of words.  Each thread is assigned one of
the bits.  When a thread enters a routine, its bit is set.  This macro
will have to be changed if there are more threads than bits in a word.
.sp
The GCT_RACING test works because the current thread does not record
its own entry into the race group until after the check.
.ne 12
.IP (2)
Immediately after GCT_RACE_GROUP_CHECK, GCT inserts a call to
GCT_RACE_GROUP_ENTER, like this:
.nf

	main()
	{  
	   char myvar = complicated_routine();

	   GCT_RACE_GROUP_CHECK(23, 1); GCT_RACE_GROUP_ENTER(1); myvar++;
	   ...
	}

.fi
.sp
GCT_RACE_GROUP_ENTER takes a number which uniquely identifies the
function.  Its default definition (found in gct-defs.h) looks like
this:
.ne 4
.nf

#define GCT_RACE_GROUP_ENTER(group) \\
    (Gct_group_table[(group)] |= (1 << GCT_THREAD))

.fi
.IP (3) 
Normally, we do not consider a routine to be racing if thread
1 is in its body and thread 2 is in some subfunction:  in such a
case, there's a much smaller chance that the two routines will
actually have a locking conflict.
Therefore, when a function is called, GCT surrounds its call with the macros 
GCT_RACE_GROUP_CALL and GCT_RACE_GROUP_REENTER:
.ne 8
.nf

	i = find_something();

		becomes

	i = (GCT_RACE_GROUP_CALL(1), _G123 = find_something(),
		GCT_RACE_GROUP_REENTER(1), _G123);

.fi
.ne 10
These routines turn the thread's bit off and then back on,
respectively:
.nf

#define GCT_RACE_GROUP_CALL(group) 
    (Gct_group_table[(group)] &= ~(1 << GCT_THREAD))

#define GCT_RACE_GROUP_REENTER(group) 
    (Gct_group_table[(group)] |= (1 << GCT_THREAD))

.fi
.ne 8
If you prefer to count threads in subfunctions, simply define these
macros to do nothing:
.nf

#define GCT_NO_OP  49   /* random expression needed to avoid syntax error */
#define GCT_RACE_GROUP_CALL(group)    GCT_NO_OP
#define GCT_RACE_GROUP_REENTER(group)    GCT_NO_OP

.fi
.sp
If you define GCT_RACE_GROUP_CALL to do nothing, GCT_RACE_GROUP_CHECK
has to be changed to avoid counting recursive calls as races.  See
\fIgct-defs.h\fR for the new definition.
.sp
Note that special non-returning "magic cookies" like longjmp() or the
UNIX kernel's swtch() routines look like function calls; they will not fool
GCT.
.IP (4)
When a routine returns (whether explicitly or by falling off the
end), GCT_RACE_GROUP_EXIT is called.  If the original end of the
routine looked like:
.ne 6
.nf

	if (errors > 0)
		return;
	clean_up_temporaries();
      }

.fi
.ne 8
GCT would rewrite it as
.nf


	if (errors > 0)
		{ GCT_RACE_GROUP_EXIT(1); return;}
	clean_up_temporaries();
      GCT_RACE_GROUP_EXIT(1);}


.fi
.ne 10
The standard macro does the same thing as GCT_RACE_GROUP_CALL.
GCT_RACE_GROUP_EXIT will not be added before the closing brace
if a return statement directly precedes it.  However, in code like
.nf

	if (test)
		return 5;
	else
		return 4;
	} /* End of routine. */

.fi
GCT will add a GCT_RACE_GROUP_EXIT before the closing brace.  
This may provoke
warnings about unreached statements from your compiler; ignore them.
.NH 2
Miscellany
.LP
You might be tempted to worry about problems caused by the lack of
locking in the GCT macros.  Don't bother.  The lack of locking may
lead to a "missed decrement" if two threads are manipulating the
Gct_group_table at the same time:  in this case, the number of races
will be inflated -- that's OK, since your goal was to find when a race
occurred and one certainly did.  The lack of locking may
also cause an underestimate of the number of races.  That's OK, too:
you should aim to count many races, so mistakenly seeing no races when
there was in fact exactly one will probably not change your interpretation of
the results. 
.LP
Note that GCT_RACE_GROUP_EXIT is placed before return statements.
That means that the returned expression is "outside" the race group,
exactly as declarations are.  No GCT_RACE_GROUP_CALL or
GCT_RACE_GROUP_REENTER macros are generated for function calls within
returned expressions.
