I really don't think this is a language design issue but clearly if you want absolute control you can't get that with the Go run-time.
It's true the Go runtime could give users more control over goroutine and thread scheduling but that would kind of defeat the purpose of not needing to know about it and having Goroutines as the only flexible unit of concurrency.
I think the kludge here is on the Linux side. Having some magic properties bestowed upon threads doesn't make sense. The property should be accessible via a handle that can be shared amongst all threads.
I've never needed to have half my process in one namespace and half in another. It's a niche application. Green threads/goroutines is something I use extensively and I wouldn't give it up for the ability to have half my process in another namespace. There's probably some middle ground there in giving Go users more control over which thread pools run which goroutines...
Someone should file a Go bug and see what the response is...
From memory, several people working on Docker have done so over the years. It's still a problem because the only "real" solutions are:
1. Do what glibc does and implement nptl(7) (effectively a way to make Linux threads look like POSIX threads by synchronising certain operations on all threads). This would require making first-class library APIs for Linux features (outside of the wild-west that is syscall).
2. Give programs far more control over threading, which would require making runtime.LockOSThread and GOMAXPROCS actually do what their documentation says. However, that would restrict their ability to be opinionated about threading (and would almost certainly cause deadlocks in some programs) so I understand why they don't want to do this either.
It's not exactly the same issue, but the themes are all the same.
This isn't true in Linux or Windows so I don't see why Go would make this assumption other than poor design.
>The property should be accessible via a handle that can be shared amongst all threads.
It literally is. The namespace information is shared with all threads in a PID group globally available in the /proc fs.
---
What OP is doing is effectively having 1 program run part of itself in 1 container, and another part outside of that container.
Go isn't a systems programming language so this level of fine grain control isn't possible. Hell its pretty difficult in a C/Rust/C++ environment.
I think it's mostly true in Linux and the situations where it isn't true are subtle and require expert knowledge in some specific areas. It's not like there's a big sticker on Linux that says threads are generally not symmetric.
I get the bit of half the process being in one space and half in another. I just find it odd. Can half the process run as root and half the process run as another user? Maybe yes? Can a file be open in half the process and not open in the other?
At any rate, I think it's not black and white. Green threads do make sense in general and introducing different thread types and more granular control makes things more complicated.
I think you're missing my point about APIs and handles:
handle = open_namespace("test")
syscall(handle, "yes")
if handle can be used across threads then this sequence will run correctly regardless of what thread is executing it, it doesn't rely on anything bestowed on a thread.(EDIT: thread local storage I guess is an example of asymmetry between threads but it's intentional/clear asymmetry designed for specific purposes and something you don't need to access in Go e.g. because you don't use threads directly).
Any of the data stored in the TIB. For instance each thread can be bound to a different subsystem.
More prominently the locale is stored there.
In Linux you can unset CLONE_FS when creating a thread, but the Go runtime does not do this.
I note the Windows docs[1] say "Multithreaded applications and shared library code should not use the SetCurrentDirectory function" (which Go's Chdir() calls)
[1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa3...