--- /dev/null
+// 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()
+}
+