device: fix data race in peer.timersActive

Found by the race detector and existing tests.

To avoid introducing a lock into this hot path,
calculate and cache whether any peers exist.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
This commit is contained in:
Josh Bleecher Snyder 2020-12-15 17:44:21 -08:00 committed by Jason A. Donenfeld
parent 70861686d3
commit f7bbdc31a0
3 changed files with 6 additions and 3 deletions

View File

@ -49,8 +49,9 @@ type Device struct {
} }
peers struct { peers struct {
sync.RWMutex empty AtomicBool // empty reports whether len(keyMap) == 0
keyMap map[NoisePublicKey]*Peer sync.RWMutex // protects keyMap
keyMap map[NoisePublicKey]*Peer
} }
// unprotected / "self-synchronising resources" // unprotected / "self-synchronising resources"
@ -129,6 +130,7 @@ func unsafeRemovePeer(device *Device, peer *Peer, key NoisePublicKey) {
// remove from peer map // remove from peer map
delete(device.peers.keyMap, key) delete(device.peers.keyMap, key)
device.peers.empty.Set(len(device.peers.keyMap) == 0)
} }
func deviceUpdateState(device *Device) { func deviceUpdateState(device *Device) {

View File

@ -125,6 +125,7 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
// add // add
device.peers.keyMap[pk] = peer device.peers.keyMap[pk] = peer
device.peers.empty.Set(false)
// start peer // start peer

View File

@ -73,7 +73,7 @@ func (timer *Timer) IsPending() bool {
} }
func (peer *Peer) timersActive() bool { func (peer *Peer) timersActive() bool {
return peer.isRunning.Get() && peer.device != nil && peer.device.isUp.Get() && len(peer.device.peers.keyMap) > 0 return peer.isRunning.Get() && peer.device != nil && peer.device.isUp.Get() && !peer.device.peers.empty.Get()
} }
func expiredRetransmitHandshake(peer *Peer) { func expiredRetransmitHandshake(peer *Peer) {