oauthutil: add Shutdown method

Before this change, calling the `oauthutil.NewRenew` func may
cause goroutine leaks.

This change adds a `Shutdown` method to allow the caller to exit
the goroutine to avoid leaks.

Signed-off-by: rkonfj <rkonfj@gmail.com>
This commit is contained in:
rkonfj 2023-11-30 14:16:22 +08:00 committed by Nick Craig-Wood
parent 110d07548f
commit 6c58e9976c

View File

@ -1,6 +1,7 @@
package oauthutil package oauthutil
import ( import (
"sync"
"sync/atomic" "sync/atomic"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
@ -8,10 +9,12 @@ import (
// Renew allows tokens to be renewed on expiry if uploads are in progress. // Renew allows tokens to be renewed on expiry if uploads are in progress.
type Renew struct { type Renew struct {
name string // name to use in logs name string // name to use in logs
ts *TokenSource // token source that needs renewing ts *TokenSource // token source that needs renewing
uploads atomic.Int32 // number of uploads in progress uploads atomic.Int32 // number of uploads in progress
run func() error // a transaction to run to renew the token on run func() error // a transaction to run to renew the token on
done chan any // channel to end the go routine
shutdown sync.Once
} }
// NewRenew creates a new Renew struct and starts a background process // NewRenew creates a new Renew struct and starts a background process
@ -24,6 +27,7 @@ func NewRenew(name string, ts *TokenSource, run func() error) *Renew {
name: name, name: name,
ts: ts, ts: ts,
run: run, run: run,
done: make(chan any),
} }
go r.renewOnExpiry() go r.renewOnExpiry()
return r return r
@ -36,7 +40,11 @@ func NewRenew(name string, ts *TokenSource, run func() error) *Renew {
func (r *Renew) renewOnExpiry() { func (r *Renew) renewOnExpiry() {
expiry := r.ts.OnExpiry() expiry := r.ts.OnExpiry()
for { for {
<-expiry select {
case <-expiry:
case <-r.done:
return
}
uploads := r.uploads.Load() uploads := r.uploads.Load()
if uploads != 0 { if uploads != 0 {
fs.Debugf(r.name, "Token expired - %d uploads in progress - refreshing", uploads) fs.Debugf(r.name, "Token expired - %d uploads in progress - refreshing", uploads)
@ -72,3 +80,15 @@ func (r *Renew) Invalidate() {
func (r *Renew) Expire() error { func (r *Renew) Expire() error {
return r.ts.Expire() return r.ts.Expire()
} }
// Shutdown stops the timer and no more renewal will take place.
func (r *Renew) Shutdown() {
if r == nil {
return
}
// closing a channel can only be done once
r.shutdown.Do(func() {
r.ts.expiryTimer.Stop()
close(r.done)
})
}