在“ LockOSThread” GoRoutine中调用函数

I'm writing a package to control a Canon DSLR using their EDSDK DLL from Go.

This is a personal project for a photo booth to use at our wedding at my partners request, which I'll be happy to post on GitHub when complete :).

Looking at the examples of using the SDK elsewhere, it isn't threadsafe and uses thread-local resources, so I'll need to make sure I'm calling it from a single thread during usage. While not ideal, it looks like Go provides a "runtime.LockOSThread" function for doing just that, although this does get called by the core DLL interop code itself, so I'll have to wait and find out if that interferes or not.

I want the rest of the application to be able to call the SDK using a higher level interface without worrying about the threading, so I need a way to pass function call requests to the locked thread/Goroutine to execute there, then pass the results back to the calling function outside of that Goroutine.

So far, I've come up with this working example of using very broad function definitions using []interface{} arrays and passing back and forward via channels. This would take a lot of mangling of input/output data on every call to do type assertions back out of the interface{} array, even if we know what we should expect for each function ahead of time, but it looks like it'll work.

Before I invest a lot of time doing it this way for possibly the worst way to do it - does anyone have any better options?

package edsdk

import (
    "fmt"
    "runtime"
)

type CanonSDK struct {
    FChan           chan functionCall
}

type functionCall struct {
    Function        func([]interface{}) []interface{}
    Arguments       []interface{}
    Return          chan []interface{}
}

func NewCanonSDK() (*CanonSDK, error) {
    c := &CanonSDK {
        FChan:  make(chan functionCall),
    }

    go c.BackgroundThread(c.FChan)

    return c, nil
}

func (c *CanonSDK) BackgroundThread(fcalls <-chan functionCall) {
    runtime.LockOSThread()
    for f := range fcalls {
        f.Return <- f.Function(f.Arguments)
    }
    runtime.UnlockOSThread()
}

func (c *CanonSDK) TestCall() {
    ret := make(chan []interface{})

    f := functionCall {
        Function: c.DoTestCall,
        Arguments: []interface{}{},
        Return: ret,
    }

    c.FChan <- f

    results := <- ret
    close(ret)

    fmt.Printf("%#v", results)
}

func (c *CanonSDK) DoTestCall([]interface{}) []interface{} {
    return []interface{}{ "Test", nil }
}

For similar embedded projects I've played with, I tend to create a single goroutine worker that listens on a channel to perform all the work over that USB device. And any results sent back out on another channel.

Talk to the device with channels only in Go in a one-way exchange. LIsten for responses from the other channel.

Since USB is serial and polling, I had to setup a dedicated channel with another goroutine that justs picks items off the channel when they were pushed into it from the worker goroutine that just looped.