aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2008-09-22 16:26:57 -0700
committerRuss Cox <rsc@golang.org>2008-09-22 16:26:57 -0700
commit3c17ee69d969d04db6ce1593770ac2e9a5087620 (patch)
tree5befa4000bea77cffbe579b36b5319bd0b41b426 /src/lib
parent173ca8a2d088992f5bde75f04af9358b25dbadd9 (diff)
downloadgo-3c17ee69d969d04db6ce1593770ac2e9a5087620.tar.xz
add "once" package
R=r DELTA=79 (79 added, 0 deleted, 0 changed) OCL=15656 CL=15656
Diffstat (limited to 'src/lib')
-rwxr-xr-xsrc/lib/make.bash1
-rw-r--r--src/lib/once.go79
2 files changed, 80 insertions, 0 deletions
diff --git a/src/lib/make.bash b/src/lib/make.bash
index 6277c93f83..24f8c0e62d 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 0000000000..c8433e8619
--- /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()
+}
+