diff --git a/historic/jam/src/Jamfile b/historic/jam/src/Jamfile index 1197ae73a..f9a680d09 100644 --- a/historic/jam/src/Jamfile +++ b/historic/jam/src/Jamfile @@ -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. diff --git a/historic/jam/src/build.jam b/historic/jam/src/build.jam index cd6e08b0e..42b057ab9 100644 --- a/historic/jam/src/build.jam +++ b/historic/jam/src/build.jam @@ -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 ; diff --git a/historic/jam/src/make.c b/historic/jam/src/make.c index 63b36d5fa..94a740167 100644 --- a/historic/jam/src/make.c +++ b/historic/jam/src/make.c @@ -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 ); diff --git a/historic/jam/src/make1.c b/historic/jam/src/make1.c index 02ce6f757..4c1a5e5f0 100644 --- a/historic/jam/src/make1.c +++ b/historic/jam/src/make1.c @@ -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); + } } } diff --git a/historic/jam/src/readme.html b/historic/jam/src/readme.html index 2c6937fa5..5649f4cb8 100644 --- a/historic/jam/src/readme.html +++ b/historic/jam/src/readme.html @@ -76,6 +76,7 @@
The UPDATE rule and changes to command line handling
+
Semaphores
Jam Fundamentals
@@ -760,6 +761,29 @@ rule UPDATE ( targets * )

The rule causes the specified targets to be updated. If no target was specified with the UPDATE rule, the "all" target will be implicitly updated.

+ +

Semaphores

+ +

It is sometimes desirable to disallow parallel execution of + some actions. For example: +

+ Craig McPeeters has extended Perforce Jam to solve such problems, and that + extension was integrated in Boost.Jam. + +

Any target can be assigned a semaphore, by setting a variable + called SEMAPHORE 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. + +

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

Jam Fundamentals

This section is derived from the official Jam documentation and from my experience using it and reading the Jambase rules. I repeat the diff --git a/historic/jam/src/rules.h b/historic/jam/src/rules.h index 3b13eb4cf..0e954f801 100644 --- a/historic/jam/src/rules.h +++ b/historic/jam/src/rules.h @@ -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 */ diff --git a/jam_src/Jamfile b/jam_src/Jamfile index 1197ae73a..f9a680d09 100644 --- a/jam_src/Jamfile +++ b/jam_src/Jamfile @@ -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. diff --git a/jam_src/build.jam b/jam_src/build.jam index cd6e08b0e..42b057ab9 100644 --- a/jam_src/build.jam +++ b/jam_src/build.jam @@ -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 ; diff --git a/jam_src/make.c b/jam_src/make.c index 63b36d5fa..94a740167 100644 --- a/jam_src/make.c +++ b/jam_src/make.c @@ -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 ); diff --git a/jam_src/make1.c b/jam_src/make1.c index 02ce6f757..4c1a5e5f0 100644 --- a/jam_src/make1.c +++ b/jam_src/make1.c @@ -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); + } } } diff --git a/jam_src/readme.html b/jam_src/readme.html index 2c6937fa5..5649f4cb8 100644 --- a/jam_src/readme.html +++ b/jam_src/readme.html @@ -76,6 +76,7 @@

The UPDATE rule and changes to command line handling
+
Semaphores
Jam Fundamentals
@@ -760,6 +761,29 @@ rule UPDATE ( targets * )

The rule causes the specified targets to be updated. If no target was specified with the UPDATE rule, the "all" target will be implicitly updated.

+ +

Semaphores

+ +

It is sometimes desirable to disallow parallel execution of + some actions. For example: +

+ Craig McPeeters has extended Perforce Jam to solve such problems, and that + extension was integrated in Boost.Jam. + +

Any target can be assigned a semaphore, by setting a variable + called SEMAPHORE 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. + +

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

Jam Fundamentals

This section is derived from the official Jam documentation and from my experience using it and reading the Jambase rules. I repeat the diff --git a/jam_src/rules.h b/jam_src/rules.h index 3b13eb4cf..0e954f801 100644 --- a/jam_src/rules.h +++ b/jam_src/rules.h @@ -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 */