Fix race condition writing soong.config
soong_build is run twice simultaneously now, once for manifest
generation and once for docs generation. If one starts writing the
default soong.config file, and the other starts reading it, the reader
can see an empty file and fail. Write the soong.config file to a
temporary file and the atomically rename it into place.
Bug: 32628314
Test: rm out/soong/soong.config && m -j blah && cat out/soong/soong.config
Change-Id: I8119b11d45093284b24cbc926d81eb9ea4bf2e27
diff --git a/android/config.go b/android/config.go
index 3d494af..f6ca2e7 100644
--- a/android/config.go
+++ b/android/config.go
@@ -17,6 +17,7 @@
import (
"encoding/json"
"fmt"
+ "io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -127,28 +128,34 @@
return nil
}
+// atomically writes the config file in case two copies of soong_build are running simultaneously
+// (for example, docs generation and ninja manifest generation)
func saveToConfigFile(config jsonConfigurable, filename string) error {
data, err := json.MarshalIndent(&config, "", " ")
if err != nil {
return fmt.Errorf("cannot marshal config data: %s", err.Error())
}
- configFileWriter, err := os.Create(filename)
+ f, err := ioutil.TempFile(filepath.Dir(filename), "config")
if err != nil {
return fmt.Errorf("cannot create empty config file %s: %s\n", filename, err.Error())
}
- defer configFileWriter.Close()
+ defer os.Remove(f.Name())
+ defer f.Close()
- _, err = configFileWriter.Write(data)
+ _, err = f.Write(data)
if err != nil {
return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
}
- _, err = configFileWriter.WriteString("\n")
+ _, err = f.WriteString("\n")
if err != nil {
return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
}
+ f.Close()
+ os.Rename(f.Name(), filename)
+
return nil
}