Checks for closed/uninitialized context and devices. (#93)

This commit is contained in:
Sebastian Zagrodzki
2021-01-15 17:25:29 +01:00
committed by GitHub
parent c9efe54672
commit 0eba1b1264
3 changed files with 51 additions and 10 deletions

View File

@@ -209,7 +209,7 @@ func (d *Device) Close() error {
if d.claimed != nil { if d.claimed != nil {
return fmt.Errorf("can't release the device %s, it has an open config %d", d, d.claimed.Desc.Number) return fmt.Errorf("can't release the device %s, it has an open config %d", d, d.claimed.Desc.Number)
} }
d.ctx.libusb.close(d.handle) d.ctx.closeDev(d)
d.handle = nil d.handle = nil
return nil return nil
} }

48
usb.go
View File

@@ -125,11 +125,20 @@ see the excellent "USB in a nutshell" guide: http://www.beyondlogic.org/usbnutsh
*/ */
package gousb package gousb
import (
"errors"
"fmt"
"sync"
)
// Context manages all resources related to USB device handling. // Context manages all resources related to USB device handling.
type Context struct { type Context struct {
ctx *libusbContext ctx *libusbContext
done chan struct{} done chan struct{}
libusb libusbIntf libusb libusbIntf
mu sync.Mutex
devices map[*Device]bool
} }
// Debug changes the debug level. Level 0 means no debug, higher levels // Debug changes the debug level. Level 0 means no debug, higher levels
@@ -149,6 +158,7 @@ func newContextWithImpl(impl libusbIntf) *Context {
ctx: c, ctx: c,
done: make(chan struct{}), done: make(chan struct{}),
libusb: impl, libusb: impl,
devices: make(map[*Device]bool),
} }
go impl.handleEvents(ctx.ctx, ctx.done) go impl.handleEvents(ctx.ctx, ctx.done)
return ctx return ctx
@@ -165,6 +175,9 @@ func NewContext() *Context {
// If there are any errors enumerating the devices, // If there are any errors enumerating the devices,
// the final one is returned along with any successfully opened devices. // the final one is returned along with any successfully opened devices.
func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, error) { func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, error) {
if c.ctx == nil {
return nil, errors.New("OpenDevices called on a closed or uninitialized Context")
}
list, err := c.libusb.getDevices(c.ctx) list, err := c.libusb.getDevices(c.ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -187,7 +200,11 @@ func (c *Context) OpenDevices(opener func(desc *DeviceDesc) bool) ([]*Device, er
reterr = err reterr = err
continue continue
} }
ret = append(ret, &Device{handle: handle, ctx: c, Desc: desc}) o := &Device{handle: handle, ctx: c, Desc: desc}
ret = append(ret, o)
c.mu.Lock()
c.devices[o] = true
c.mu.Unlock()
} else { } else {
c.libusb.dereference(dev) c.libusb.dereference(dev)
} }
@@ -219,13 +236,32 @@ func (c *Context) OpenDeviceWithVIDPID(vid, pid ID) (*Device, error) {
return devs[0], nil return devs[0], nil
} }
func (c *Context) closeDev(d *Device) {
c.mu.Lock()
defer c.mu.Unlock()
c.libusb.close(d.handle)
delete(c.devices, d)
}
func (c *Context) checkOpenDevs() error {
c.mu.Lock()
defer c.mu.Unlock()
if l := len(c.devices); l > 0 {
return fmt.Errorf("Context.Close called while %d Devices are still open, Close may be called only after all previously opened devices were successfuly closed.", l)
}
return nil
}
// Close releases the Context and all associated resources. // Close releases the Context and all associated resources.
func (c *Context) Close() error { func (c *Context) Close() error {
var ret error if c.ctx == nil {
return nil
}
if err := c.checkOpenDevs(); err != nil {
return err
}
c.done <- struct{}{} c.done <- struct{}{}
if c.ctx != nil { err := c.libusb.exit(c.ctx)
ret = c.libusb.exit(c.ctx)
}
c.ctx = nil c.ctx = nil
return ret return err
} }

View File

@@ -41,6 +41,11 @@ func TestOPenDevices(t *testing.T) {
t.Fatalf("OpenDevices(): %s", err) t.Fatalf("OpenDevices(): %s", err)
} }
// attempt to Close() should fail because of open devices
if err := c.Close(); err == nil {
t.Fatal("Context.Close succeeded while some devices were still open")
}
if got, want := len(devs), len(fakeDevices); got != want { if got, want := len(devs), len(fakeDevices); got != want {
t.Fatalf("len(devs) = %d, want %d (based on num fake devs)", got, want) t.Fatalf("len(devs) = %d, want %d (based on num fake devs)", got, want)
} }