2017-05-02 19:26:49 +03:00
|
|
|
// Copyright 2017 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2016-07-12 21:56:12 +03:00
|
|
|
package gps
|
2016-03-16 23:34:09 +03:00
|
|
|
|
2016-03-31 08:10:24 +03:00
|
|
|
import (
|
2017-03-20 08:50:01 +03:00
|
|
|
"context"
|
2016-03-31 08:10:24 +03:00
|
|
|
"fmt"
|
2017-08-17 16:26:17 +03:00
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
2017-09-13 18:32:14 +03:00
|
|
|
"net/url"
|
2016-04-05 08:47:22 +03:00
|
|
|
"os"
|
2016-09-10 09:24:20 +03:00
|
|
|
"os/signal"
|
2016-07-29 23:55:23 +03:00
|
|
|
"path/filepath"
|
2016-09-12 18:17:43 +03:00
|
|
|
"runtime"
|
2016-07-29 23:55:23 +03:00
|
|
|
"strings"
|
2016-08-01 23:21:02 +03:00
|
|
|
"sync"
|
2016-09-10 09:24:20 +03:00
|
|
|
"sync/atomic"
|
2016-12-30 18:49:17 +03:00
|
|
|
"time"
|
2017-03-11 01:43:16 +03:00
|
|
|
|
2017-05-10 07:13:22 +03:00
|
|
|
"github.com/golang/dep/internal/gps/pkgtree"
|
2017-07-19 09:55:00 +03:00
|
|
|
"github.com/nightlyone/lockfile"
|
2017-06-16 17:40:32 +03:00
|
|
|
"github.com/pkg/errors"
|
2017-04-25 20:13:14 +03:00
|
|
|
"github.com/sdboyer/constext"
|
2016-03-31 08:10:24 +03:00
|
|
|
)
|
|
|
|
|
2017-03-20 08:50:01 +03:00
|
|
|
// Used to compute a friendly filepath from a URL-shaped input.
|
|
|
|
var sanitizer = strings.NewReplacer("-", "--", ":", "-", "/", "-", "+", "-")
|
2016-07-29 23:55:23 +03:00
|
|
|
|
2017-09-30 00:20:59 +03:00
|
|
|
// A locker is responsible for preventing multiple instances of dep from
|
|
|
|
// interfering with one-another.
|
|
|
|
//
|
|
|
|
// Currently, anything that can either TryLock(), Unlock(), or GetOwner()
|
|
|
|
// satifies that need.
|
|
|
|
type locker interface {
|
|
|
|
TryLock() error
|
|
|
|
Unlock() error
|
|
|
|
GetOwner() (*os.Process, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// A falselocker adheres to the locker interface and its purpose is to quietly
|
|
|
|
// fail to lock when the DEPNOLOCK environment variable is set.
|
|
|
|
//
|
|
|
|
// This allows dep to run on systems where file locking doesn't work --
|
|
|
|
// particularly those that use union mount type filesystems that don't
|
|
|
|
// implement hard links or fnctl() style locking.
|
|
|
|
type falseLocker struct{}
|
|
|
|
|
|
|
|
// Always returns an error to indicate there's no current ower PID for our
|
|
|
|
// lock.
|
|
|
|
func (fl falseLocker) GetOwner() (*os.Process, error) {
|
|
|
|
return nil, fmt.Errorf("falseLocker always fails")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does nothing and returns a nil error so caller beleives locking succeeded.
|
|
|
|
func (fl falseLocker) TryLock() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does nothing and returns a nil error so caller beleives unlocking succeeded.
|
|
|
|
func (fl falseLocker) Unlock() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-30 04:53:15 +03:00
|
|
|
// A SourceManager is responsible for retrieving, managing, and interrogating
|
|
|
|
// source repositories. Its primary purpose is to serve the needs of a Solver,
|
|
|
|
// but it is handy for other purposes, as well.
|
|
|
|
//
|
2016-07-28 04:46:52 +03:00
|
|
|
// gps's built-in SourceManager, SourceMgr, is intended to be generic and
|
|
|
|
// sufficient for any purpose. It provides some additional semantics around the
|
|
|
|
// methods defined here.
|
2016-03-16 23:34:09 +03:00
|
|
|
type SourceManager interface {
|
2016-08-10 08:45:17 +03:00
|
|
|
// SourceExists checks if a repository exists, either upstream or in the
|
2016-06-30 04:53:15 +03:00
|
|
|
// SourceManager's central repository cache.
|
2016-08-10 08:45:17 +03:00
|
|
|
SourceExists(ProjectIdentifier) (bool, error)
|
2016-06-30 04:53:15 +03:00
|
|
|
|
2016-08-18 05:41:59 +03:00
|
|
|
// SyncSourceFor will attempt to bring all local information about a source
|
|
|
|
// fully up to date.
|
|
|
|
SyncSourceFor(ProjectIdentifier) error
|
|
|
|
|
2016-06-30 04:53:15 +03:00
|
|
|
// ListVersions retrieves a list of the available versions for a given
|
|
|
|
// repository name.
|
2017-04-06 12:26:05 +03:00
|
|
|
ListVersions(ProjectIdentifier) ([]PairedVersion, error)
|
2016-06-30 04:53:15 +03:00
|
|
|
|
2016-07-10 08:52:20 +03:00
|
|
|
// RevisionPresentIn indicates whether the provided Version is present in
|
|
|
|
// the given repository.
|
2016-07-28 04:46:52 +03:00
|
|
|
RevisionPresentIn(ProjectIdentifier, Revision) (bool, error)
|
2016-07-06 04:44:09 +03:00
|
|
|
|
2016-07-28 04:46:52 +03:00
|
|
|
// ListPackages parses the tree of the Go packages at or below root of the
|
|
|
|
// provided ProjectIdentifier, at the provided version.
|
2017-03-11 01:43:16 +03:00
|
|
|
ListPackages(ProjectIdentifier, Version) (pkgtree.PackageTree, error)
|
2016-06-30 04:53:15 +03:00
|
|
|
|
2016-07-22 18:28:27 +03:00
|
|
|
// GetManifestAndLock returns manifest and lock information for the provided
|
|
|
|
// root import path.
|
|
|
|
//
|
2016-07-28 04:46:52 +03:00
|
|
|
// gps currently requires that projects be rooted at their repository root,
|
|
|
|
// necessitating that the ProjectIdentifier's ProjectRoot must also be a
|
2016-06-30 04:53:15 +03:00
|
|
|
// repository root.
|
2017-04-01 08:14:58 +03:00
|
|
|
GetManifestAndLock(ProjectIdentifier, Version, ProjectAnalyzer) (Manifest, Lock, error)
|
2016-07-28 04:46:52 +03:00
|
|
|
|
|
|
|
// ExportProject writes out the tree of the provided import path, at the
|
|
|
|
// provided version, to the provided directory.
|
2017-09-11 04:30:59 +03:00
|
|
|
ExportProject(context.Context, ProjectIdentifier, Version, string) error
|
2016-06-30 04:53:15 +03:00
|
|
|
|
2017-09-18 15:16:35 +03:00
|
|
|
// DeduceProjectRoot takes an import path and deduces the corresponding
|
2016-08-15 17:58:20 +03:00
|
|
|
// project/source root.
|
|
|
|
DeduceProjectRoot(ip string) (ProjectRoot, error)
|
2017-04-10 22:01:27 +03:00
|
|
|
|
2017-09-18 10:32:03 +03:00
|
|
|
// SourceURLsForPath takes an import path and deduces the set of source URLs
|
|
|
|
// that may refer to a canonical upstream source.
|
|
|
|
// In general, these URLs differ only by protocol (e.g. https vs. ssh), not path
|
2017-09-13 18:32:14 +03:00
|
|
|
SourceURLsForPath(ip string) ([]*url.URL, error)
|
|
|
|
|
2017-04-10 22:01:27 +03:00
|
|
|
// Release lets go of any locks held by the SourceManager. Once called, it is
|
|
|
|
// no longer safe to call methods against it; all method calls will
|
|
|
|
// immediately result in errors.
|
|
|
|
Release()
|
2017-06-16 17:40:32 +03:00
|
|
|
|
2017-07-06 18:25:20 +03:00
|
|
|
// InferConstraint tries to puzzle out what kind of version is given in a string -
|
2017-06-16 17:40:32 +03:00
|
|
|
// semver, a revision, or as a fallback, a plain tag
|
2017-07-06 18:25:20 +03:00
|
|
|
InferConstraint(s string, pi ProjectIdentifier) (Constraint, error)
|
2016-03-30 20:51:23 +03:00
|
|
|
}
|
|
|
|
|
2016-07-22 08:54:40 +03:00
|
|
|
// A ProjectAnalyzer is responsible for analyzing a given path for Manifest and
|
|
|
|
// Lock information. Tools relying on gps must implement one.
|
2016-06-30 05:47:25 +03:00
|
|
|
type ProjectAnalyzer interface {
|
2016-07-22 18:28:27 +03:00
|
|
|
// Perform analysis of the filesystem tree rooted at path, with the
|
|
|
|
// root import path importRoot, to determine the project's constraints, as
|
|
|
|
// indicated by a Manifest and Lock.
|
2017-05-17 06:37:47 +03:00
|
|
|
//
|
|
|
|
// Note that an error will typically cause the solver to treat the analyzed
|
|
|
|
// version as unusable. As such, an error should generally only be returned
|
|
|
|
// if the code tree is somehow malformed, but not if the implementor's
|
|
|
|
// expected files containing Manifest and Lock data are merely absent.
|
2016-07-22 18:28:27 +03:00
|
|
|
DeriveManifestAndLock(path string, importRoot ProjectRoot) (Manifest, Lock, error)
|
2016-08-15 17:58:20 +03:00
|
|
|
|
2017-06-14 19:56:28 +03:00
|
|
|
// Info reports this project analyzer's info.
|
|
|
|
Info() ProjectAnalyzerInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProjectAnalyzerInfo indicates a ProjectAnalyzer's name and version.
|
|
|
|
type ProjectAnalyzerInfo struct {
|
|
|
|
Name string
|
|
|
|
Version int
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns a string like: "<name>.<decimal version>"
|
|
|
|
func (p ProjectAnalyzerInfo) String() string {
|
|
|
|
return fmt.Sprintf("%s.%d", p.Name, p.Version)
|
2016-06-30 05:47:25 +03:00
|
|
|
}
|
|
|
|
|
2016-07-12 21:56:12 +03:00
|
|
|
// SourceMgr is the default SourceManager for gps.
|
2016-03-31 08:10:24 +03:00
|
|
|
//
|
|
|
|
// There's no (planned) reason why it would need to be reimplemented by other
|
|
|
|
// tools; control via dependency injection is intended to be sufficient.
|
2016-07-10 08:52:20 +03:00
|
|
|
type SourceMgr struct {
|
2017-03-28 14:18:00 +03:00
|
|
|
cachedir string // path to root of cache dir
|
2017-09-30 00:20:59 +03:00
|
|
|
lf locker // handle for the sm lock file on disk
|
2017-03-31 13:01:17 +03:00
|
|
|
suprvsr *supervisor // subsystem that supervises running calls/io
|
2017-03-31 19:56:30 +03:00
|
|
|
cancelAll context.CancelFunc // cancel func to kill all running work
|
2017-03-28 14:18:00 +03:00
|
|
|
deduceCoord *deductionCoordinator // subsystem that manages import path deduction
|
|
|
|
srcCoord *sourceCoordinator // subsystem that manages sources
|
|
|
|
sigmut sync.Mutex // mutex protecting signal handling setup/teardown
|
2017-03-31 19:56:30 +03:00
|
|
|
qch chan struct{} // quit chan for signal handler
|
2017-03-28 14:18:00 +03:00
|
|
|
relonce sync.Once // once-er to ensure we only release once
|
|
|
|
releasing int32 // flag indicating release of sm has begun
|
2016-09-10 06:40:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type smIsReleased struct{}
|
|
|
|
|
|
|
|
func (smIsReleased) Error() string {
|
|
|
|
return "this SourceMgr has been released, its methods can no longer be called"
|
2016-03-31 08:10:24 +03:00
|
|
|
}
|
|
|
|
|
2016-07-10 08:52:20 +03:00
|
|
|
var _ SourceManager = &SourceMgr{}
|
|
|
|
|
2017-08-17 16:26:17 +03:00
|
|
|
// SourceManagerConfig holds configuration information for creating SourceMgrs.
|
|
|
|
type SourceManagerConfig struct {
|
2017-09-30 00:20:59 +03:00
|
|
|
Cachedir string // Where to store local instances of upstream sources.
|
|
|
|
Logger *log.Logger // Optional info/warn logger. Discards if nil.
|
|
|
|
DisableLocking bool // True if the SourceManager should NOT use a lock file to protect the Cachedir from multiple processes.
|
2017-08-17 16:26:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewSourceManager produces an instance of gps's built-in SourceManager.
|
2016-06-30 04:53:15 +03:00
|
|
|
//
|
2016-07-10 08:52:20 +03:00
|
|
|
// The returned SourceManager aggressively caches information wherever possible.
|
2016-09-22 05:28:09 +03:00
|
|
|
// If tools need to do preliminary work involving upstream repository analysis
|
|
|
|
// prior to invoking a solve run, it is recommended that they create this
|
|
|
|
// SourceManager as early as possible and use it to their ends. That way, the
|
|
|
|
// solver can benefit from any caches that may have already been warmed.
|
2016-06-30 04:53:15 +03:00
|
|
|
//
|
2016-09-22 05:28:09 +03:00
|
|
|
// gps's SourceManager is intended to be threadsafe (if it's not, please file a
|
|
|
|
// bug!). It should be safe to reuse across concurrent solving runs, even on
|
|
|
|
// unrelated projects.
|
2017-08-17 16:26:17 +03:00
|
|
|
func NewSourceManager(c SourceManagerConfig) (*SourceMgr, error) {
|
|
|
|
if c.Logger == nil {
|
|
|
|
c.Logger = log.New(ioutil.Discard, "", 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := os.MkdirAll(filepath.Join(c.Cachedir, "sources"), 0777)
|
2016-04-05 22:19:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-07-19 09:55:00 +03:00
|
|
|
// Fix for #820
|
|
|
|
//
|
|
|
|
// Consult https://godoc.org/github.com/nightlyone/lockfile for the lockfile
|
|
|
|
// behaviour. It's magic. It deals with stale processes, and if there is
|
|
|
|
// a process keeping the lock busy, it will pass back a temporary error that
|
|
|
|
// we can spin on.
|
|
|
|
|
2017-08-17 16:26:17 +03:00
|
|
|
glpath := filepath.Join(c.Cachedir, "sm.lock")
|
2017-09-30 00:20:59 +03:00
|
|
|
|
|
|
|
lockfile, err := func() (locker, error) {
|
|
|
|
if c.DisableLocking {
|
|
|
|
return falseLocker{}, nil
|
|
|
|
}
|
|
|
|
return lockfile.New(glpath)
|
|
|
|
}()
|
|
|
|
|
2017-07-19 09:55:00 +03:00
|
|
|
if err != nil {
|
2016-09-22 05:28:09 +03:00
|
|
|
return nil, CouldNotCreateLockError{
|
|
|
|
Path: glpath,
|
2017-09-01 03:11:22 +03:00
|
|
|
Err: errors.Wrapf(err, "unable to create lock %s", glpath),
|
2016-09-22 05:28:09 +03:00
|
|
|
}
|
2016-04-05 22:19:14 +03:00
|
|
|
}
|
2016-04-06 07:08:28 +03:00
|
|
|
|
2017-07-19 17:37:14 +03:00
|
|
|
process, err := lockfile.GetOwner()
|
|
|
|
if err == nil {
|
|
|
|
// If we didn't get an error, then the lockfile exists already. We should
|
|
|
|
// check to see if it's us already:
|
|
|
|
if process.Pid == os.Getpid() {
|
|
|
|
return nil, CouldNotCreateLockError{
|
|
|
|
Path: glpath,
|
|
|
|
Err: fmt.Errorf("lockfile %s already locked by this process", glpath),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-20 07:24:21 +03:00
|
|
|
// There is a lockfile, but it's owned by someone else. We'll try to lock
|
2017-07-19 17:37:14 +03:00
|
|
|
// it anyway.
|
|
|
|
}
|
|
|
|
|
2017-07-19 09:55:00 +03:00
|
|
|
// If it's a TemporaryError, we retry every second. Otherwise, we fail
|
|
|
|
// permanently.
|
2017-07-20 07:24:21 +03:00
|
|
|
//
|
2017-08-05 08:00:45 +03:00
|
|
|
// TODO: #534 needs to be implemented to provide a better way to log warnings,
|
|
|
|
// but until then we will just use stderr.
|
2017-07-19 09:55:00 +03:00
|
|
|
|
2017-08-06 18:02:21 +03:00
|
|
|
// Implicit Time of 0.
|
|
|
|
var lasttime time.Time
|
2017-07-19 09:55:00 +03:00
|
|
|
err = lockfile.TryLock()
|
|
|
|
for err != nil {
|
2017-08-05 08:00:45 +03:00
|
|
|
nowtime := time.Now()
|
|
|
|
duration := nowtime.Sub(lasttime)
|
|
|
|
|
2017-08-06 18:02:21 +03:00
|
|
|
// The first time this is evaluated, duration will be very large as lasttime is 0.
|
|
|
|
// Unless time travel is invented and someone travels back to the year 1, we should
|
|
|
|
// be ok.
|
2017-08-05 08:00:45 +03:00
|
|
|
if duration > 15*time.Second {
|
|
|
|
fmt.Fprintf(os.Stderr, "waiting for lockfile %s: %s\n", glpath, err.Error())
|
|
|
|
lasttime = nowtime
|
|
|
|
}
|
|
|
|
|
2017-09-11 21:50:32 +03:00
|
|
|
if t, ok := err.(interface {
|
2017-07-19 09:55:00 +03:00
|
|
|
Temporary() bool
|
2017-09-11 21:50:32 +03:00
|
|
|
}); ok && t.Temporary() {
|
2017-07-19 09:55:00 +03:00
|
|
|
time.Sleep(time.Second * 1)
|
|
|
|
} else {
|
|
|
|
return nil, CouldNotCreateLockError{
|
|
|
|
Path: glpath,
|
2017-09-01 03:11:22 +03:00
|
|
|
Err: errors.Wrapf(err, "unable to lock %s", glpath),
|
2017-07-19 09:55:00 +03:00
|
|
|
}
|
2016-09-22 05:28:09 +03:00
|
|
|
}
|
2017-07-19 09:55:00 +03:00
|
|
|
err = lockfile.TryLock()
|
2016-04-05 22:19:14 +03:00
|
|
|
}
|
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
ctx, cf := context.WithCancel(context.TODO())
|
|
|
|
superv := newSupervisor(ctx)
|
2017-03-31 13:01:17 +03:00
|
|
|
deducer := newDeductionCoordinator(superv)
|
2017-03-20 08:50:01 +03:00
|
|
|
|
2016-09-10 09:24:20 +03:00
|
|
|
sm := &SourceMgr{
|
2017-08-17 16:26:17 +03:00
|
|
|
cachedir: c.Cachedir,
|
2017-09-30 00:20:59 +03:00
|
|
|
lf: lockfile,
|
2017-03-31 13:01:17 +03:00
|
|
|
suprvsr: superv,
|
2017-03-31 19:56:30 +03:00
|
|
|
cancelAll: cf,
|
2017-03-20 08:50:01 +03:00
|
|
|
deduceCoord: deducer,
|
2017-08-17 16:26:17 +03:00
|
|
|
srcCoord: newSourceCoordinator(superv, deducer, c.Cachedir, c.Logger),
|
2017-03-20 08:50:01 +03:00
|
|
|
qch: make(chan struct{}),
|
2016-09-10 09:24:20 +03:00
|
|
|
}
|
|
|
|
|
2016-12-30 19:35:06 +03:00
|
|
|
return sm, nil
|
|
|
|
}
|
|
|
|
|
2017-01-11 06:00:30 +03:00
|
|
|
// UseDefaultSignalHandling sets up typical os.Interrupt signal handling for a
|
2017-01-01 08:07:21 +03:00
|
|
|
// SourceMgr.
|
2017-01-11 06:00:30 +03:00
|
|
|
func (sm *SourceMgr) UseDefaultSignalHandling() {
|
2017-01-01 08:07:21 +03:00
|
|
|
sigch := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigch, os.Interrupt)
|
2016-12-30 19:35:06 +03:00
|
|
|
sm.HandleSignals(sigch)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleSignals sets up logic to handle incoming signals with the goal of
|
|
|
|
// shutting down the SourceMgr safely.
|
|
|
|
//
|
|
|
|
// Calling code must provide the signal channel, and is responsible for calling
|
|
|
|
// signal.Notify() on that channel.
|
|
|
|
//
|
|
|
|
// Successive calls to HandleSignals() will deregister the previous handler and
|
|
|
|
// set up a new one. It is not recommended that the same channel be passed
|
|
|
|
// multiple times to this method.
|
|
|
|
//
|
|
|
|
// SetUpSigHandling() will set up a handler that is appropriate for most
|
|
|
|
// use cases.
|
|
|
|
func (sm *SourceMgr) HandleSignals(sigch chan os.Signal) {
|
|
|
|
sm.sigmut.Lock()
|
|
|
|
// always start by closing the qch, which will lead to any existing signal
|
|
|
|
// handler terminating, and deregistering its sigch.
|
|
|
|
if sm.qch != nil {
|
|
|
|
close(sm.qch)
|
|
|
|
}
|
|
|
|
sm.qch = make(chan struct{})
|
|
|
|
|
|
|
|
// Run a new goroutine with the input sigch and the fresh qch
|
|
|
|
go func(sch chan os.Signal, qch <-chan struct{}) {
|
2016-12-30 20:23:12 +03:00
|
|
|
defer signal.Stop(sch)
|
2017-09-11 22:24:56 +03:00
|
|
|
select {
|
|
|
|
case <-sch:
|
|
|
|
// Set up a timer to uninstall the signal handler after three
|
|
|
|
// seconds, so that the user can easily force termination with a
|
|
|
|
// second ctrl-c
|
2017-09-11 22:35:35 +03:00
|
|
|
time.AfterFunc(3*time.Second, func() {
|
2017-09-11 22:24:56 +03:00
|
|
|
signal.Stop(sch)
|
2017-09-11 22:35:35 +03:00
|
|
|
})
|
2017-09-11 22:24:56 +03:00
|
|
|
|
2017-09-11 22:39:09 +03:00
|
|
|
if opc := sm.suprvsr.count(); opc > 0 {
|
2017-09-11 22:24:56 +03:00
|
|
|
fmt.Printf("Signal received: waiting for %v ops to complete...\n", opc)
|
|
|
|
}
|
|
|
|
|
2017-09-11 22:39:09 +03:00
|
|
|
sm.Release()
|
2017-09-11 22:24:56 +03:00
|
|
|
case <-qch:
|
|
|
|
// quit channel triggered - deregister our sigch and return
|
2016-09-10 09:24:20 +03:00
|
|
|
}
|
2016-12-30 19:35:06 +03:00
|
|
|
}(sigch, sm.qch)
|
|
|
|
// Try to ensure handler is blocked in for-select before releasing the mutex
|
2016-09-12 18:17:43 +03:00
|
|
|
runtime.Gosched()
|
2016-09-10 09:24:20 +03:00
|
|
|
|
2016-12-30 19:35:06 +03:00
|
|
|
sm.sigmut.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// StopSignalHandling deregisters any signal handler running on this SourceMgr.
|
|
|
|
//
|
|
|
|
// It's normally not necessary to call this directly; it will be called as
|
|
|
|
// needed by Release().
|
|
|
|
func (sm *SourceMgr) StopSignalHandling() {
|
|
|
|
sm.sigmut.Lock()
|
|
|
|
if sm.qch != nil {
|
|
|
|
close(sm.qch)
|
2016-12-30 20:21:17 +03:00
|
|
|
sm.qch = nil
|
2016-12-30 19:35:06 +03:00
|
|
|
runtime.Gosched()
|
|
|
|
}
|
|
|
|
sm.sigmut.Unlock()
|
2016-03-31 08:10:24 +03:00
|
|
|
}
|
|
|
|
|
2016-10-22 04:35:08 +03:00
|
|
|
// CouldNotCreateLockError describe failure modes in which creating a SourceMgr
|
|
|
|
// did not succeed because there was an error while attempting to create the
|
|
|
|
// on-disk lock file.
|
2016-09-22 05:28:09 +03:00
|
|
|
type CouldNotCreateLockError struct {
|
|
|
|
Path string
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e CouldNotCreateLockError) Error() string {
|
|
|
|
return e.Err.Error()
|
|
|
|
}
|
|
|
|
|
2016-09-12 18:17:43 +03:00
|
|
|
// Release lets go of any locks held by the SourceManager. Once called, it is no
|
|
|
|
// longer safe to call methods against it; all method calls will immediately
|
|
|
|
// result in errors.
|
2016-07-10 08:52:20 +03:00
|
|
|
func (sm *SourceMgr) Release() {
|
2017-09-11 22:39:09 +03:00
|
|
|
atomic.StoreInt32(&sm.releasing, 1)
|
2016-09-10 06:40:52 +03:00
|
|
|
|
2017-09-11 22:39:09 +03:00
|
|
|
sm.relonce.Do(func() {
|
|
|
|
// Send the signal to the supervisor to cancel all running calls.
|
|
|
|
sm.cancelAll()
|
|
|
|
sm.suprvsr.wait()
|
|
|
|
|
|
|
|
// Close the source coordinator.
|
|
|
|
sm.srcCoord.close()
|
|
|
|
|
|
|
|
// Close the file handle for the lock file and remove it from disk
|
|
|
|
sm.lf.Unlock()
|
|
|
|
os.Remove(filepath.Join(sm.cachedir, "sm.lock"))
|
|
|
|
|
|
|
|
// Close the qch, if non-nil, so the signal handlers run out. This will
|
|
|
|
// also deregister the sig channel, if any has been set up.
|
|
|
|
if sm.qch != nil {
|
|
|
|
close(sm.qch)
|
|
|
|
}
|
|
|
|
})
|
2016-07-22 08:54:40 +03:00
|
|
|
}
|
|
|
|
|
2016-07-28 04:46:52 +03:00
|
|
|
// GetManifestAndLock returns manifest and lock information for the provided
|
2017-04-01 08:14:58 +03:00
|
|
|
// ProjectIdentifier, at the provided Version. The work of producing the
|
|
|
|
// manifest and lock is delegated to the provided ProjectAnalyzer's
|
|
|
|
// DeriveManifestAndLock() method.
|
|
|
|
func (sm *SourceMgr) GetManifestAndLock(id ProjectIdentifier, v Version, an ProjectAnalyzer) (Manifest, Lock, error) {
|
2017-09-11 20:34:04 +03:00
|
|
|
if atomic.LoadInt32(&sm.releasing) == 1 {
|
2016-09-10 06:40:52 +03:00
|
|
|
return nil, nil, smIsReleased{}
|
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id)
|
2016-03-31 08:10:24 +03:00
|
|
|
if err != nil {
|
2016-06-30 05:11:59 +03:00
|
|
|
return nil, nil, err
|
2016-03-31 08:10:24 +03:00
|
|
|
}
|
|
|
|
|
2017-04-01 08:14:58 +03:00
|
|
|
return srcg.getManifestAndLock(context.TODO(), id.ProjectRoot, v, an)
|
2016-03-31 08:10:24 +03:00
|
|
|
}
|
|
|
|
|
2016-07-28 04:46:52 +03:00
|
|
|
// ListPackages parses the tree of the Go packages at and below the ProjectRoot
|
|
|
|
// of the given ProjectIdentifier, at the given version.
|
2017-03-11 01:43:16 +03:00
|
|
|
func (sm *SourceMgr) ListPackages(id ProjectIdentifier, v Version) (pkgtree.PackageTree, error) {
|
2017-09-11 20:34:04 +03:00
|
|
|
if atomic.LoadInt32(&sm.releasing) == 1 {
|
2017-03-11 01:43:16 +03:00
|
|
|
return pkgtree.PackageTree{}, smIsReleased{}
|
2016-09-10 06:40:52 +03:00
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id)
|
2016-06-15 06:57:48 +03:00
|
|
|
if err != nil {
|
2017-03-11 01:43:16 +03:00
|
|
|
return pkgtree.PackageTree{}, err
|
2016-06-15 06:57:48 +03:00
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
return srcg.listPackages(context.TODO(), id.ProjectRoot, v)
|
2016-06-15 06:57:48 +03:00
|
|
|
}
|
|
|
|
|
2016-06-30 04:53:15 +03:00
|
|
|
// ListVersions retrieves a list of the available versions for a given
|
|
|
|
// repository name.
|
|
|
|
//
|
2016-07-06 04:44:09 +03:00
|
|
|
// The list is not sorted; while it may be returned in the order that the
|
2016-06-30 04:53:15 +03:00
|
|
|
// underlying VCS reports version information, no guarantee is made. It is
|
|
|
|
// expected that the caller either not care about order, or sort the result
|
|
|
|
// themselves.
|
|
|
|
//
|
2016-07-28 04:46:52 +03:00
|
|
|
// This list is always retrieved from upstream on the first call. Subsequent
|
|
|
|
// calls will return a cached version of the first call's results. if upstream
|
|
|
|
// is not accessible (network outage, access issues, or the resource actually
|
|
|
|
// went away), an error will be returned.
|
2017-04-06 12:26:05 +03:00
|
|
|
func (sm *SourceMgr) ListVersions(id ProjectIdentifier) ([]PairedVersion, error) {
|
2017-09-11 20:34:04 +03:00
|
|
|
if atomic.LoadInt32(&sm.releasing) == 1 {
|
2016-09-10 06:40:52 +03:00
|
|
|
return nil, smIsReleased{}
|
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id)
|
2016-03-31 08:10:24 +03:00
|
|
|
if err != nil {
|
2016-07-12 07:44:02 +03:00
|
|
|
// TODO(sdboyer) More-er proper-er errors
|
2016-03-31 08:10:24 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-04-06 12:26:05 +03:00
|
|
|
return srcg.listVersions(context.TODO())
|
2016-03-31 08:10:24 +03:00
|
|
|
}
|
|
|
|
|
2016-07-06 04:44:09 +03:00
|
|
|
// RevisionPresentIn indicates whether the provided Revision is present in the given
|
2016-07-10 08:52:20 +03:00
|
|
|
// repository.
|
2016-07-28 04:46:52 +03:00
|
|
|
func (sm *SourceMgr) RevisionPresentIn(id ProjectIdentifier, r Revision) (bool, error) {
|
2017-09-11 20:34:04 +03:00
|
|
|
if atomic.LoadInt32(&sm.releasing) == 1 {
|
2016-09-10 06:40:52 +03:00
|
|
|
return false, smIsReleased{}
|
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id)
|
2016-07-06 04:44:09 +03:00
|
|
|
if err != nil {
|
2016-07-12 07:44:02 +03:00
|
|
|
// TODO(sdboyer) More-er proper-er errors
|
2016-07-06 04:44:09 +03:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
return srcg.revisionPresentIn(context.TODO(), r)
|
2016-07-06 04:44:09 +03:00
|
|
|
}
|
|
|
|
|
2016-08-10 08:45:17 +03:00
|
|
|
// SourceExists checks if a repository exists, either upstream or in the cache,
|
2016-07-28 04:46:52 +03:00
|
|
|
// for the provided ProjectIdentifier.
|
2016-08-10 08:45:17 +03:00
|
|
|
func (sm *SourceMgr) SourceExists(id ProjectIdentifier) (bool, error) {
|
2017-09-11 20:34:04 +03:00
|
|
|
if atomic.LoadInt32(&sm.releasing) == 1 {
|
2016-09-10 06:40:52 +03:00
|
|
|
return false, smIsReleased{}
|
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id)
|
2016-04-05 22:19:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2017-04-01 20:15:52 +03:00
|
|
|
ctx := context.TODO()
|
|
|
|
return srcg.existsInCache(ctx) || srcg.existsUpstream(ctx), nil
|
2016-03-31 08:10:24 +03:00
|
|
|
}
|
|
|
|
|
2016-08-18 05:41:59 +03:00
|
|
|
// SyncSourceFor will ensure that all local caches and information about a
|
|
|
|
// source are up to date with any network-acccesible information.
|
|
|
|
//
|
|
|
|
// The primary use case for this is prefetching.
|
|
|
|
func (sm *SourceMgr) SyncSourceFor(id ProjectIdentifier) error {
|
2017-09-11 20:34:04 +03:00
|
|
|
if atomic.LoadInt32(&sm.releasing) == 1 {
|
2016-09-10 06:40:52 +03:00
|
|
|
return smIsReleased{}
|
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id)
|
2016-08-18 05:41:59 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-03-28 05:14:12 +03:00
|
|
|
return srcg.syncLocal(context.TODO())
|
2016-08-18 05:41:59 +03:00
|
|
|
}
|
|
|
|
|
2016-07-28 04:46:52 +03:00
|
|
|
// ExportProject writes out the tree of the provided ProjectIdentifier's
|
|
|
|
// ProjectRoot, at the provided version, to the provided directory.
|
2017-09-11 04:30:59 +03:00
|
|
|
func (sm *SourceMgr) ExportProject(ctx context.Context, id ProjectIdentifier, v Version, to string) error {
|
2017-09-11 20:34:04 +03:00
|
|
|
if atomic.LoadInt32(&sm.releasing) == 1 {
|
2016-09-10 06:40:52 +03:00
|
|
|
return smIsReleased{}
|
|
|
|
}
|
|
|
|
|
2017-09-11 04:30:59 +03:00
|
|
|
srcg, err := sm.srcCoord.getSourceGatewayFor(ctx, id)
|
2016-04-13 17:59:10 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-09-11 04:30:59 +03:00
|
|
|
return srcg.exportVersionTo(ctx, v, to)
|
2016-04-13 17:59:10 +03:00
|
|
|
}
|
|
|
|
|
2016-10-22 04:35:08 +03:00
|
|
|
// DeduceProjectRoot takes an import path and deduces the corresponding
|
2016-08-15 08:48:11 +03:00
|
|
|
// project/source root.
|
2016-08-14 06:21:03 +03:00
|
|
|
//
|
|
|
|
// Note that some import paths may require network activity to correctly
|
|
|
|
// determine the root of the path, such as, but not limited to, vanity import
|
|
|
|
// paths. (A special exception is written for gopkg.in to minimize network
|
|
|
|
// activity, as its behavior is well-structured)
|
|
|
|
func (sm *SourceMgr) DeduceProjectRoot(ip string) (ProjectRoot, error) {
|
2017-09-11 20:34:04 +03:00
|
|
|
if atomic.LoadInt32(&sm.releasing) == 1 {
|
2016-09-10 06:40:52 +03:00
|
|
|
return "", smIsReleased{}
|
|
|
|
}
|
2016-08-14 06:21:03 +03:00
|
|
|
|
2017-03-28 17:18:28 +03:00
|
|
|
pd, err := sm.deduceCoord.deduceRootPath(context.TODO(), ip)
|
2017-03-28 05:14:12 +03:00
|
|
|
return ProjectRoot(pd.root), err
|
2016-08-14 06:21:03 +03:00
|
|
|
}
|
|
|
|
|
2017-07-22 15:54:38 +03:00
|
|
|
// InferConstraint tries to puzzle out what kind of version is given in a
|
2017-08-28 16:48:59 +03:00
|
|
|
// string. Preference is given first for branches, then semver constraints, then
|
|
|
|
// plain tags, and then revisions.
|
2017-07-06 18:25:20 +03:00
|
|
|
func (sm *SourceMgr) InferConstraint(s string, pi ProjectIdentifier) (Constraint, error) {
|
2017-07-26 03:21:30 +03:00
|
|
|
if s == "" {
|
|
|
|
return Any(), nil
|
|
|
|
}
|
|
|
|
|
2017-06-03 20:25:52 +03:00
|
|
|
// Lookup the string in the repository
|
|
|
|
var version PairedVersion
|
2017-06-16 17:40:32 +03:00
|
|
|
versions, err := sm.ListVersions(pi)
|
|
|
|
if err != nil {
|
2017-08-31 03:53:53 +03:00
|
|
|
return nil, errors.Wrapf(err, "list versions for %s", pi) // means repo does not exist
|
2017-06-16 17:40:32 +03:00
|
|
|
}
|
2017-06-03 20:25:52 +03:00
|
|
|
SortPairedForUpgrade(versions)
|
|
|
|
for _, v := range versions {
|
2017-07-26 03:21:30 +03:00
|
|
|
if s == v.String() {
|
2017-06-03 20:25:52 +03:00
|
|
|
version = v
|
|
|
|
break
|
2017-06-16 17:40:32 +03:00
|
|
|
}
|
|
|
|
}
|
2017-06-03 20:25:52 +03:00
|
|
|
|
|
|
|
// Branch
|
|
|
|
if version != nil && version.Type() == IsBranch {
|
|
|
|
return version.Unpair(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Semver Constraint
|
|
|
|
c, err := NewSemverConstraintIC(s)
|
|
|
|
if c != nil && err == nil {
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tag
|
|
|
|
if version != nil {
|
|
|
|
return version.Unpair(), nil
|
|
|
|
}
|
|
|
|
|
2017-08-17 23:31:17 +03:00
|
|
|
// Revision, possibly abbreviated
|
|
|
|
r, err := sm.disambiguateRevision(context.TODO(), pi, Revision(s))
|
|
|
|
if err == nil {
|
|
|
|
return r, nil
|
2017-08-17 18:16:07 +03:00
|
|
|
}
|
|
|
|
|
2017-06-16 17:40:32 +03:00
|
|
|
return nil, errors.Errorf("%s is not a valid version for the package %s(%s)", s, pi.ProjectRoot, pi.Source)
|
|
|
|
}
|
|
|
|
|
2017-09-18 10:32:03 +03:00
|
|
|
// SourceURLsForPath takes an import path and deduces the set of source URLs
|
|
|
|
// that may refer to a canonical upstream source.
|
|
|
|
// In general, these URLs differ only by protocol (e.g. https vs. ssh), not path
|
2017-09-13 18:32:14 +03:00
|
|
|
func (sm *SourceMgr) SourceURLsForPath(ip string) ([]*url.URL, error) {
|
|
|
|
deduced, err := sm.deduceCoord.deduceRootPath(context.TODO(), ip)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return deduced.mb.possibleURLs(), nil
|
|
|
|
}
|
|
|
|
|
2017-08-17 23:31:17 +03:00
|
|
|
// disambiguateRevision looks up a revision in the underlying source, spitting
|
|
|
|
// it back out in an unabbreviated, disambiguated form.
|
|
|
|
//
|
|
|
|
// For example, if pi refers to a git-based project, then rev could be an
|
|
|
|
// abbreviated git commit hash. disambiguateRevision would return the complete
|
|
|
|
// hash.
|
|
|
|
func (sm *SourceMgr) disambiguateRevision(ctx context.Context, pi ProjectIdentifier, rev Revision) (Revision, error) {
|
|
|
|
srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), pi)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return srcg.disambiguateRevision(ctx, rev)
|
|
|
|
}
|
|
|
|
|
2017-03-28 14:36:39 +03:00
|
|
|
type timeCount struct {
|
|
|
|
count int
|
|
|
|
start time.Time
|
2016-08-14 06:21:03 +03:00
|
|
|
}
|
|
|
|
|
2017-03-28 14:36:39 +03:00
|
|
|
type durCount struct {
|
|
|
|
count int
|
|
|
|
dur time.Duration
|
|
|
|
}
|
2016-08-14 06:21:03 +03:00
|
|
|
|
2017-03-31 13:01:17 +03:00
|
|
|
type supervisor struct {
|
2017-09-11 03:45:22 +03:00
|
|
|
ctx context.Context
|
|
|
|
mu sync.Mutex // Guards all maps
|
|
|
|
cond sync.Cond // Wraps mu so callers can wait until all calls end
|
|
|
|
running map[callInfo]timeCount
|
|
|
|
ran map[callType]durCount
|
2017-03-28 14:36:39 +03:00
|
|
|
}
|
2016-08-14 06:21:03 +03:00
|
|
|
|
2017-03-31 13:01:17 +03:00
|
|
|
func newSupervisor(ctx context.Context) *supervisor {
|
2017-04-01 07:43:06 +03:00
|
|
|
supv := &supervisor{
|
2017-09-11 03:45:22 +03:00
|
|
|
ctx: ctx,
|
|
|
|
running: make(map[callInfo]timeCount),
|
|
|
|
ran: make(map[callType]durCount),
|
2016-08-14 06:21:03 +03:00
|
|
|
}
|
|
|
|
|
2017-04-01 07:43:06 +03:00
|
|
|
supv.cond = sync.Cond{L: &supv.mu}
|
|
|
|
return supv
|
2016-08-14 06:21:03 +03:00
|
|
|
}
|
|
|
|
|
2017-03-31 07:00:00 +03:00
|
|
|
// do executes the incoming closure using a conjoined context, and keeps
|
|
|
|
// counters to ensure the sourceMgr can't finish Release()ing until after all
|
|
|
|
// calls have returned.
|
2017-03-31 19:56:30 +03:00
|
|
|
func (sup *supervisor) do(inctx context.Context, name string, typ callType, f func(context.Context) error) error {
|
2017-03-28 14:36:39 +03:00
|
|
|
ci := callInfo{
|
|
|
|
name: name,
|
|
|
|
typ: typ,
|
2016-10-17 06:19:34 +03:00
|
|
|
}
|
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
octx, err := sup.start(ci)
|
2016-08-14 06:21:03 +03:00
|
|
|
if err != nil {
|
2017-03-31 07:00:00 +03:00
|
|
|
return err
|
2016-10-17 06:19:34 +03:00
|
|
|
}
|
|
|
|
|
2017-03-28 14:36:39 +03:00
|
|
|
cctx, cancelFunc := constext.Cons(inctx, octx)
|
2017-03-31 07:00:00 +03:00
|
|
|
err = f(cctx)
|
2017-03-31 19:56:30 +03:00
|
|
|
sup.done(ci)
|
2017-03-31 07:00:00 +03:00
|
|
|
cancelFunc()
|
|
|
|
return err
|
2017-03-28 14:36:39 +03:00
|
|
|
}
|
2016-10-17 06:19:34 +03:00
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
func (sup *supervisor) start(ci callInfo) (context.Context, error) {
|
|
|
|
sup.mu.Lock()
|
|
|
|
defer sup.mu.Unlock()
|
2017-09-11 03:45:22 +03:00
|
|
|
if err := sup.ctx.Err(); err != nil {
|
2017-03-28 14:36:39 +03:00
|
|
|
// We've already been canceled; error out.
|
2017-09-11 03:45:22 +03:00
|
|
|
return nil, err
|
2016-10-17 06:19:34 +03:00
|
|
|
}
|
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
if existingInfo, has := sup.running[ci]; has {
|
2017-03-28 14:36:39 +03:00
|
|
|
existingInfo.count++
|
2017-03-31 19:56:30 +03:00
|
|
|
sup.running[ci] = existingInfo
|
2016-10-17 06:19:34 +03:00
|
|
|
} else {
|
2017-03-31 19:56:30 +03:00
|
|
|
sup.running[ci] = timeCount{
|
2017-03-28 14:36:39 +03:00
|
|
|
count: 1,
|
|
|
|
start: time.Now(),
|
|
|
|
}
|
2016-08-14 06:21:03 +03:00
|
|
|
}
|
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
return sup.ctx, nil
|
|
|
|
}
|
2016-10-17 06:19:34 +03:00
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
func (sup *supervisor) count() int {
|
|
|
|
sup.mu.Lock()
|
|
|
|
defer sup.mu.Unlock()
|
|
|
|
return len(sup.running)
|
2017-03-28 14:36:39 +03:00
|
|
|
}
|
2016-10-17 06:19:34 +03:00
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
func (sup *supervisor) done(ci callInfo) {
|
|
|
|
sup.mu.Lock()
|
2016-10-17 06:19:34 +03:00
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
existingInfo, has := sup.running[ci]
|
2017-03-28 14:36:39 +03:00
|
|
|
if !has {
|
|
|
|
panic(fmt.Sprintf("sourceMgr: tried to complete a call that had not registered via run()"))
|
|
|
|
}
|
2016-10-17 06:19:34 +03:00
|
|
|
|
2017-03-28 14:36:39 +03:00
|
|
|
if existingInfo.count > 1 {
|
|
|
|
// If more than one is pending, don't stop the clock yet.
|
|
|
|
existingInfo.count--
|
2017-03-31 19:56:30 +03:00
|
|
|
sup.running[ci] = existingInfo
|
2017-03-28 14:36:39 +03:00
|
|
|
} else {
|
|
|
|
// Last one for this particular key; update metrics with info.
|
2017-03-31 19:56:30 +03:00
|
|
|
durCnt := sup.ran[ci.typ]
|
2017-03-28 14:36:39 +03:00
|
|
|
durCnt.count++
|
2017-04-25 20:13:14 +03:00
|
|
|
durCnt.dur += time.Since(existingInfo.start)
|
2017-03-31 19:56:30 +03:00
|
|
|
sup.ran[ci.typ] = durCnt
|
|
|
|
delete(sup.running, ci)
|
|
|
|
|
|
|
|
if len(sup.running) == 0 {
|
|
|
|
// This is the only place where we signal the cond, as it's the only
|
|
|
|
// time that the number of running calls could become zero.
|
|
|
|
sup.cond.Signal()
|
2016-08-14 06:21:03 +03:00
|
|
|
}
|
2017-03-28 14:36:39 +03:00
|
|
|
}
|
2017-03-31 19:56:30 +03:00
|
|
|
sup.mu.Unlock()
|
|
|
|
}
|
2016-08-14 06:21:03 +03:00
|
|
|
|
2017-03-31 19:56:30 +03:00
|
|
|
// wait until all active calls have terminated.
|
|
|
|
//
|
|
|
|
// Assumes something else has already canceled the supervisor via its context.
|
|
|
|
func (sup *supervisor) wait() {
|
|
|
|
sup.cond.L.Lock()
|
|
|
|
for len(sup.running) > 0 {
|
|
|
|
sup.cond.Wait()
|
2016-08-14 06:21:03 +03:00
|
|
|
}
|
2017-03-31 19:56:30 +03:00
|
|
|
sup.cond.L.Unlock()
|
2017-03-28 14:36:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type callType uint
|
|
|
|
|
|
|
|
const (
|
|
|
|
ctHTTPMetadata callType = iota
|
|
|
|
ctListVersions
|
|
|
|
ctGetManifestAndLock
|
2017-03-31 07:00:00 +03:00
|
|
|
ctListPackages
|
|
|
|
ctSourcePing
|
|
|
|
ctSourceInit
|
|
|
|
ctSourceFetch
|
|
|
|
ctExportTree
|
2017-03-28 14:36:39 +03:00
|
|
|
)
|
2016-08-14 06:21:03 +03:00
|
|
|
|
2017-03-28 14:36:39 +03:00
|
|
|
// callInfo provides metadata about an ongoing call.
|
|
|
|
type callInfo struct {
|
|
|
|
name string
|
|
|
|
typ callType
|
2016-08-14 06:21:03 +03:00
|
|
|
}
|