Spracherkennung für: .go vermutete Sprache: Abap {ABAP[60] Delphi[40] Fortran[40]} [Methode: maximale Elemente, drei Dimensionen]
// 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)
}
lockfilePollDuration :=
time.Second
lockfileTimeout :=
time.Second
* 10
if envTimeout := os.Getenv(
"SOONG_LOCK_TIMEOUT"); envTimeout !=
"" {
lockfileTimeout, err =
time.ParseDuration(envTimeout)
if err != nil {
ctx.Logger.Fatalf(
"failure parsing SOONG_LOCK_TIMEOUT %q: %s", envTimeout, err)
}
}
err = lockSynchronous(
*lockingInfo, newSleepWaiter(lockfilePollDuration, lockfileTimeout), ctx.Logger)
if err != nil {
ctx.Logger.Fatal(err)
}
return lockingInfo
}
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
}
done, description := waiter.checkDeadline()
if !waited {
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())
}
waited = true
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 {
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)
}