diff --git a/ipn/ipnlocal/drive.go b/ipn/ipnlocal/drive.go index 02d3f8d70..c5380d9af 100644 --- a/ipn/ipnlocal/drive.go +++ b/ipn/ipnlocal/drive.go @@ -247,8 +247,17 @@ func (b *LocalBackend) driveSetSharesLocked(shares []*drive.Share) error { } // driveNotifyShares notifies IPN bus listeners (e.g. Mac Application process) -// about the latest list of shares. +// about the latest list of shares, if and only if the shares have changed since +// the last time we notified. func (b *LocalBackend) driveNotifyShares(shares views.SliceView[*drive.Share, drive.ShareView]) { + b.lastNotifiedDriveSharesMu.Lock() + defer b.lastNotifiedDriveSharesMu.Unlock() + if b.lastNotifiedDriveShares != nil && driveShareViewsEqual(b.lastNotifiedDriveShares, shares) { + // shares are unchanged since last notification, don't bother notifying + return + } + b.lastNotifiedDriveShares = &shares + // Ensures shares is not nil to distinguish "no shares" from "not notifying shares" if shares.IsNil() { shares = views.SliceOfViews(make([]*drive.Share, 0)) @@ -265,11 +274,8 @@ func (b *LocalBackend) driveNotifyCurrentSharesLocked() { shares = b.pm.prefs.DriveShares() } - lastNotified := b.lastNotifiedDriveShares.Load() - if lastNotified == nil || !driveShareViewsEqual(lastNotified, shares) { - // Do the below on a goroutine to avoid deadlocking on b.mu in b.send(). - go b.driveNotifyShares(shares) - } + // Do the below on a goroutine to avoid deadlocking on b.mu in b.send(). + go b.driveNotifyShares(shares) } func driveShareViewsEqual(a *views.SliceView[*drive.Share, drive.ShareView], b views.SliceView[*drive.Share, drive.ShareView]) bool { diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 6d3f40f20..2b268c766 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -332,9 +332,12 @@ type LocalBackend struct { // Last ClientVersion received in MapResponse, guarded by mu. lastClientVersion *tailcfg.ClientVersion + // lastNotifiedDriveSharesMu guards lastNotifiedDriveShares + lastNotifiedDriveSharesMu sync.Mutex + // lastNotifiedDriveShares keeps track of the last set of shares that we // notified about. - lastNotifiedDriveShares atomic.Pointer[views.SliceView[*drive.Share, drive.ShareView]] + lastNotifiedDriveShares *views.SliceView[*drive.Share, drive.ShareView] // outgoingFiles keeps track of Taildrop outgoing files keyed to their OutgoingFile.ID outgoingFiles map[string]*ipn.OutgoingFile