From 550fe8d1f20bb2d88ba8f6307112ac3dac6103a3 Mon Sep 17 00:00:00 2001 From: Sebastian Zagrodzki Date: Wed, 26 Apr 2017 21:32:49 +0200 Subject: [PATCH] Introduce xferMap, avoid passing Go pointer into the C code. Weirdness with memory barriers. --- usb/libusb.go | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/usb/libusb.go b/usb/libusb.go index fb4e2b7..9249896 100644 --- a/usb/libusb.go +++ b/usb/libusb.go @@ -18,6 +18,7 @@ import ( "fmt" "log" "reflect" + "sync" "time" "unsafe" ) @@ -138,9 +139,9 @@ type libusbIntf interface { setAlt(*libusbDevHandle, uint8, uint8) error // transfer - alloc(*libusbDevHandle, *EndpointInfo, time.Duration, int, []byte) (*libusbTransfer, error) + alloc(*libusbDevHandle, *EndpointInfo, time.Duration, int, []byte, chan struct{}) (*libusbTransfer, error) cancel(*libusbTransfer) error - submit(*libusbTransfer, chan struct{}) error + submit(*libusbTransfer) error data(*libusbTransfer) (int, TransferStatus) free(*libusbTransfer) setIsoPacketLengths(*libusbTransfer, uint32) @@ -371,7 +372,7 @@ func (libusbImpl) setAlt(d *libusbDevHandle, iface, setup uint8) error { return fromErrNo(C.libusb_set_interface_alt_setting((*C.libusb_device_handle)(d), C.int(iface), C.int(setup))) } -func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointInfo, timeout time.Duration, isoPackets int, buf []byte) (*libusbTransfer, error) { +func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointInfo, timeout time.Duration, isoPackets int, buf []byte, done chan struct{}) (*libusbTransfer, error) { xfer := C.libusb_alloc_transfer(C.int(isoPackets)) if xfer == nil { return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets) @@ -381,17 +382,20 @@ func (libusbImpl) alloc(d *libusbDevHandle, ep *EndpointInfo, timeout time.Durat xfer.timeout = C.uint(timeout / time.Millisecond) xfer._type = C.uchar(ep.TransferType) xfer.num_iso_packets = C.int(isoPackets) - xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0])) + xfer.buffer = (*C.uchar)(unsafe.Pointer(&buf[0])) xfer.length = C.int(len(buf)) - return (*libusbTransfer)(xfer), nil + ret := (*libusbTransfer)(xfer) + xferDoneMap.Lock() + xferDoneMap.m[ret] = done + xferDoneMap.Unlock() + return ret, nil } func (libusbImpl) cancel(t *libusbTransfer) error { return fromErrNo(C.libusb_cancel_transfer((*C.struct_libusb_transfer)(t))) } -func (libusbImpl) submit(t *libusbTransfer, done chan struct{}) error { - t.user_data = (unsafe.Pointer)(&done) +func (libusbImpl) submit(t *libusbTransfer) error { return fromErrNo(C.submit((*C.struct_libusb_transfer)(t))) } @@ -406,6 +410,9 @@ func (libusbImpl) data(t *libusbTransfer) (int, TransferStatus) { func (libusbImpl) free(t *libusbTransfer) { C.libusb_free_transfer((*C.struct_libusb_transfer)(t)) + xferDoneMap.Lock() + delete(xferDoneMap.m, t) + xferDoneMap.Unlock() } func (libusbImpl) setIsoPacketLengths(t *libusbTransfer, length uint32) { @@ -420,10 +427,20 @@ var ( libusbIsoOffset = unsafe.Offsetof(C.struct_libusb_transfer{}.iso_packet_desc) ) +// xferDoneMap keeps a map of done callback channels for all allocated transfers. +var xferDoneMap = struct { + m map[*libusbTransfer]chan struct{} + sync.RWMutex +}{ + m: make(map[*libusbTransfer]chan struct{}), +} + //export xferCallback -func xferCallback(cptr unsafe.Pointer) { - ch := *(*chan struct{})(cptr) - close(ch) +func xferCallback(xfer *C.struct_libusb_transfer) { + xferDoneMap.RLock() + ch := xferDoneMap.m[(*libusbTransfer)(xfer)] + xferDoneMap.RUnlock() + ch <- struct{}{} } // for benchmarking and testing