]> Cypherpunks repositories - gostls13.git/commitdiff
add "once" package
authorRuss Cox <rsc@golang.org>
Mon, 22 Sep 2008 23:26:57 +0000 (16:26 -0700)
committerRuss Cox <rsc@golang.org>
Mon, 22 Sep 2008 23:26:57 +0000 (16:26 -0700)
R=r
DELTA=79  (79 added, 0 deleted, 0 changed)
OCL=15656
CL=15656

src/lib/make.bash
src/lib/once.go [new file with mode: 0644]

index 6277c93f832859a6c882aeae3924c559e7dc249e..24f8c0e62d0de404d59bf04f4e48bdf6f0acc8c5 100755 (executable)
@@ -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 (file)
index 0000000..c8433e8
--- /dev/null
@@ -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()
+}
+