[client, android] Fix/notifier threading (#3807)

- Fix potential deadlocks
- When adding a listener, immediately notify with the last known IP and fqdn.
This commit is contained in:
Zoltan Papp 2025-05-27 17:12:04 +02:00 committed by GitHub
parent 6f436e57b5
commit 0492c1724a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -18,6 +18,8 @@ type notifier struct {
currentClientState bool currentClientState bool
lastNotification int lastNotification int
lastNumberOfPeers int lastNumberOfPeers int
lastFqdnAddress string
lastIPAddress string
} }
func newNotifier() *notifier { func newNotifier() *notifier {
@ -25,15 +27,22 @@ func newNotifier() *notifier {
} }
func (n *notifier) setListener(listener Listener) { func (n *notifier) setListener(listener Listener) {
n.serverStateLock.Lock()
lastNotification := n.lastNotification
numOfPeers := n.lastNumberOfPeers
fqdnAddress := n.lastFqdnAddress
address := n.lastIPAddress
n.serverStateLock.Unlock()
n.listenersLock.Lock() n.listenersLock.Lock()
defer n.listenersLock.Unlock() defer n.listenersLock.Unlock()
n.serverStateLock.Lock()
n.notifyListener(listener, n.lastNotification)
listener.OnPeersListChanged(n.lastNumberOfPeers)
n.serverStateLock.Unlock()
n.listener = listener n.listener = listener
listener.OnAddressChanged(fqdnAddress, address)
notifyListener(listener, lastNotification)
// run on go routine to avoid on Java layer to call go functions on same thread
go listener.OnPeersListChanged(numOfPeers)
} }
func (n *notifier) removeListener() { func (n *notifier) removeListener() {
@ -44,41 +53,44 @@ func (n *notifier) removeListener() {
func (n *notifier) updateServerStates(mgmState bool, signalState bool) { func (n *notifier) updateServerStates(mgmState bool, signalState bool) {
n.serverStateLock.Lock() n.serverStateLock.Lock()
defer n.serverStateLock.Unlock()
calculatedState := n.calculateState(mgmState, signalState) calculatedState := n.calculateState(mgmState, signalState)
if !n.isServerStateChanged(calculatedState) { if !n.isServerStateChanged(calculatedState) {
n.serverStateLock.Unlock()
return return
} }
n.lastNotification = calculatedState n.lastNotification = calculatedState
n.serverStateLock.Unlock()
n.notify(n.lastNotification) n.notify(calculatedState)
} }
func (n *notifier) clientStart() { func (n *notifier) clientStart() {
n.serverStateLock.Lock() n.serverStateLock.Lock()
defer n.serverStateLock.Unlock()
n.currentClientState = true n.currentClientState = true
n.lastNotification = stateConnecting n.lastNotification = stateConnecting
n.notify(n.lastNotification) n.serverStateLock.Unlock()
n.notify(stateConnecting)
} }
func (n *notifier) clientStop() { func (n *notifier) clientStop() {
n.serverStateLock.Lock() n.serverStateLock.Lock()
defer n.serverStateLock.Unlock()
n.currentClientState = false n.currentClientState = false
n.lastNotification = stateDisconnected n.lastNotification = stateDisconnected
n.notify(n.lastNotification) n.serverStateLock.Unlock()
n.notify(stateDisconnected)
} }
func (n *notifier) clientTearDown() { func (n *notifier) clientTearDown() {
n.serverStateLock.Lock() n.serverStateLock.Lock()
defer n.serverStateLock.Unlock()
n.currentClientState = false n.currentClientState = false
n.lastNotification = stateDisconnecting n.lastNotification = stateDisconnecting
n.notify(n.lastNotification) n.serverStateLock.Unlock()
n.notify(stateDisconnecting)
} }
func (n *notifier) isServerStateChanged(newState int) bool { func (n *notifier) isServerStateChanged(newState int) bool {
@ -87,26 +99,14 @@ func (n *notifier) isServerStateChanged(newState int) bool {
func (n *notifier) notify(state int) { func (n *notifier) notify(state int) {
n.listenersLock.Lock() n.listenersLock.Lock()
defer n.listenersLock.Unlock() listener := n.listener
if n.listener == nil { n.listenersLock.Unlock()
if listener == nil {
return return
} }
n.notifyListener(n.listener, state)
}
func (n *notifier) notifyListener(l Listener, state int) { notifyListener(listener, state)
go func() {
switch state {
case stateDisconnected:
l.OnDisconnected()
case stateConnected:
l.OnConnected()
case stateConnecting:
l.OnConnecting()
case stateDisconnecting:
l.OnDisconnecting()
}
}()
} }
func (n *notifier) calculateState(managementConn, signalConn bool) int { func (n *notifier) calculateState(managementConn, signalConn bool) int {
@ -126,20 +126,48 @@ func (n *notifier) calculateState(managementConn, signalConn bool) int {
} }
func (n *notifier) peerListChanged(numOfPeers int) { func (n *notifier) peerListChanged(numOfPeers int) {
n.serverStateLock.Lock()
n.lastNumberOfPeers = numOfPeers n.lastNumberOfPeers = numOfPeers
n.serverStateLock.Unlock()
n.listenersLock.Lock() n.listenersLock.Lock()
defer n.listenersLock.Unlock() listener := n.listener
if n.listener == nil { n.listenersLock.Unlock()
if listener == nil {
return return
} }
n.listener.OnPeersListChanged(numOfPeers)
// run on go routine to avoid on Java layer to call go functions on same thread
go listener.OnPeersListChanged(numOfPeers)
} }
func (n *notifier) localAddressChanged(fqdn, address string) { func (n *notifier) localAddressChanged(fqdn, address string) {
n.serverStateLock.Lock()
n.lastFqdnAddress = fqdn
n.lastIPAddress = address
n.serverStateLock.Unlock()
n.listenersLock.Lock() n.listenersLock.Lock()
defer n.listenersLock.Unlock() listener := n.listener
if n.listener == nil { n.listenersLock.Unlock()
if listener == nil {
return return
} }
n.listener.OnAddressChanged(fqdn, address)
listener.OnAddressChanged(fqdn, address)
}
func notifyListener(l Listener, state int) {
switch state {
case stateDisconnected:
l.OnDisconnected()
case stateConnected:
l.OnConnected()
case stateConnecting:
l.OnConnecting()
case stateDisconnecting:
l.OnDisconnecting()
}
} }