diff --git a/backend/backend.go b/backend/backend.go index b04554519f..bf91822825 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -5,6 +5,7 @@ package backend import ( "encoding/hex" "fmt" + "maps" "math/big" "net/http" "net/url" @@ -213,7 +214,8 @@ type Backend struct { notifier *Notifier - devices map[string]device.Interface + devices map[string]device.Interface + devicesLock locker.Locker usbManager *usb.Manager bluetooth *bluetooth.Bluetooth @@ -717,7 +719,8 @@ func (backend *Backend) UsbUpdate() { // DevicesRegistered returns a map of device IDs to device of registered devices. func (backend *Backend) DevicesRegistered() map[string]device.Interface { - return backend.devices + defer backend.devicesLock.RLock()() + return maps.Clone(backend.devices) } // HTTPClient is a getter method for the HTTPClient instance. @@ -843,9 +846,11 @@ func (backend *Backend) DeregisterKeystore() { // Register registers the given device at this backend. func (backend *Backend) Register(theDevice device.Interface) error { + unlock := backend.devicesLock.Lock() backend.devices[theDevice.Identifier()] = theDevice - mainKeystore := len(backend.devices) == 1 + unlock() + theDevice.SetOnEvent(func(event deviceevent.Event, data interface{}) { backend.events <- deviceEvent{ DeviceID: theDevice.Identifier(), @@ -899,9 +904,15 @@ func (backend *Backend) Register(theDevice device.Interface) error { // Deregister deregisters the device with the given ID from this backend. func (backend *Backend) Deregister(deviceID string) { - if device, ok := backend.devices[deviceID]; ok { - backend.onDeviceUninit(deviceID) + unlock := backend.devicesLock.Lock() + device, ok := backend.devices[deviceID] + if ok { delete(backend.devices, deviceID) + } + unlock() + + if ok { + backend.onDeviceUninit(deviceID) backend.DeregisterKeystore() backend.Notify(observable.Event{ diff --git a/backend/backend_test.go b/backend/backend_test.go index 983af5227e..601f688179 100644 --- a/backend/backend_test.go +++ b/backend/backend_test.go @@ -321,6 +321,20 @@ func newBackend(t *testing.T, testing, regtest bool) *Backend { return b } +func TestDevicesRegisteredReturnsSnapshot(t *testing.T) { + b := newBackend(t, testnetDisabled, regtestDisabled) + defer b.Close() + + unlock := b.devicesLock.Lock() + b.devices["device-id"] = nil + unlock() + + devices := b.DevicesRegistered() + delete(devices, "device-id") + + require.Contains(t, b.DevicesRegistered(), "device-id") +} + func TestRegisterKeystore(t *testing.T) { // From mnemonic: wisdom minute home employ west tail liquid mad deal catalog narrow mistake rootKey1 := test.TstMustXKey("xprv9s21ZrQH143K3gie3VFLgx8JcmqZNsBcBc6vAdJrsf4bPRhx69U8qZe3EYAyvRWyQdEfz7ZpyYtL8jW2d2Lfkfh6g2zivq8JdZPQqxoxLwB")