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

62
mk.go
View file

@ -24,24 +24,54 @@ const maxRuleCnt = 1
// Limit the number of recipes executed simultaneously. // Limit the number of recipes executed simultaneously.
var subprocsAllowed int 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. // Wait until there is an available subprocess slot.
func reserveSubproc() { func reserveSubproc() {
subprocsAllowedCond.L.Lock() subprocsRunningCond.L.Lock()
for subprocsAllowed == 0 { for subprocsRunning >= subprocsAllowed {
subprocsAllowedCond.Wait() subprocsRunningCond.Wait()
} }
subprocsAllowed-- subprocsRunning++
subprocsAllowedCond.L.Unlock() subprocsRunningCond.L.Unlock()
} }
// Free up another subprocess to run. // Free up another subprocess to run.
func finishSubproc() { func finishSubproc() {
subprocsAllowedCond.L.Lock() subprocsRunningCond.L.Lock()
subprocsAllowed++ subprocsRunning--
subprocsAllowedCond.Signal() subprocsRunningCond.Signal()
subprocsAllowedCond.L.Unlock() 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. // Ansi color codes.
@ -161,12 +191,22 @@ func mkNode(g *graph, u *node, dryrun bool) {
// execute the recipe, unless the prereqs failed // execute the recipe, unless the prereqs failed
if !uptodate && finalstatus != nodeStatusFailed && len(e.r.recipe) > 0 { if !uptodate && finalstatus != nodeStatusFailed && len(e.r.recipe) > 0 {
if e.r.attributes.exclusive {
reserveExclusiveSubproc()
} else {
reserveSubproc() reserveSubproc()
}
if !dorecipe(u.name, u, e, dryrun) { if !dorecipe(u.name, u, e, dryrun) {
finalstatus = nodeStatusFailed finalstatus = nodeStatusFailed
} }
u.updateTimestamp() u.updateTimestamp()
if e.r.attributes.exclusive {
finishExclusiveSubproc()
} else {
finishSubproc() finishSubproc()
}
} else if finalstatus != nodeStatusFailed { } else if finalstatus != nodeStatusFailed {
finalstatus = nodeStatusNop finalstatus = nodeStatusNop
} }
@ -267,7 +307,7 @@ func main() {
// Create a dummy virtula rule that depends on every target // Create a dummy virtula rule that depends on every target
root := rule{} root := rule{}
root.targets = []pattern{pattern{false, "", nil}} 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 root.prereqs = targets
rs.add(root) rs.add(root)

View file

@ -328,7 +328,7 @@ func parseRecipe(p *parser, t token) parserStateFun {
patstr := fmt.Sprintf("^%s(.*)%s$", left, right) patstr := fmt.Sprintf("^%s(.*)%s$", left, right)
rpat, err := regexp.Compile(patstr) rpat, err := regexp.Compile(patstr)
if err != nil { 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]) p.basicErrorAtToken(msg, p.tokenbuf[k])
} }
r.targets[len(r.targets)-1].rpat = rpat r.targets[len(r.targets)-1].rpat = rpat

View file

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