Add comments documenting gousb lifecycle management
Explain why we use gousb directly instead of antgo/driver/usb (libusb context leak on error paths), document the defer cleanup ordering, and note that errors are returned for graceful retry.
This commit is contained in:
parent
9efdfc9b83
commit
8a4457119a
@ -44,6 +44,11 @@ func resetAndWait(drv io.ReadWriter) error {
|
|||||||
func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32,
|
func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32,
|
||||||
events chan<- Event) error {
|
events chan<- Event) error {
|
||||||
|
|
||||||
|
// Use gousb directly instead of antgo/driver/usb to manage context
|
||||||
|
// lifecycle explicitly. The old code created a new gousb.Context on
|
||||||
|
// every call via libusb_init and leaked it on error paths (device not
|
||||||
|
// found, interface claim failure, etc.), which triggered a libusb
|
||||||
|
// assertion after repeated ListenForTrainer calls.
|
||||||
gctx := gousb.NewContext()
|
gctx := gousb.NewContext()
|
||||||
defer gctx.Close()
|
defer gctx.Close()
|
||||||
|
|
||||||
@ -60,6 +65,9 @@ func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ant: claim interface: %w", err)
|
return fmt.Errorf("ant: claim interface: %w", err)
|
||||||
}
|
}
|
||||||
|
// Cleanup is ordered by defer stack: done() releases interface,
|
||||||
|
// dev.Close() closes device, gctx.Close() closes context, cancel()
|
||||||
|
// cancels the ANT message goroutine.
|
||||||
defer done()
|
defer done()
|
||||||
|
|
||||||
out, err := intf.OutEndpoint(1)
|
out, err := intf.OutEndpoint(1)
|
||||||
@ -75,6 +83,8 @@ func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32,
|
|||||||
|
|
||||||
logMsg("ant: device opened")
|
logMsg("ant: device opened")
|
||||||
|
|
||||||
|
// Errors are returned (not panicked) so the caller can retry
|
||||||
|
// gracefully instead of requiring a full process restart.
|
||||||
if err := resetAndWait(drv); err != nil {
|
if err := resetAndWait(drv); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -85,6 +95,8 @@ func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32,
|
|||||||
|
|
||||||
msgs := make(chan ant.BroadcastMessage, 100)
|
msgs := make(chan ant.BroadcastMessage, 100)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
// cancel is the final defer in the stack, ensuring the ANT message
|
||||||
|
// goroutine is torn down after all USB resources are released.
|
||||||
defer cancel()
|
defer cancel()
|
||||||
go func() {
|
go func() {
|
||||||
device.DumpBroadcastMessages(ctx, drv, msgs)
|
device.DumpBroadcastMessages(ctx, drv, msgs)
|
||||||
@ -124,6 +136,8 @@ func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32,
|
|||||||
}
|
}
|
||||||
case <-time.After(10 * time.Second):
|
case <-time.After(10 * time.Second):
|
||||||
logMsg("ant: no more messages arrived, restarting")
|
logMsg("ant: no more messages arrived, restarting")
|
||||||
|
// Return without error so the caller can retry gracefully
|
||||||
|
// rather than panicking and requiring a full restart.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user