diff --git a/src/lib/make.bash b/src/lib/make.bash index 6277c93f832..24f8c0e62d0 100755 --- a/src/lib/make.bash +++ b/src/lib/make.bash @@ -25,6 +25,7 @@ for i in \ io.go\ bufio.go\ strings.go\ + once.go\ do base=$(basename $i .go) diff --git a/src/lib/once.go b/src/lib/once.go new file mode 100644 index 00000000000..c8433e8619d --- /dev/null +++ b/src/lib/once.go @@ -0,0 +1,79 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// For one-time initialization that is not done during init. +// Wrap the initialization in a niladic function f() and call +// once.Do(&f) +// If multiple processes call once.Do(&f) simultaneously +// with the same f argument, only one will call f, and the +// others will block until f finishes running. + +package once + +type Job struct { + done bool; + doit *chan bool; // buffer of 1 +} + +type Request struct { + f *(); + reply *chan *Job +} + +// TODO: Would like to use chan Request but 6g rejects it. +var service = new(chan *Request) +var jobmap = new(map[*()]*Job) + +// Moderate access to the jobmap. +// Even if accesses were thread-safe (they should be but are not) +// something needs to serialize creation of new jobs. +// That's what the Server does. +func Server() { + for { + req := <-service; + job, present := jobmap[req.f] + if !present { + job = new(Job); + job.doit = new(chan bool, 1); + job.doit <- true; + jobmap[req.f] = job + } + req.reply <- job + } +} + +export func Do(f *()) { + // Look for job in map (avoids channel communication). + // If not there, ask map server to make one. + // TODO: Uncomment use of jobmap[f] once + // maps are thread-safe. + var job *Job + var present bool + // job, present = jobmap[f] + if !present { + c := new(chan *Job); + req := Request{f, c}; + service <- &req; + job = <-c + } + + // Optimization + if job.done { + return + } + + // If we're the first one, job.doit has a true waiting. + if <-job.doit { + f(); + job.done = true + } + + // Leave a false waiting for the next guy. + job.doit <- false +} + +func init() { + go Server() +} +