2
0
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:
Vladimir Prus
2002-12-02 12:28:01 +00:00
parent a8d2feea84
commit 0cd123a2db
12 changed files with 226 additions and 2 deletions

View File

@@ -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.

View File

@@ -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 ;

View File

@@ -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 );

View File

@@ -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);
}
}
}

View File

@@ -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 &quot;-j&quot; 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

View File

@@ -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 */

View File

@@ -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.

View File

@@ -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 ;

View File

@@ -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 );

View File

@@ -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);
}
}
}

View File

@@ -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 &quot;-j&quot; 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

View File

@@ -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 */