runtime.LockOSThread() does exactly that [1].
Trust me, we've been trying to get Go to co-operate with containers for quite a few years. It's not as simple as just reading the standard library docs. ;)
That sounds like an implementation issue, why not assume the documentation is the intended behavior and this side effect is a bug? I'd support a CL to fix this behavior or add a block-clone-from-here runtime call (but the end result of that is you want the thread to exit when you're done with it, and not to go back into the pool... which is also new behavior). At the minimum, this behavior of new threads spawning from LockOSThread could be documented.
As a workaround, what about CGO -> pthreads -> spawn a control thread free from the Go scheduler -> call back into Golang to run a control loop function? You can do this in init() to ensure it has full control over itself. Or will Golang call clone() from unscheduled code?
Because after discussion with the Go devs they've concluded it's not a bug. To be fair, it's their decision to make a language runtime hostile to user's being able to mess with the process model, it just makes programs hard to write.
> At the minimum, this behavior of new threads spawning from LockOSThread could be documented.
The thing is it is documented[1], it's just very subtle:
> LockOSThread wires the calling goroutine to its current operating system thread. Until the calling goroutine exits or calls UnlockOSThread, it will always execute in that thread, and no other goroutine can.
An implication of the emphasised part is that if you use 'go' (or a function you call uses 'go') inside a locked goroutine, you are guaranteed that goroutine will be scheduled on another thread. Which is not what you might want or expect (personally I would expect goroutines created from a locked goroutine to act as normal coroutines). The problem is made much worse because a lot of the Go standard library uses 'go' internally and there's no way for you to know what functions use it and what functions don't (and what functions might use it in future versions). Not to mention that there are even more edge cases where functions you call could end up on separate OS threads.
> As a workaround, what about CGO -> pthreads -> spawn a control thread free from the Go scheduler -> call back into Golang to run a control loop function?
At that point you're really just massively hacking around the Go runtime. I would not be confident that such hacks would be a good idea in the long run. Remember that Go doesn't have any real forking model in its standard library or language, so the language provides no guarantees that it has to "play nice" with foreign threads.
Also, calling from foreign C code into Go is quite difficult (especially if you're calling into an _already running Go program_ which might reschedule your code at any time and would require hooking into core runtime internals).
> You can do this in init() to ensure it has full control over itself.
init() runs after the Go runtime starts up, you would want to do it as an __attribute__((constructor)) in C code so it is started before the Go runtime.