Add at 'X' attribute to indicate rules that are not to be run concurrently with anything

This commit is contained in:
Daniel Jones 2013-07-09 23:13:37 -07:00
parent 782a73e0d9
commit 1eb840af57
3 changed files with 57 additions and 14 deletions

66
mk.go
View file

@ -24,24 +24,54 @@ const maxRuleCnt = 1
// Limit the number of recipes executed simultaneously.
var subprocsAllowed int
var subprocsAllowedCond *sync.Cond = sync.NewCond(&sync.Mutex{})
// Current subprocesses being executed
var subprocsRunning int
// Wakeup on a free subprocess slot.
var subprocsRunningCond *sync.Cond = sync.NewCond(&sync.Mutex{})
// Prevent more than one recipe at a time from trying to take over
var exclusiveSubproc = sync.Mutex{}
// Wait until there is an available subprocess slot.
func reserveSubproc() {
subprocsAllowedCond.L.Lock()
for subprocsAllowed == 0 {
subprocsAllowedCond.Wait()
subprocsRunningCond.L.Lock()
for subprocsRunning >= subprocsAllowed {
subprocsRunningCond.Wait()
}
subprocsAllowed--
subprocsAllowedCond.L.Unlock()
subprocsRunning++
subprocsRunningCond.L.Unlock()
}
// Free up another subprocess to run.
func finishSubproc() {
subprocsAllowedCond.L.Lock()
subprocsAllowed++
subprocsAllowedCond.Signal()
subprocsAllowedCond.L.Unlock()
subprocsRunningCond.L.Lock()
subprocsRunning--
subprocsRunningCond.Signal()
subprocsRunningCond.L.Unlock()
}
// Make everyone wait while we
func reserveExclusiveSubproc() {
exclusiveSubproc.Lock()
// Wait until everything is done running
stolen_subprocs := 0
subprocsRunningCond.L.Lock()
stolen_subprocs = subprocsAllowed - subprocsRunning
subprocsRunning = subprocsAllowed
for stolen_subprocs < subprocsAllowed {
subprocsRunningCond.Wait()
stolen_subprocs += subprocsAllowed - subprocsRunning
subprocsRunning = subprocsAllowed
}
}
func finishExclusiveSubproc() {
subprocsRunning = 0
subprocsRunningCond.Broadcast()
subprocsRunningCond.L.Unlock()
exclusiveSubproc.Unlock()
}
// Ansi color codes.
@ -161,12 +191,22 @@ func mkNode(g *graph, u *node, dryrun bool) {
// execute the recipe, unless the prereqs failed
if !uptodate && finalstatus != nodeStatusFailed && len(e.r.recipe) > 0 {
reserveSubproc()
if e.r.attributes.exclusive {
reserveExclusiveSubproc()
} else {
reserveSubproc()
}
if !dorecipe(u.name, u, e, dryrun) {
finalstatus = nodeStatusFailed
}
u.updateTimestamp()
finishSubproc()
if e.r.attributes.exclusive {
finishExclusiveSubproc()
} else {
finishSubproc()
}
} else if finalstatus != nodeStatusFailed {
finalstatus = nodeStatusNop
}
@ -267,7 +307,7 @@ func main() {
// Create a dummy virtula rule that depends on every target
root := rule{}
root.targets = []pattern{pattern{false, "", nil}}
root.attributes = attribSet{false, false, false, false, false, false, false, true}
root.attributes = attribSet{false, false, false, false, false, false, false, true, false}
root.prereqs = targets
rs.add(root)

View file

@ -328,7 +328,7 @@ func parseRecipe(p *parser, t token) parserStateFun {
patstr := fmt.Sprintf("^%s(.*)%s$", left, right)
rpat, err := regexp.Compile(patstr)
if err != nil {
msg := fmt.Sprintf("error compiling suffix rule. This is a bug.", err)
msg := fmt.Sprintf("error compiling suffix rule. This is a bug. Error: %s", err)
p.basicErrorAtToken(msg, p.tokenbuf[k])
}
r.targets[len(r.targets)-1].rpat = rpat

View file

@ -20,6 +20,7 @@ type attribSet struct {
regex bool // regular expression meta-rule
update bool // treat the targets as if they were updated
virtual bool // rule is virtual (does not match files)
exclusive bool // don't execute concurrently with any other rule
}
// Error parsing an attribute
@ -113,6 +114,8 @@ func (r *rule) parseAttribs(inputs []string) *attribError {
r.attributes.update = true
case 'V':
r.attributes.virtual = true
case 'X':
r.attributes.exclusive = true
case 'P':
if pos+w < len(input) {
r.command = append(r.command, input[pos+w:])