blob: 6019df5154e94404f985a847e82cfeb381aeb80e [file] [log] [blame]
// 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
}
var service = make(chan _Request)
var jobmap = make(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 = make(chan bool, 1);
job.doit <- true;
jobmap[req.f] = job
}
req.reply <- job
}
}
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 := 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 {
f();
job.done = true
}
// Leave a false waiting for the next guy.
job.doit <- false
}
func init() {
go server()
}