Author of the go netlink library here. I've run into this issue a number of times. There has been conversation in the past about adding some kind of new runtime command like LockOSThread to prevent new threads from being spawned, but it didn't gain any momentum.<p>Even though I am a big fan of go, I've personally built two container runtimes in other languages do to the namespace clumsiness.<p>Personally, I think rust is an excellent alternative for namespace utilities.<p>EDIT: there is more information and links in the issue in the netns library: <a href="https://github.com/vishvananda/netns/issues/17" rel="nofollow">https://github.com/vishvananda/netns/issues/17</a>
I think the proper title is: Linux Process and Threads Don't Mix.<p>The Linux syscall interface exposes certain functionalities that are much more easy to reason about at the process level such as namespaces, capabilities, seteuid and so on. However these syscalls all operate on the thread level (since the kernel treats threads pretty similarly to processes). Therefore in order to perform these operations safely you need some sort of process wide mechanism to apply the operation on every thread (and don't forget error handling!)<p>This is _not_ just a golang problem or an M:N threading problem as many comments suggest. The kernel really needs to provide new syscalls for these features that operate at the process / thread-group level. The current syscalls are extremely difficult to use correctly in any multithreaded context in any language. When you consider the security implications of these features it makes the problem even worse.<p>Check out <a href="https://ewontfix.com/17/" rel="nofollow">https://ewontfix.com/17/</a> for a really good analysis of the difficulty musl libc has faced making a multi-thread safe seteuid on Linux. There are also many bugs in glibc related to this as well. Linux makes userspace responsible for patching up the leaks in the kernel's process abstraction and that's really not a job that userspace is in the right position to take on.
This leads to go code being roughly as messy/clumsy as C (or whatever else) code in the sections that need concurrency and also need to change namespace. That's unfortunate, but I don't know that it really "raises a few eyebrows".<p>I mean, what's the better alternative to Go for this work? Maybe Rust? It is, at least more controllable at a lower level...but, not as easy to pick up for people coming from a C/Python/Perl/Ruby systems and ops background. I'm not saying one <i>should</i> use Go for containers/namespaces programming, but a lot of people are with some success (probably also banging into the namespaces issue now and then), I'm just saying it's not obvious to me what the better alternative would be.
I've ran into this problem before. IMO this issue has nothing to do with go and the solution is straightforward.
Simply create a sub-process whenever entering a new namespace because the operation isn't concurrency safe within a process.<p>Note that you'd run into this bug within any multithreaded process, whether the code was written in go, Java, c, or whatever.
This is discouraging considering go was initially designed as a systems programming language. I wonder if there is another way for go to handle blocking syscall such that this use case would become reliable.