1
0
mirror of https://github.com/golang/go synced 2024-09-24 19:30:12 -06:00

assorted changes:

- use a lock instead of a thread in once
		avoids deadlock in recursive once calls
	- implement os.Setenv
	- remove "export" from some scripts
	- remove _ from names in time package
	- fix time test for non-MTV machines

R=r
DELTA=265  (87 added, 58 deleted, 120 changed)
OCL=25057
CL=25057
This commit is contained in:
Russ Cox 2009-02-15 22:12:35 -08:00
parent 55d13cf139
commit ff3173849e
10 changed files with 193 additions and 164 deletions

View File

@ -94,11 +94,12 @@ test: test.files
bignum.6: fmt.dirinstall
bufio.6: io.dirinstall os.dirinstall
exec.6: os.dirinstall
flag.6: fmt.dirinstall
log.6: fmt.dirinstall io.dirinstall os.dirinstall time.dirinstall
testing.6: flag.install fmt.dirinstall
once.6: sync.dirinstall
strings.6: utf8.install
exec.6: os.dirinstall
testing.6: flag.install fmt.dirinstall
fmt.dirinstall: io.dirinstall reflect.dirinstall strconv.dirinstall
hash.dirinstall: os.dirinstall
@ -108,7 +109,7 @@ json.dirinstall: container/array.dirinstall fmt.dirinstall io.dirinstall math.di
strconv.dirinstall strings.install utf8.install
# TODO(rsc): net is not supposed to depend on fmt or strings or strconv
net.dirinstall: fmt.dirinstall once.install os.dirinstall strconv.dirinstall strings.install
os.dirinstall: syscall.dirinstall
os.dirinstall: syscall.dirinstall once.install
regexp.dirinstall: os.dirinstall
reflect.dirinstall: strconv.dirinstall sync.dirinstall
strconv.dirinstall: math.dirinstall os.dirinstall utf8.install

View File

@ -4,74 +4,41 @@
// 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
// 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 {
import "sync"
type job struct {
done bool;
doit chan bool; // buffer of 1
sync.Mutex; // should probably be sync.Notification or some such
}
type _Request struct {
f func();
reply chan *_Job
}
var service = make(chan _Request)
var jobmap = make(map[func()]*_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
}
}
var jobs = make(map[func()]*job)
var joblock sync.Mutex;
func Do(f func()) {
// 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]
joblock.Lock();
j, present := jobs[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 {
// run it
j = new(job);
j.Lock();
jobs[f] = j;
joblock.Unlock();
f();
job.done = true
j.done = true;
j.Unlock();
} else {
// wait for it
joblock.Unlock();
if j.done != true {
j.Lock();
j.Unlock();
}
}
// Leave a false waiting for the next guy.
job.doit <- false
}
func init() {
go server()
}

View File

@ -3,25 +3,71 @@
// license that can be found in the LICENSE file.
// Environment variables.
// Setenv doesn't exist yet: don't have the run-time hooks yet
package os
import os "os"
import (
"once";
"os";
)
var (
ENOENV = NewError("no such environment variable");
env map[string] string;
)
func Getenv(s string) (v string, err *Error) {
n := len(s);
if n == 0 {
return "", EINVAL
}
for i, e := range sys.Envs {
if len(e) > n && e[n] == '=' && e[0:n] == s {
return e[n+1:len(e)], nil
func copyenv() {
env = make(map[string] string);
for i, s := range sys.Envs {
for j := 0; j < len(s); j++ {
if s[j] == '=' {
env[s[0:j]] = s[j+1:len(s)];
break;
}
}
}
return "", ENOENV
}
func Getenv(key string) (value string, err *Error) {
once.Do(copyenv);
if len(key) == 0 {
return "", EINVAL;
}
v, ok := env[key];
if !ok {
return "", ENOENV;
}
return v, nil;
}
func Setenv(key, value string) *Error {
once.Do(copyenv);
if len(key) == 0 {
return EINVAL;
}
env[key] = value;
return nil;
}
func Clearenv() {
once.Do(copyenv); // prevent copyenv in Getenv/Setenv
env = make(map[string] string);
}
func Environ() []string {
once.Do(copyenv);
a := make([]string, len(env));
i := 0;
for k, v := range(env) {
// check i < len(a) for safety,
// in case env is changing underfoot.
if i < len(a) {
a[i] = k + "=" + v;
i++;
}
}
return a[0:i];
}

View File

@ -27,6 +27,9 @@ func ForkExec(argv0 string, argv []string, envv []string, fd []*FD)
}
func Exec(argv0 string, argv []string, envv []string) *Error {
if envv == nil {
envv = Environ();
}
e := syscall.Exec(argv0, argv, envv);
return ErrnoToError(e);
}

View File

@ -10,7 +10,7 @@ print <<EOF;
package syscall
export const (
const (
EOF
while(<>){

View File

@ -11,7 +11,7 @@ print <<EOF;
package syscall
export const(
const(
EOF
while(<>){
@ -25,4 +25,7 @@ while(<>){
print <<EOF;
)
func _darwin_system_call_conflict() {
}
EOF

View File

@ -10,7 +10,7 @@ print <<EOF;
package syscall
export const(
const(
EOF
while(<>){

View File

@ -138,7 +138,7 @@ func SecondsToUTC(sec int64) *Time {
}
t.Month = m+1;
t.Day = yday+1;
t.Zone = "GMT";
t.Zone = "UTC";
return t;
}
@ -149,12 +149,12 @@ func UTC() *Time {
// TODO: Should this return an error?
func SecondsToLocalTime(sec int64) *Time {
zone, offset, err := time.LookupTimezone(sec);
z, offset, err := time.LookupTimezone(sec);
if err != nil {
return SecondsToUTC(sec)
}
t := SecondsToUTC(sec+int64(offset));
t.Zone = zone;
t.Zone = z;
t.ZoneOffset = offset;
return t
}

View File

@ -5,31 +5,39 @@
package time
import (
"os";
"testing";
"time";
)
type _TimeTest struct {
func init() {
// Force US Pacific time for daylight-savings
// tests below (localtests). Needs to be set
// before the first call into the time library.
os.Setenv("TZ", "US/Pacific");
}
type TimeTest struct {
seconds int64;
golden Time;
}
var utctests = []_TimeTest (
_TimeTest(0, Time(1970, 1, 1, 0, 0, 0, Thursday, 0, "GMT")),
_TimeTest(1221681866, Time(2008, 9, 17, 20, 4, 26, Wednesday, 0, "GMT")),
_TimeTest(-1221681866, Time(1931, 4, 16, 3, 55, 34, Thursday, 0, "GMT")),
_TimeTest(1e18, Time(31688740476, 10, 23, 1, 46, 40, Friday, 0, "GMT")),
_TimeTest(-1e18, Time(-31688736537, 3, 10, 22, 13, 20, Tuesday, 0, "GMT")),
_TimeTest(0x7fffffffffffffff, Time(292277026596, 12, 4, 15, 30, 7, Sunday, 0, "GMT")),
_TimeTest(-0x8000000000000000, Time(-292277022657, 1, 27, 8, 29, 52, Sunday, 0, "GMT"))
var utctests = []TimeTest (
TimeTest(0, Time(1970, 1, 1, 0, 0, 0, Thursday, 0, "UTC")),
TimeTest(1221681866, Time(2008, 9, 17, 20, 4, 26, Wednesday, 0, "UTC")),
TimeTest(-1221681866, Time(1931, 4, 16, 3, 55, 34, Thursday, 0, "UTC")),
TimeTest(1e18, Time(31688740476, 10, 23, 1, 46, 40, Friday, 0, "UTC")),
TimeTest(-1e18, Time(-31688736537, 3, 10, 22, 13, 20, Tuesday, 0, "UTC")),
TimeTest(0x7fffffffffffffff, Time(292277026596, 12, 4, 15, 30, 7, Sunday, 0, "UTC")),
TimeTest(-0x8000000000000000, Time(-292277022657, 1, 27, 8, 29, 52, Sunday, 0, "UTC"))
)
var localtests = []_TimeTest (
_TimeTest(0, Time(1969, 12, 31, 16, 0, 0, Wednesday, -8*60*60, "PST")),
_TimeTest(1221681866, Time(2008, 9, 17, 13, 4, 26, Wednesday, -7*60*60, "PDT"))
var localtests = []TimeTest (
TimeTest(0, Time(1969, 12, 31, 16, 0, 0, Wednesday, -8*60*60, "PST")),
TimeTest(1221681866, Time(2008, 9, 17, 13, 4, 26, Wednesday, -7*60*60, "PDT"))
)
func _Same(t, u *Time) bool {
func same(t, u *Time) bool {
return t.Year == u.Year
&& t.Month == u.Month
&& t.Day == u.Day
@ -50,7 +58,7 @@ func TestSecondsToUTC(t *testing.T) {
if newsec != sec {
t.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec);
}
if !_Same(tm, golden) {
if !same(tm, golden) {
t.Errorf("SecondsToUTC(%d):", sec);
t.Errorf(" want=%v", *golden);
t.Errorf(" have=%v", *tm);
@ -67,7 +75,7 @@ func TestSecondsToLocalTime(t *testing.T) {
if newsec != sec {
t.Errorf("SecondsToLocalTime(%d).Seconds() = %d", sec, newsec);
}
if !_Same(tm, golden) {
if !same(tm, golden) {
t.Errorf("SecondsToLocalTime(%d):", sec);
t.Errorf(" want=%v", *golden);
t.Errorf(" have=%v", *tm);

View File

@ -16,8 +16,10 @@ import (
)
const (
_MaxFileSize = 8192; // actual files are closer to 1K
_HeaderSize = 4+16+4*7
maxFileSize = 8192; // actual files are closer to 1K
headerSize = 4+16+4*7;
zoneDir = "/usr/share/zoneinfo/";
)
var (
@ -26,13 +28,13 @@ var (
)
// Simple I/O interface to binary blob of data.
type _Data struct {
type data struct {
p []byte;
error bool;
}
func (d *_Data) Read(n int) []byte {
func (d *data) read(n int) []byte {
if len(d.p) < n {
d.p = nil;
d.error = true;
@ -43,8 +45,8 @@ func (d *_Data) Read(n int) []byte {
return p
}
func (d *_Data) Big4() (n uint32, ok bool) {
p := d.Read(4);
func (d *data) big4() (n uint32, ok bool) {
p := d.read(4);
if len(p) < 4 {
d.error = true;
return 0, false
@ -52,8 +54,8 @@ func (d *_Data) Big4() (n uint32, ok bool) {
return uint32(p[0]) << 24 | uint32(p[1]) << 16 | uint32(p[2]) << 8 | uint32(p[3]), true
}
func (d *_Data) Byte() (n byte, ok bool) {
p := d.Read(1);
func (d *data) byte() (n byte, ok bool) {
p := d.read(1);
if len(p) < 1 {
d.error = true;
return 0, false
@ -63,7 +65,7 @@ func (d *_Data) Byte() (n byte, ok bool) {
// Make a string by stopping at the first NUL
func _ByteString(p []byte) string {
func byteString(p []byte) string {
for i := 0; i < len(p); i++ {
if p[i] == 0 {
return string(p[0:i])
@ -73,31 +75,29 @@ func _ByteString(p []byte) string {
}
// Parsed representation
type _Zone struct {
type zone struct {
utcoff int;
isdst bool;
name string;
}
type _Zonetime struct {
type zonetime struct {
time int32; // transition time, in seconds since 1970 GMT
zone *_Zone; // the zone that goes into effect at that time
zone *zone; // the zone that goes into effect at that time
isstd, isutc bool; // ignored - no idea what these mean
}
func parseinfo(bytes []byte) (zt []_Zonetime, err *os.Error) {
data1 := _Data(bytes, false);
data := &data1;
func parseinfo(bytes []byte) (zt []zonetime, err *os.Error) {
d := data(bytes, false);
// 4-byte magic "TZif"
if magic := data.Read(4); string(magic) != "TZif" {
if magic := d.read(4); string(magic) != "TZif" {
return nil, BadZoneinfo
}
// 1-byte version, then 15 bytes of padding
var p []byte;
if p = data.Read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
return nil, BadZoneinfo
}
vers := p[0];
@ -119,7 +119,7 @@ func parseinfo(bytes []byte) (zt []_Zonetime, err *os.Error) {
)
var n [6]int;
for i := 0; i < 6; i++ {
nn, ok := data.Big4();
nn, ok := d.big4();
if !ok {
return nil, BadZoneinfo
}
@ -127,32 +127,29 @@ func parseinfo(bytes []byte) (zt []_Zonetime, err *os.Error) {
}
// Transition times.
txtimes1 := _Data(data.Read(n[NTime]*4), false);
txtimes := &txtimes1;
txtimes := data(d.read(n[NTime]*4), false);
// Time zone indices for transition times.
txzones := data.Read(n[NTime]);
txzones := d.read(n[NTime]);
// Zone info structures
zonedata1 := _Data(data.Read(n[NZone]*6), false);
zonedata := &zonedata1;
zonedata := data(d.read(n[NZone]*6), false);
// Time zone abbreviations.
abbrev := data.Read(n[NChar]);
abbrev := d.read(n[NChar]);
// Leap-second time pairs
leapdata1 := _Data(data.Read(n[NLeap]*8), false);
leapdata := &leapdata1;
leapdata := data(d.read(n[NLeap]*8), false);
// Whether tx times associated with local time types
// are specified as standard time or wall time.
isstd := data.Read(n[NStdWall]);
isstd := d.read(n[NStdWall]);
// Whether tx times associated with local time types
// are specified as UTC or local time.
isutc := data.Read(n[NUTCLocal]);
isutc := d.read(n[NUTCLocal]);
if data.error { // ran out of data
if d.error { // ran out of data
return nil, BadZoneinfo
}
@ -163,38 +160,38 @@ func parseinfo(bytes []byte) (zt []_Zonetime, err *os.Error) {
// Now we can build up a useful data structure.
// First the zone information.
// utcoff[4] isdst[1] nameindex[1]
zone := make([]_Zone, n[NZone]);
for i := 0; i < len(zone); i++ {
z := make([]zone, n[NZone]);
for i := 0; i < len(z); i++ {
var ok bool;
var n uint32;
if n, ok = zonedata.Big4(); !ok {
if n, ok = zonedata.big4(); !ok {
return nil, BadZoneinfo
}
zone[i].utcoff = int(n);
z[i].utcoff = int(n);
var b byte;
if b, ok = zonedata.Byte(); !ok {
if b, ok = zonedata.byte(); !ok {
return nil, BadZoneinfo
}
zone[i].isdst = b != 0;
if b, ok = zonedata.Byte(); !ok || int(b) >= len(abbrev) {
z[i].isdst = b != 0;
if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
return nil, BadZoneinfo
}
zone[i].name = _ByteString(abbrev[b:len(abbrev)])
z[i].name = byteString(abbrev[b:len(abbrev)])
}
// Now the transition time info.
zt = make([]_Zonetime, n[NTime]);
zt = make([]zonetime, n[NTime]);
for i := 0; i < len(zt); i++ {
var ok bool;
var n uint32;
if n, ok = txtimes.Big4(); !ok {
if n, ok = txtimes.big4(); !ok {
return nil, BadZoneinfo
}
zt[i].time = int32(n);
if int(txzones[i]) >= len(zone) {
if int(txzones[i]) >= len(z) {
return nil, BadZoneinfo
}
zt[i].zone = &zone[txzones[i]];
zt[i].zone = &z[txzones[i]];
if i < len(isstd) {
zt[i].isstd = isstd[i] != 0
}
@ -208,52 +205,56 @@ func parseinfo(bytes []byte) (zt []_Zonetime, err *os.Error) {
func readfile(name string, max int) (p []byte, err *os.Error) {
fd, e := os.Open(name, os.O_RDONLY, 0);
if e != nil {
return nil, e
}
p = make([]byte, max+1)[0:0];
n := 0;
for len(p) < max {
nn, e := fd.Read(p[n:cap(p)]);
if e != nil {
fd.Close();
return nil, e
}
if nn == 0 {
fd.Close();
return p, nil
}
p = p[0:n+nn]
return nil, e;
}
p = make([]byte, max);
n, err1 := io.Readn(fd, p);
fd.Close();
return nil, BadZoneinfo // too long
if err1 == nil { // too long
return nil, BadZoneinfo;
}
if err1 != io.ErrEOF {
return nil, err1;
}
return p[0:n], nil;
}
func readinfofile(name string) (tx []_Zonetime, err *os.Error) {
data, e := readfile(name, _MaxFileSize);
func readinfofile(name string) (tx []zonetime, err *os.Error) {
buf, e := readfile(name, maxFileSize);
if e != nil {
return nil, e
}
tx, err = parseinfo(data);
tx, err = parseinfo(buf);
return tx, err
}
var zones []_Zonetime
var zones []zonetime
var zoneerr *os.Error
func _SetupZone() {
// TODO: /etc/localtime is the default time zone info
// for the system, but libc allows setting an environment
// variable in order to direct reading a different file
// (in /usr/share/zoneinfo). We should check that
// environment variable.
zones, zoneerr = readinfofile("/etc/localtime");
func setupZone() {
// consult $TZ to find the time zone to use.
// no $TZ means use the system default /etc/localtime.
// $TZ="" means use UTC.
// $TZ="foo" means use /usr/share/zoneinfo/foo.
tz, err := os.Getenv("TZ");
var file string;
switch {
case err == os.ENOENV:
zones, zoneerr = readinfofile("/etc/localtime");
case err != nil:
zoneerr = err;
case len(tz) > 0:
zones, zoneerr = readinfofile(zoneDir + tz);
case len(tz) == 0:
// do nothing: use UTC
}
}
func LookupTimezone(sec int64) (zone string, offset int, err *os.Error) {
once.Do(_SetupZone);
once.Do(setupZone);
if zoneerr != nil || len(zones) == 0 {
return "GMT", 0, zoneerr
return "UTC", 0, zoneerr
}
// Binary search for entry with largest time <= sec