mirror of
https://github.com/boostorg/build.git
synced 2026-02-15 13:02:11 +00:00
Integrate semaphores extension from Craig McPeeters' branch on
public.perforce.com (revisions 1664, 1665). [SVN r16472]
This commit is contained in:
@@ -67,6 +67,7 @@ else { code = execunix.c fileunix.c pathunix.c ; }
|
||||
# Perforce public depot.
|
||||
LOCAL_DEFINES += OPT_HEADER_CACHE_EXT ;
|
||||
LOCAL_DEFINES += OPT_GRAPH_DEBUG_EXT ;
|
||||
LOCAL_DEFINES += OPT_SEMAPHORE ;
|
||||
|
||||
# Improvements developed locally.
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ if ! $(debug)
|
||||
# Enable some optional features.
|
||||
--defs += OPT_HEADER_CACHE_EXT ;
|
||||
--defs += OPT_GRAPH_DEBUG_EXT ;
|
||||
--defs += OPT_SEMAPHORE ;
|
||||
|
||||
# Bug fixes
|
||||
--defs += OPT_FIX_TARGET_VARIABLES_EXT ;
|
||||
|
||||
@@ -275,6 +275,19 @@ make0(
|
||||
|
||||
/* Step 2c: If its a file, search for headers. */
|
||||
|
||||
#ifdef OPT_SEMAPHORE
|
||||
{
|
||||
LIST *var = var_get( "JAM_SEMAPHORE" );
|
||||
if( var )
|
||||
{
|
||||
TARGET *semaphore = bindtarget( var->string );
|
||||
|
||||
semaphore->progress = T_MAKE_SEMAPHORE;
|
||||
t->semaphore = semaphore;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( t->binding == T_BIND_EXISTS )
|
||||
headers( t );
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
|
||||
# include "lists.h"
|
||||
# include "parse.h"
|
||||
# include "assert.h"
|
||||
# include "variable.h"
|
||||
# include "rules.h"
|
||||
|
||||
@@ -262,6 +263,7 @@ make1( TARGET *t )
|
||||
static void
|
||||
make1a( state *pState)
|
||||
{
|
||||
TARGET* t = pState->t;
|
||||
TARGETS *c;
|
||||
int i;
|
||||
|
||||
@@ -293,7 +295,6 @@ make1a( state *pState)
|
||||
|
||||
pState->t->asynccnt = 1;
|
||||
|
||||
/* Recurse on our dependents, manipulating progress to guard */
|
||||
/* against circular dependency. */
|
||||
|
||||
pState->t->progress = T_MAKE_ONSTACK;
|
||||
@@ -329,6 +330,7 @@ static void make1atail(state *pState)
|
||||
static void
|
||||
make1b( state *pState )
|
||||
{
|
||||
TARGET *t = pState->t;
|
||||
TARGETS *c;
|
||||
int i;
|
||||
char *failed = "dependents";
|
||||
@@ -341,6 +343,24 @@ make1b( state *pState )
|
||||
pop_state(&state_stack);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to aquire a semaphore. If it's locked, wait until the target
|
||||
that locked it is build and signals completition. */
|
||||
#ifdef OPT_SEMAPHORE
|
||||
if( t->semaphore && t->semaphore->asynccnt )
|
||||
{
|
||||
/* Append 't' to the list of targets waiting on semaphore. */
|
||||
t->semaphore->parents = targetentry( t->semaphore->parents, t );
|
||||
t->asynccnt++;
|
||||
|
||||
if( DEBUG_EXECCMD )
|
||||
printf( "SEM: %s is busy, delaying launch of %s\n",
|
||||
t->semaphore->name, t->name);
|
||||
pop_state(&state_stack);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Now ready to build target 't'... if dependents built ok. */
|
||||
|
||||
@@ -394,6 +414,7 @@ make1b( state *pState )
|
||||
case T_FATE_MISSING:
|
||||
case T_FATE_OUTDATED:
|
||||
case T_FATE_UPDATE:
|
||||
|
||||
/* Set "on target" vars, build actions, unset vars */
|
||||
/* Set "progress" so that make1c() counts this target among */
|
||||
/* the successes/failures. */
|
||||
@@ -419,6 +440,21 @@ make1b( state *pState )
|
||||
/* (because of dependency failures or because no commands need to */
|
||||
/* be run) the chain will be empty and make1c() will directly */
|
||||
/* signal the completion of target. */
|
||||
|
||||
/* Recurse on our dependents, manipulating progress to guard */
|
||||
|
||||
#ifdef OPT_SEMAPHORE
|
||||
/* If there is a semaphore, indicate that its in use */
|
||||
if( pState->t->semaphore )
|
||||
{
|
||||
++(pState->t->semaphore->asynccnt);
|
||||
|
||||
if( DEBUG_EXECCMD )
|
||||
printf( "SEM: %s now used by %s\n", pState->t->semaphore->name,
|
||||
pState->t->name );
|
||||
}
|
||||
#endif
|
||||
|
||||
pState->curstate = T_STATE_MAKE1C;
|
||||
}
|
||||
|
||||
@@ -506,12 +542,42 @@ make1c( state *pState )
|
||||
|
||||
for( c = t->parents; c; c = c->next )
|
||||
push_state(&temp_stack, c->target, NULL, T_STATE_MAKE1B);
|
||||
|
||||
#ifdef OPT_SEMAPHORE
|
||||
/* If there is a semaphore, its now free */
|
||||
if( t->semaphore )
|
||||
{
|
||||
assert( t->semaphore->asynccnt == 1 );
|
||||
--(t->semaphore->asynccnt);
|
||||
|
||||
if( DEBUG_EXECCMD )
|
||||
printf( "SEM: %s is now free\n", t->semaphore->name);
|
||||
|
||||
/* If anything is waiting, notify the next target. There's no
|
||||
point in notifying all waiting targets, since they'll be
|
||||
serialized again. */
|
||||
if( t->semaphore->parents )
|
||||
{
|
||||
TARGETS *first = t->semaphore->parents;
|
||||
if( first->next )
|
||||
first->next->tail = first->tail;
|
||||
t->semaphore->parents = first->next;
|
||||
|
||||
if( DEBUG_EXECCMD )
|
||||
printf( "SEM: placing %s on stack\n", first->target->name);
|
||||
push_state(&temp_stack, first->target, NULL, T_STATE_MAKE1B);
|
||||
free( first );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* must pop state before pushing any more */
|
||||
pop_state(&state_stack);
|
||||
|
||||
/* using stacks reverses the order of execution. Reverse it back */
|
||||
push_stack_on_stack(&state_stack, &temp_stack);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
</dd>
|
||||
<dt><a href="#UPDATE">The <tt>UPDATE</tt> rule and changes to
|
||||
command line handling</a></dt>
|
||||
<dt><a href="#semaphores">Semaphores</a></dt>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><a href="#jam_fundamentals">Jam Fundamentals</a></dt>
|
||||
@@ -760,6 +761,29 @@ rule UPDATE ( targets * )
|
||||
<p>The rule causes the specified targets to be updated. If no target was
|
||||
specified with the <tt>UPDATE</tt> rule, the "all" target will be
|
||||
implicitly updated.</p>
|
||||
|
||||
<h3 id="semaphores">Semaphores</h3>
|
||||
|
||||
<p>It is sometimes desirable to disallow parallel execution of
|
||||
some actions. For example:
|
||||
<ul>
|
||||
<li>Old versions of <tt>yacc</tt> use files with fixed names. So,
|
||||
running two yacc actions is dangerous.
|
||||
<li>One might want to perform parallel compiling, but not do parallel
|
||||
linking, because linking is i/o bound and only gets slower.
|
||||
</ul>
|
||||
Craig McPeeters has extended Perforce Jam to solve such problems, and that
|
||||
extension was integrated in Boost.Jam.
|
||||
|
||||
<p>Any target can be assigned a <em>semaphore</em>, by setting a variable
|
||||
called <tt>SEMAPHORE</tt> on that target. The value of the variable is
|
||||
the semaphore name. It must be different from names of any declared
|
||||
target, but is arbitrary otherwise.
|
||||
|
||||
<p>The semantic of semaphores is that in a group of targets which
|
||||
have the same semaphore, only one can be updated at the moment,
|
||||
regardless of "-j" option.
|
||||
|
||||
<h2><a name="jam_fundamentals">Jam Fundamentals</a></h2>
|
||||
<p>This section is derived from the official Jam documentation and from
|
||||
my experience using it and reading the Jambase rules. I repeat the
|
||||
|
||||
@@ -153,11 +153,18 @@ struct _target {
|
||||
/* runs from a Jamfile.. */
|
||||
/* */
|
||||
# define T_FLAG_FAIL_EXPECTED 0x0100 /* FAIL_EXPECTED applied */
|
||||
#ifdef OPT_SEMAPHORE
|
||||
# define T_MAKE_SEMAPHORE 5 /* Special target type for semaphores */
|
||||
#endif
|
||||
|
||||
|
||||
char binding; /* how target relates to real file */
|
||||
|
||||
# define T_BIND_UNBOUND 0 /* a disembodied name */
|
||||
# define T_BIND_MISSING 1 /* couldn't find real file */
|
||||
#ifdef OPT_SEMAPHORE
|
||||
TARGET *semaphore; /* used in serialization */
|
||||
#endif
|
||||
# define T_BIND_PARENTS 2 /* using parent's timestamp */
|
||||
# define T_BIND_EXISTS 3 /* real file, timestamp valid */
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ else { code = execunix.c fileunix.c pathunix.c ; }
|
||||
# Perforce public depot.
|
||||
LOCAL_DEFINES += OPT_HEADER_CACHE_EXT ;
|
||||
LOCAL_DEFINES += OPT_GRAPH_DEBUG_EXT ;
|
||||
LOCAL_DEFINES += OPT_SEMAPHORE ;
|
||||
|
||||
# Improvements developed locally.
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ if ! $(debug)
|
||||
# Enable some optional features.
|
||||
--defs += OPT_HEADER_CACHE_EXT ;
|
||||
--defs += OPT_GRAPH_DEBUG_EXT ;
|
||||
--defs += OPT_SEMAPHORE ;
|
||||
|
||||
# Bug fixes
|
||||
--defs += OPT_FIX_TARGET_VARIABLES_EXT ;
|
||||
|
||||
@@ -275,6 +275,19 @@ make0(
|
||||
|
||||
/* Step 2c: If its a file, search for headers. */
|
||||
|
||||
#ifdef OPT_SEMAPHORE
|
||||
{
|
||||
LIST *var = var_get( "JAM_SEMAPHORE" );
|
||||
if( var )
|
||||
{
|
||||
TARGET *semaphore = bindtarget( var->string );
|
||||
|
||||
semaphore->progress = T_MAKE_SEMAPHORE;
|
||||
t->semaphore = semaphore;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( t->binding == T_BIND_EXISTS )
|
||||
headers( t );
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
|
||||
# include "lists.h"
|
||||
# include "parse.h"
|
||||
# include "assert.h"
|
||||
# include "variable.h"
|
||||
# include "rules.h"
|
||||
|
||||
@@ -262,6 +263,7 @@ make1( TARGET *t )
|
||||
static void
|
||||
make1a( state *pState)
|
||||
{
|
||||
TARGET* t = pState->t;
|
||||
TARGETS *c;
|
||||
int i;
|
||||
|
||||
@@ -293,7 +295,6 @@ make1a( state *pState)
|
||||
|
||||
pState->t->asynccnt = 1;
|
||||
|
||||
/* Recurse on our dependents, manipulating progress to guard */
|
||||
/* against circular dependency. */
|
||||
|
||||
pState->t->progress = T_MAKE_ONSTACK;
|
||||
@@ -329,6 +330,7 @@ static void make1atail(state *pState)
|
||||
static void
|
||||
make1b( state *pState )
|
||||
{
|
||||
TARGET *t = pState->t;
|
||||
TARGETS *c;
|
||||
int i;
|
||||
char *failed = "dependents";
|
||||
@@ -341,6 +343,24 @@ make1b( state *pState )
|
||||
pop_state(&state_stack);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to aquire a semaphore. If it's locked, wait until the target
|
||||
that locked it is build and signals completition. */
|
||||
#ifdef OPT_SEMAPHORE
|
||||
if( t->semaphore && t->semaphore->asynccnt )
|
||||
{
|
||||
/* Append 't' to the list of targets waiting on semaphore. */
|
||||
t->semaphore->parents = targetentry( t->semaphore->parents, t );
|
||||
t->asynccnt++;
|
||||
|
||||
if( DEBUG_EXECCMD )
|
||||
printf( "SEM: %s is busy, delaying launch of %s\n",
|
||||
t->semaphore->name, t->name);
|
||||
pop_state(&state_stack);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Now ready to build target 't'... if dependents built ok. */
|
||||
|
||||
@@ -394,6 +414,7 @@ make1b( state *pState )
|
||||
case T_FATE_MISSING:
|
||||
case T_FATE_OUTDATED:
|
||||
case T_FATE_UPDATE:
|
||||
|
||||
/* Set "on target" vars, build actions, unset vars */
|
||||
/* Set "progress" so that make1c() counts this target among */
|
||||
/* the successes/failures. */
|
||||
@@ -419,6 +440,21 @@ make1b( state *pState )
|
||||
/* (because of dependency failures or because no commands need to */
|
||||
/* be run) the chain will be empty and make1c() will directly */
|
||||
/* signal the completion of target. */
|
||||
|
||||
/* Recurse on our dependents, manipulating progress to guard */
|
||||
|
||||
#ifdef OPT_SEMAPHORE
|
||||
/* If there is a semaphore, indicate that its in use */
|
||||
if( pState->t->semaphore )
|
||||
{
|
||||
++(pState->t->semaphore->asynccnt);
|
||||
|
||||
if( DEBUG_EXECCMD )
|
||||
printf( "SEM: %s now used by %s\n", pState->t->semaphore->name,
|
||||
pState->t->name );
|
||||
}
|
||||
#endif
|
||||
|
||||
pState->curstate = T_STATE_MAKE1C;
|
||||
}
|
||||
|
||||
@@ -506,12 +542,42 @@ make1c( state *pState )
|
||||
|
||||
for( c = t->parents; c; c = c->next )
|
||||
push_state(&temp_stack, c->target, NULL, T_STATE_MAKE1B);
|
||||
|
||||
#ifdef OPT_SEMAPHORE
|
||||
/* If there is a semaphore, its now free */
|
||||
if( t->semaphore )
|
||||
{
|
||||
assert( t->semaphore->asynccnt == 1 );
|
||||
--(t->semaphore->asynccnt);
|
||||
|
||||
if( DEBUG_EXECCMD )
|
||||
printf( "SEM: %s is now free\n", t->semaphore->name);
|
||||
|
||||
/* If anything is waiting, notify the next target. There's no
|
||||
point in notifying all waiting targets, since they'll be
|
||||
serialized again. */
|
||||
if( t->semaphore->parents )
|
||||
{
|
||||
TARGETS *first = t->semaphore->parents;
|
||||
if( first->next )
|
||||
first->next->tail = first->tail;
|
||||
t->semaphore->parents = first->next;
|
||||
|
||||
if( DEBUG_EXECCMD )
|
||||
printf( "SEM: placing %s on stack\n", first->target->name);
|
||||
push_state(&temp_stack, first->target, NULL, T_STATE_MAKE1B);
|
||||
free( first );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* must pop state before pushing any more */
|
||||
pop_state(&state_stack);
|
||||
|
||||
/* using stacks reverses the order of execution. Reverse it back */
|
||||
push_stack_on_stack(&state_stack, &temp_stack);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
</dd>
|
||||
<dt><a href="#UPDATE">The <tt>UPDATE</tt> rule and changes to
|
||||
command line handling</a></dt>
|
||||
<dt><a href="#semaphores">Semaphores</a></dt>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><a href="#jam_fundamentals">Jam Fundamentals</a></dt>
|
||||
@@ -760,6 +761,29 @@ rule UPDATE ( targets * )
|
||||
<p>The rule causes the specified targets to be updated. If no target was
|
||||
specified with the <tt>UPDATE</tt> rule, the "all" target will be
|
||||
implicitly updated.</p>
|
||||
|
||||
<h3 id="semaphores">Semaphores</h3>
|
||||
|
||||
<p>It is sometimes desirable to disallow parallel execution of
|
||||
some actions. For example:
|
||||
<ul>
|
||||
<li>Old versions of <tt>yacc</tt> use files with fixed names. So,
|
||||
running two yacc actions is dangerous.
|
||||
<li>One might want to perform parallel compiling, but not do parallel
|
||||
linking, because linking is i/o bound and only gets slower.
|
||||
</ul>
|
||||
Craig McPeeters has extended Perforce Jam to solve such problems, and that
|
||||
extension was integrated in Boost.Jam.
|
||||
|
||||
<p>Any target can be assigned a <em>semaphore</em>, by setting a variable
|
||||
called <tt>SEMAPHORE</tt> on that target. The value of the variable is
|
||||
the semaphore name. It must be different from names of any declared
|
||||
target, but is arbitrary otherwise.
|
||||
|
||||
<p>The semantic of semaphores is that in a group of targets which
|
||||
have the same semaphore, only one can be updated at the moment,
|
||||
regardless of "-j" option.
|
||||
|
||||
<h2><a name="jam_fundamentals">Jam Fundamentals</a></h2>
|
||||
<p>This section is derived from the official Jam documentation and from
|
||||
my experience using it and reading the Jambase rules. I repeat the
|
||||
|
||||
@@ -153,11 +153,18 @@ struct _target {
|
||||
/* runs from a Jamfile.. */
|
||||
/* */
|
||||
# define T_FLAG_FAIL_EXPECTED 0x0100 /* FAIL_EXPECTED applied */
|
||||
#ifdef OPT_SEMAPHORE
|
||||
# define T_MAKE_SEMAPHORE 5 /* Special target type for semaphores */
|
||||
#endif
|
||||
|
||||
|
||||
char binding; /* how target relates to real file */
|
||||
|
||||
# define T_BIND_UNBOUND 0 /* a disembodied name */
|
||||
# define T_BIND_MISSING 1 /* couldn't find real file */
|
||||
#ifdef OPT_SEMAPHORE
|
||||
TARGET *semaphore; /* used in serialization */
|
||||
#endif
|
||||
# define T_BIND_PARENTS 2 /* using parent's timestamp */
|
||||
# define T_BIND_EXISTS 3 /* real file, timestamp valid */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user