mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 13:05:46 +00:00
control/controlclient: use less battery when stopped, stop map requests
Updates #604 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
dd97111d06
commit
a275b9d7aa
@ -117,13 +117,15 @@ type Client struct {
|
|||||||
mu sync.Mutex // mutex guards the following fields
|
mu sync.Mutex // mutex guards the following fields
|
||||||
statusFunc func(Status) // called to update Client status
|
statusFunc func(Status) // called to update Client status
|
||||||
|
|
||||||
loggedIn bool // true if currently logged in
|
paused bool // whether we should stop making HTTP requests
|
||||||
loginGoal *LoginGoal // non-nil if some login activity is desired
|
unpauseWaiters []chan struct{}
|
||||||
synced bool // true if our netmap is up-to-date
|
loggedIn bool // true if currently logged in
|
||||||
hostinfo *tailcfg.Hostinfo
|
loginGoal *LoginGoal // non-nil if some login activity is desired
|
||||||
inPollNetMap bool // true if currently running a PollNetMap
|
synced bool // true if our netmap is up-to-date
|
||||||
inSendStatus int // number of sendStatus calls currently in progress
|
hostinfo *tailcfg.Hostinfo
|
||||||
state State
|
inPollNetMap bool // true if currently running a PollNetMap
|
||||||
|
inSendStatus int // number of sendStatus calls currently in progress
|
||||||
|
state State
|
||||||
|
|
||||||
authCtx context.Context // context used for auth requests
|
authCtx context.Context // context used for auth requests
|
||||||
mapCtx context.Context // context used for netmap requests
|
mapCtx context.Context // context used for netmap requests
|
||||||
@ -169,6 +171,27 @@ func NewNoStart(opts Options) (*Client, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPaused controls whether HTTP activity should be paused.
|
||||||
|
//
|
||||||
|
// The client can be paused and unpaused repeatedly, unlike Start and Shutdown, which can only be used once.
|
||||||
|
func (c *Client) SetPaused(paused bool) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if paused == c.paused {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.paused = paused
|
||||||
|
if paused {
|
||||||
|
// Just cancel the map routine. The auth routine isn't expensive.
|
||||||
|
c.cancelMapLocked()
|
||||||
|
} else {
|
||||||
|
for _, ch := range c.unpauseWaiters {
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
c.unpauseWaiters = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start starts the client's goroutines.
|
// Start starts the client's goroutines.
|
||||||
//
|
//
|
||||||
// It should only be called for clients created by NewNoStart.
|
// It should only be called for clients created by NewNoStart.
|
||||||
@ -272,6 +295,7 @@ func (c *Client) authRoutine() {
|
|||||||
if goal == nil {
|
if goal == nil {
|
||||||
// Wait for something interesting to happen
|
// Wait for something interesting to happen
|
||||||
var exp <-chan time.Time
|
var exp <-chan time.Time
|
||||||
|
var expTimer *time.Timer
|
||||||
if expiry != nil && !expiry.IsZero() {
|
if expiry != nil && !expiry.IsZero() {
|
||||||
// if expiry is in the future, don't delay
|
// if expiry is in the future, don't delay
|
||||||
// past that time.
|
// past that time.
|
||||||
@ -284,11 +308,15 @@ func (c *Client) authRoutine() {
|
|||||||
if delay > 5*time.Second {
|
if delay > 5*time.Second {
|
||||||
delay = time.Second
|
delay = time.Second
|
||||||
}
|
}
|
||||||
exp = time.After(delay)
|
expTimer = time.NewTimer(delay)
|
||||||
|
exp = expTimer.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
if expTimer != nil {
|
||||||
|
expTimer.Stop()
|
||||||
|
}
|
||||||
c.logf("authRoutine: context done.")
|
c.logf("authRoutine: context done.")
|
||||||
case <-exp:
|
case <-exp:
|
||||||
// Unfortunately the key expiry isn't provided
|
// Unfortunately the key expiry isn't provided
|
||||||
@ -310,7 +338,7 @@ func (c *Client) authRoutine() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if !goal.wantLoggedIn {
|
} else if !goal.wantLoggedIn {
|
||||||
err := c.direct.TryLogout(c.authCtx)
|
err := c.direct.TryLogout(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
report(err, "TryLogout")
|
report(err, "TryLogout")
|
||||||
bo.BackOff(ctx, err)
|
bo.BackOff(ctx, err)
|
||||||
@ -399,12 +427,35 @@ func (c *Client) Direct() *Direct {
|
|||||||
return c.direct
|
return c.direct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unpausedChanLocked returns a new channel that is closed when the
|
||||||
|
// current Client pause is unpaused.
|
||||||
|
//
|
||||||
|
// c.mu must be held
|
||||||
|
func (c *Client) unpausedChanLocked() <-chan struct{} {
|
||||||
|
unpaused := make(chan struct{})
|
||||||
|
c.unpauseWaiters = append(c.unpauseWaiters, unpaused)
|
||||||
|
return unpaused
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) mapRoutine() {
|
func (c *Client) mapRoutine() {
|
||||||
defer close(c.mapDone)
|
defer close(c.mapDone)
|
||||||
bo := backoff.NewBackoff("mapRoutine", c.logf, 30*time.Second)
|
bo := backoff.NewBackoff("mapRoutine", c.logf, 30*time.Second)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
|
if c.paused {
|
||||||
|
unpaused := c.unpausedChanLocked()
|
||||||
|
c.mu.Unlock()
|
||||||
|
c.logf("mapRoutine: awaiting unpause")
|
||||||
|
select {
|
||||||
|
case <-unpaused:
|
||||||
|
c.logf("mapRoutine: unpaused")
|
||||||
|
case <-c.quit:
|
||||||
|
c.logf("mapRoutine: quit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
c.logf("mapRoutine: %s", c.state)
|
c.logf("mapRoutine: %s", c.state)
|
||||||
loggedIn := c.loggedIn
|
loggedIn := c.loggedIn
|
||||||
ctx := c.mapCtx
|
ctx := c.mapCtx
|
||||||
@ -487,8 +538,14 @@ func (c *Client) mapRoutine() {
|
|||||||
if c.state == StateSynchronized {
|
if c.state == StateSynchronized {
|
||||||
c.state = StateAuthenticated
|
c.state = StateAuthenticated
|
||||||
}
|
}
|
||||||
|
paused := c.paused
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
|
||||||
|
if paused {
|
||||||
|
c.logf("mapRoutine: paused")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
report(err, "PollNetMap")
|
report(err, "PollNetMap")
|
||||||
bo.BackOff(ctx, err)
|
bo.BackOff(ctx, err)
|
||||||
|
@ -1069,6 +1069,7 @@ func (b *LocalBackend) enterState(newState State) {
|
|||||||
b.state = newState
|
b.state = newState
|
||||||
prefs := b.prefs
|
prefs := b.prefs
|
||||||
notify := b.notify
|
notify := b.notify
|
||||||
|
bc := b.c
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
|
||||||
if state == newState {
|
if state == newState {
|
||||||
@ -1080,6 +1081,10 @@ func (b *LocalBackend) enterState(newState State) {
|
|||||||
b.send(Notify{State: &newState})
|
b.send(Notify{State: &newState})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bc != nil {
|
||||||
|
bc.SetPaused(newState == Stopped)
|
||||||
|
}
|
||||||
|
|
||||||
switch newState {
|
switch newState {
|
||||||
case NeedsLogin:
|
case NeedsLogin:
|
||||||
b.blockEngineUpdates(true)
|
b.blockEngineUpdates(true)
|
||||||
|
Loading…
Reference in New Issue
Block a user