diff options
author | Jeff Gaston <jeffrygaston@google.com> | 2017-05-18 00:34:48 +0000 |
---|---|---|
committer | Jeff Gaston <jeffrygaston@google.com> | 2017-05-18 00:34:48 +0000 |
commit | be9cd1192bf8774f529a1ca6d2b8ee3876a80d02 (patch) | |
tree | 8e0c7a72c324dd227c293350be47bd68e39f0f15 /ui | |
parent | 7db68be5e14c6ddc931500a484a9fb7b66d034ef (diff) | |
download | build_soong-be9cd1192bf8774f529a1ca6d2b8ee3876a80d02.tar.gz build_soong-be9cd1192bf8774f529a1ca6d2b8ee3876a80d02.tar.bz2 build_soong-be9cd1192bf8774f529a1ca6d2b8ee3876a80d02.zip |
Revert "Disallow multiple build executions in parallel"
This reverts commit 7db68be5e14c6ddc931500a484a9fb7b66d034ef.
Reason for revert: postsubmit build encountered a failure
Change-Id: I3c7304114f712d78f79afb1b9d84dc2eadcbc876
Diffstat (limited to 'ui')
-rw-r--r-- | ui/build/Android.bp | 2 | ||||
-rw-r--r-- | ui/build/build.go | 4 | ||||
-rw-r--r-- | ui/build/proc_sync.go | 143 | ||||
-rw-r--r-- | ui/build/proc_sync_test.go | 241 |
4 files changed, 0 insertions, 390 deletions
diff --git a/ui/build/Android.bp b/ui/build/Android.bp index 9186ed96..7a83684c 100644 --- a/ui/build/Android.bp +++ b/ui/build/Android.bp @@ -29,7 +29,6 @@ bootstrap_go_package { "kati.go", "make.go", "ninja.go", - "proc_sync.go", "signal.go", "soong.go", "util.go", @@ -37,7 +36,6 @@ bootstrap_go_package { testSrcs: [ "environment_test.go", "util_test.go", - "proc_sync_test.go", ], darwin: { srcs: [ diff --git a/ui/build/build.go b/ui/build/build.go index 69dabf5d..598e342d 100644 --- a/ui/build/build.go +++ b/ui/build/build.go @@ -178,10 +178,6 @@ func Build(ctx Context, config Config, what int) { // Start getting java version as early as possible getJavaVersions(ctx, config) - // Make sure that no other Soong process is running with the same output directory - buildLock := BecomeSingletonOrFail(ctx, config) - defer buildLock.Unlock() - SetupOutDir(ctx, config) checkCaseSensitivity(ctx, config) diff --git a/ui/build/proc_sync.go b/ui/build/proc_sync.go deleted file mode 100644 index 857786da..00000000 --- a/ui/build/proc_sync.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package build - -import ( - "errors" - "fmt" - "math" - "os" - "path/filepath" - "syscall" - "time" - - "android/soong/ui/logger" -) - -// This file provides cross-process synchronization methods -// i.e. making sure only one Soong process is running for a given output directory - -func BecomeSingletonOrFail(ctx Context, config Config) (lock *fileLock) { - lockingInfo, err := newLock(config.OutDir()) - if err != nil { - ctx.Logger.Fatal(err) - } - err = lockSynchronous(*lockingInfo, newSleepWaiter(lockfilePollDuration, lockfileTimeout), ctx.Logger) - if err != nil { - ctx.Logger.Fatal(err) - } - return lockingInfo -} - -var lockfileTimeout = time.Second * 10 -var lockfilePollDuration = time.Second - -type lockable interface { - tryLock() error - Unlock() error - description() string -} - -var _ lockable = (*fileLock)(nil) - -type fileLock struct { - File *os.File -} - -func (l fileLock) description() (path string) { - return l.File.Name() -} -func (l fileLock) tryLock() (err error) { - return syscall.Flock(int(l.File.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) -} -func (l fileLock) Unlock() (err error) { - return l.File.Close() -} - -func lockSynchronous(lock lockable, waiter waiter, logger logger.Logger) (err error) { - - waited := false - - for { - err = lock.tryLock() - if err == nil { - if waited { - // If we had to wait at all, then when the wait is done, we inform the user - logger.Printf("Acquired lock on %v; previous Soong process must have completed. Continuing...\n", lock.description()) - } - return nil - } - - waited = true - - done, description := waiter.checkDeadline() - - if done { - return fmt.Errorf("Tried to lock %s, but timed out %s . Make sure no other Soong process is using it", - lock.description(), waiter.summarize()) - } else { - logger.Printf("Waiting up to %s to lock %v to ensure no other Soong process is running in the same output directory\n", description, lock.description()) - waiter.wait() - } - } -} - -func newLock(basedir string) (lock *fileLock, err error) { - lockPath := filepath.Join(basedir, ".lock") - - os.MkdirAll(basedir, 0777) - lockfileDescriptor, err := os.OpenFile(lockPath, os.O_RDWR|os.O_CREATE, 0666) - if err != nil { - return nil, errors.New("failed to open " + lockPath) - } - lockingInfo := &fileLock{File: lockfileDescriptor} - - return lockingInfo, nil -} - -type waiter interface { - wait() - checkDeadline() (done bool, remainder string) - summarize() (summary string) -} - -type sleepWaiter struct { - sleepInterval time.Duration - deadline time.Time - - totalWait time.Duration -} - -var _ waiter = (*sleepWaiter)(nil) - -func newSleepWaiter(interval time.Duration, duration time.Duration) (waiter *sleepWaiter) { - return &sleepWaiter{interval, time.Now().Add(duration), duration} -} - -func (s sleepWaiter) wait() { - time.Sleep(s.sleepInterval) -} -func (s *sleepWaiter) checkDeadline() (done bool, remainder string) { - remainingSleep := s.deadline.Sub(time.Now()) - numSecondsRounded := math.Floor(remainingSleep.Seconds()*10+0.5) / 10 - if remainingSleep > 0 { - return false, fmt.Sprintf("%vs", numSecondsRounded) - } else { - return true, "" - } -} -func (s sleepWaiter) summarize() (summary string) { - return fmt.Sprintf("polling every %v until %v", s.sleepInterval, s.totalWait) -} diff --git a/ui/build/proc_sync_test.go b/ui/build/proc_sync_test.go deleted file mode 100644 index 857bea38..00000000 --- a/ui/build/proc_sync_test.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package build - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "syscall" - "testing" - - "android/soong/ui/logger" -) - -// some util methods and data structures that aren't directly part of a test -func makeLockDir() (path string, err error) { - return ioutil.TempDir("", "soong_lock_test") -} -func lockOrFail(t *testing.T) (lock fileLock) { - lockDir, err := makeLockDir() - var lockPointer *fileLock - if err == nil { - lockPointer, err = newLock(lockDir) - } - if err != nil { - os.RemoveAll(lockDir) - t.Fatalf("Failed to create lock: %v", err) - } - - return *lockPointer -} -func removeTestLock(fileLock fileLock) { - lockdir := filepath.Dir(fileLock.File.Name()) - os.RemoveAll(lockdir) -} - -// countWaiter only exists for the purposes of testing lockSynchronous -type countWaiter struct { - numWaitsElapsed int - maxNumWaits int -} - -func newCountWaiter(count int) (waiter *countWaiter) { - return &countWaiter{0, count} -} - -func (c *countWaiter) wait() { - c.numWaitsElapsed++ -} -func (c *countWaiter) checkDeadline() (done bool, remainder string) { - numWaitsRemaining := c.maxNumWaits - c.numWaitsElapsed - if numWaitsRemaining < 1 { - return true, "" - } - return false, fmt.Sprintf("%v waits remain", numWaitsRemaining) -} -func (c countWaiter) summarize() (summary string) { - return fmt.Sprintf("waiting %v times", c.maxNumWaits) -} - -// countLock only exists for the purposes of testing lockSynchronous -type countLock struct { - nextIndex int - successIndex int -} - -var _ lockable = (*countLock)(nil) - -// returns a countLock that succeeds on iteration <index> -func testLockCountingTo(index int) (lock *countLock) { - return &countLock{nextIndex: 0, successIndex: index} -} -func (c *countLock) description() (message string) { - return fmt.Sprintf("counter that counts from %v to %v", c.nextIndex, c.successIndex) -} -func (c *countLock) tryLock() (err error) { - currentIndex := c.nextIndex - c.nextIndex++ - if currentIndex == c.successIndex { - return nil - } - return fmt.Errorf("Lock busy: %s", c.description()) -} -func (c *countLock) Unlock() (err error) { - if c.nextIndex == c.successIndex { - return nil - } - return fmt.Errorf("Not locked: %s", c.description()) -} - -// end of util methods - -// start of tests - -// simple test -func TestGetLock(t *testing.T) { - lockfile := lockOrFail(t) - defer removeTestLock(lockfile) -} - -// a more complicated test that spans multiple processes -var lockPathVariable = "LOCK_PATH" -var successStatus = 0 -var unexpectedError = 1 -var busyStatus = 2 - -func TestTrylock(t *testing.T) { - lockpath := os.Getenv(lockPathVariable) - if len(lockpath) < 1 { - checkTrylockMainProcess(t) - } else { - getLockAndExit(lockpath) - } -} - -// the portion of TestTrylock that runs in the main process -func checkTrylockMainProcess(t *testing.T) { - var err error - lockfile := lockOrFail(t) - defer removeTestLock(lockfile) - lockdir := filepath.Dir(lockfile.File.Name()) - otherAcquired, message, err := forkAndGetLock(lockdir) - if err != nil { - t.Fatalf("Unexpected error in subprocess trying to lock uncontested fileLock: %v. Subprocess output: %q", err, message) - } - if !otherAcquired { - t.Fatalf("Subprocess failed to lock uncontested fileLock. Subprocess output: %q", message) - } - - err = lockfile.tryLock() - if err != nil { - t.Fatalf("Failed to lock fileLock: %v", err) - } - - reacquired, message, err := forkAndGetLock(filepath.Dir(lockfile.File.Name())) - if err != nil { - t.Fatal(err) - } - if reacquired { - t.Fatalf("Permitted locking fileLock twice. Subprocess output: %q", message) - } - - err = lockfile.Unlock() - if err != nil { - t.Fatalf("Error unlocking fileLock: %v", err) - } - - reacquired, message, err = forkAndGetLock(filepath.Dir(lockfile.File.Name())) - if err != nil { - t.Fatal(err) - } - if !reacquired { - t.Fatalf("Subprocess failed to acquire lock after it was released by the main process. Subprocess output: %q", message) - } -} -func forkAndGetLock(lockDir string) (acquired bool, subprocessOutput []byte, err error) { - cmd := exec.Command(os.Args[0], "-test.run=TestTrylock") - cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", lockPathVariable, lockDir)) - subprocessOutput, err = cmd.CombinedOutput() - exitStatus := successStatus - if exitError, ok := err.(*exec.ExitError); ok { - if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok { - exitStatus = waitStatus.ExitStatus() - } - } - if exitStatus == successStatus { - return true, subprocessOutput, nil - } else if exitStatus == busyStatus { - return false, subprocessOutput, nil - } else { - return false, subprocessOutput, fmt.Errorf("Unexpected status %v", exitStatus) - } -} - -// This function runs in a different process. See TestTrylock -func getLockAndExit(lockpath string) { - fmt.Printf("Will lock path %q\n", lockpath) - lockfile, err := newLock(lockpath) - exitStatus := unexpectedError - if err == nil { - err = lockfile.tryLock() - if err == nil { - exitStatus = successStatus - } else { - exitStatus = busyStatus - } - } - fmt.Printf("Tried to lock path %s. Received error %v. Exiting with status %v\n", lockpath, err, exitStatus) - os.Exit(exitStatus) -} - -func TestLockFirstTrySucceeds(t *testing.T) { - noopLogger := logger.New(ioutil.Discard) - lock := testLockCountingTo(0) - waiter := newCountWaiter(0) - err := lockSynchronous(lock, waiter, noopLogger) - if err != nil { - t.Fatal(err) - } - if waiter.numWaitsElapsed != 0 { - t.Fatalf("Incorrect number of waits elapsed; expected 0, got %v", waiter.numWaitsElapsed) - } -} -func TestLockThirdTrySucceeds(t *testing.T) { - noopLogger := logger.New(ioutil.Discard) - lock := testLockCountingTo(2) - waiter := newCountWaiter(2) - err := lockSynchronous(lock, waiter, noopLogger) - if err != nil { - t.Fatal(err) - } - if waiter.numWaitsElapsed != 2 { - t.Fatalf("Incorrect number of waits elapsed; expected 2, got %v", waiter.numWaitsElapsed) - } -} -func TestLockTimedOut(t *testing.T) { - noopLogger := logger.New(ioutil.Discard) - lock := testLockCountingTo(3) - waiter := newCountWaiter(2) - err := lockSynchronous(lock, waiter, noopLogger) - if err == nil { - t.Fatalf("Appeared to have acquired lock on iteration %v which should not be available until iteration %v", waiter.numWaitsElapsed, lock.successIndex) - } - if waiter.numWaitsElapsed != waiter.maxNumWaits { - t.Fatalf("Waited an incorrect number of times; expected %v, got %v", waiter.maxNumWaits, waiter.numWaitsElapsed) - } -} |