Skip to content

Adapter.Connect is not thread-safe on Mac OS #57

@erik

Description

@erik

When running adapter.Connect calls concurrently, I've noticed that connection attempts to a non-existent address sometimes successfully return with the data of other devices.

Minimal repro:

adapter := bluetooth.DefaultAdapter
wg := sync.WaitGroup()

retryConnect := func(addr bluetooth.Addresser, device *bluetooth.Device) {
  wg.Add(1)
  // Keep retrying until we successfully connect
  for {
    if d, err := adapter.Connect(addr, bluetooth.ConnectionParams{}); err == nil {
        *device = d
        break
    }
    println("Retrying connection to", addr)  
  }
  wg.Done()
}

var dev1, dev2 bluetooth.Device

go retryConnection("addr1", &dev1)
go retryConnection("addr2", &dev2)

wg.Wait()

// At this point, dev1 and dev2 may be swapped or the same device.

In the Mac OS implementation of Adapter, newly connected devices are sent over a channel which is shared between all callers (Adapter.connectChan). In Adapter.Connect, we then unconditionally take the first device sent across this channel and return it.

This can lead to a data race in the case where we have multiple goroutines connecting to different devices at once, resulting in adapter.Connect returning successfully with the wrong device.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions