diff options
Diffstat (limited to 'src/lib/once.go')
-rw-r--r-- | src/lib/once.go | 81 |
1 files changed, 24 insertions, 57 deletions
diff --git a/src/lib/once.go b/src/lib/once.go index 8ebddccee..2a09a179b 100644 --- a/src/lib/once.go +++ b/src/lib/once.go @@ -4,74 +4,41 @@ // 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 +// 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 -} +import "sync" -type _Request struct { - f func(); - reply chan *_Job +type job struct { + done bool; + sync.Mutex; // should probably be sync.Notification or some such } -var service = make(chan _Request) -var jobmap = make(map[func()]*_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 = make(chan bool, 1); - job.doit <- true; - jobmap[req.f] = job - } - req.reply <- job - } -} +var jobs = make(map[func()]*job) +var joblock sync.Mutex; func Do(f func()) { - // 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] + joblock.Lock(); + j, present := jobs[f]; if !present { - c := make(chan *_Job); - service <- _Request(f, c); - job = <-c - } - - // Optimization - if job.done { - return - } - - // If we're the first one, job.doit has a true waiting. - if <-job.doit { + // run it + j = new(job); + j.Lock(); + jobs[f] = j; + joblock.Unlock(); f(); - job.done = true + j.done = true; + j.Unlock(); + } else { + // wait for it + joblock.Unlock(); + if j.done != true { + j.Lock(); + j.Unlock(); + } } - - // Leave a false waiting for the next guy. - job.doit <- false -} - -func init() { - go server() } - |