]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: make Environ return original order
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 10 Jan 2012 00:51:20 +0000 (16:51 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 10 Jan 2012 00:51:20 +0000 (16:51 -0800)
Fixes #2619

R=r, rsc
CC=golang-dev
https://golang.org/cl/5528058

src/pkg/os/env_test.go
src/pkg/syscall/env_unix.go

index 04ff390727cf9f09608b4a2ef158cd4a8049c1c8..991fa4d057802a26750d0a43b52652f9b5140ec6 100644 (file)
@@ -6,6 +6,7 @@ package os_test
 
 import (
        . "os"
+       "reflect"
        "testing"
 )
 
@@ -57,3 +58,13 @@ func TestExpand(t *testing.T) {
                }
        }
 }
+
+func TestConsistentEnviron(t *testing.T) {
+       e0 := Environ()
+       for i := 0; i < 10; i++ {
+               e1 := Environ()
+               if !reflect.DeepEqual(e0, e1) {
+                       t.Fatalf("environment changed")
+               }
+       }
+}
index 2c873cbbad3395dd3befb6d4f6ce139efc7c228d..8b1868c271af93bb0fe6d1ba7b87ab678fa62af2 100644 (file)
@@ -10,26 +10,40 @@ package syscall
 
 import "sync"
 
-var env map[string]string
-var envOnce sync.Once
-var envs []string // provided by runtime
+var (
+       // envOnce guards initialization by copyenv, which populates env.
+       envOnce sync.Once
 
+       // envLock guards env and envs.
+       envLock sync.RWMutex
+
+       // env maps from an environment variable to its first occurrence in envs.
+       env map[string]int
+
+       // envs is provided by the runtime. elements are expected to be
+       // of the form "key=value".
+       envs []string
+)
+
+// setenv_c is provided by the runtime, but is a no-op if cgo isn't
+// loaded.
 func setenv_c(k, v string)
 
 func copyenv() {
-       env = make(map[string]string)
-       for _, s := range envs {
+       env = make(map[string]int)
+       for i, s := range envs {
                for j := 0; j < len(s); j++ {
                        if s[j] == '=' {
-                               env[s[0:j]] = s[j+1:]
+                               key := s[:j]
+                               if _, ok := env[key]; !ok {
+                                       env[key] = i
+                               }
                                break
                        }
                }
        }
 }
 
-var envLock sync.RWMutex
-
 func Getenv(key string) (value string, found bool) {
        envOnce.Do(copyenv)
        if len(key) == 0 {
@@ -39,11 +53,17 @@ func Getenv(key string) (value string, found bool) {
        envLock.RLock()
        defer envLock.RUnlock()
 
-       v, ok := env[key]
+       i, ok := env[key]
        if !ok {
                return "", false
        }
-       return v, true
+       s := envs[i]
+       for i := 0; i < len(s); i++ {
+               if s[i] == '=' {
+                       return s[i+1:], true
+               }
+       }
+       return "", false
 }
 
 func Setenv(key, value string) error {
@@ -55,8 +75,16 @@ func Setenv(key, value string) error {
        envLock.Lock()
        defer envLock.Unlock()
 
-       env[key] = value
-       setenv_c(key, value) // is a no-op if cgo isn't loaded
+       i, ok := env[key]
+       kv := key + "=" + value
+       if ok {
+               envs[i] = kv
+       } else {
+               i = len(envs)
+               envs = append(envs, kv)
+       }
+       env[key] = i
+       setenv_c(key, value)
        return nil
 }
 
@@ -66,8 +94,8 @@ func Clearenv() {
        envLock.Lock()
        defer envLock.Unlock()
 
-       env = make(map[string]string)
-
+       env = make(map[string]int)
+       envs = []string{}
        // TODO(bradfitz): pass through to C
 }
 
@@ -75,11 +103,7 @@ func Environ() []string {
        envOnce.Do(copyenv)
        envLock.RLock()
        defer envLock.RUnlock()
-       a := make([]string, len(env))
-       i := 0
-       for k, v := range env {
-               a[i] = k + "=" + v
-               i++
-       }
+       a := make([]string, len(envs))
+       copy(a, envs)
        return a
 }