2008-09-22 17:26:57 -06:00
|
|
|
// 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;
|
2008-10-07 13:31:31 -06:00
|
|
|
job, present := jobmap[req.f];
|
2008-09-22 17:26:57 -06:00
|
|
|
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.
|
2008-10-07 13:31:31 -06:00
|
|
|
var job *Job;
|
|
|
|
var present bool;
|
2008-09-22 17:26:57 -06:00
|
|
|
// 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()
|
|
|
|
}
|
|
|
|
|