Earlier quoted context omitted.
It's harder for us, but I think we can get to an ergonomic solution with async/await. It's not as easy as threads (M:N vs 1:1 is a red herring as far as this is concerned), but there's no free lunch.
async/await still has the fundamental problem of composability that all the other attempts at sugaring around an event loop have (aka the "functions have colors" problem [1]). It sucks for collaboration, which is one of the most important things for modern software development. 1. http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
Linux Namespaces and Go Don't Mix
161–170 of 175 posts
Re: Linux Namespaces and Go Don't Mix
#162Earlier quoted context omitted.
No, it doesn't. Go runtime can decide to spawn a new thread from the locked one. The new thread will inherit the characteristics (namespace, uid) of the old one. See https://github.com/docker/libnetwork/issues/1113 for more details.
> Go runtime can decide to spawn a new thread from the locked one 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 be…
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.
Re: Linux Namespaces and Go Don't Mix
#163Earlier quoted context omitted.
Thanks for explaining how it's done in runc. That does sound pretty awful. So, even though the initialization can be outsourced to a C function, you still would prefer to be working entirely in C? Are there no advantages to Go for runc? And, would it be possible for someone to write a somewhat standardized Go library for doing this grunt work? Is it merely fear of C that keeps so much of the container infrastructure…
> Is it merely fear of C that keeps so much of the container infrastructure on Go? Sort of. Go binaries are statically linked by default which is a must in situations where you are e.g. unsure about what libs are available in the current environment. You have to go through a lot of hoops to make sure your C executable is really fully statically linked.
Note though that Go packages will be statically linked into your binary by default.
Re: Linux Namespaces and Go Don't Mix
#164Earlier quoted context omitted.
> Go runtime can decide to spawn a new thread from the locked one 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 be…
> That sounds like an implementation issue, why not assume the documentation is the intended behavior and this side effect is a bug? 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 spawni…
I hope it continues to play nice, as I have a project depending on this behavior (bindings for a CPU emulator which spawns a thread for the CPU main loop, and calls back into my Go code on some events).
Thanks for the info. I guess the biggest problem is making sure `go` in a locked goroutine doesn't release threads to the scheduler from the locked goroutine? That's a weird thing to reason about. I initially guessed you'd need a separate feeder thread, but you want `go` from a locked thread to keep the uid and namespace of the locked thread.
So I guess there are two potential solutions:
- goroutines spawned from locked threads are pure coroutines and will not be scheduled on another thread
- goroutines spawned from locked threads can only execute on the locked thread, or on threads spawned from the locked thread that don't predate the goroutine. Child threads can also only be used for matching child goroutines, and will be more aggressively collected. This would converge closer to an oldschool threading model than green threads, but should hopefully prevent bugs like UID/namespace hopping.
This is the kind of thing you could possibly do with a small patch to the Go runtime. Go makes it so easy to build the compiler/runtime, perhaps forking Go for this is reasonable until there's an official solution.
There's also the issue of what to do with the primary thread that has been marked unsafe when it returns. Instead of LockOSThread, you probably want "give this OS thread and its children magic sandbox scheduling behavior, which includes collecting the thread when it returns, because we're going to call stuff like setuid".
Re: Linux Namespaces and Go Don't Mix
#165Earlier quoted context omitted.
The M:N problem in Go is that you cannot control which thread runs which code. So yes, you could wish the OS exposed different APIs, but presently you can manage this situation in languages that let you manage threads.
The person you're replying to has already made that clear. It's, in fact, also possible to manage the problem in Go with some finagling. But like Go, if you have multiple threads, it's difficult I've hit this exact problem with multithreading in C and setuid and just because it _can_ be managed in C doesn't make it easy or straightforward. Therefore, I mirror the sentiment that there needs to be a way to operate on a…
I too love reading the code for nptl(7). It's a riot. :D
Re: Linux Namespaces and Go Don't Mix
#166Earlier quoted context omitted.
> There is some prep for ugly contingencies(pthread_atfork) but it usually just works. Actually, that's not true. Perhaps it's theoretically possible, in some cases, to make your threaded code work with fork with the appropriate pthread_atfork() handlers, but in general, it's a total mess. Thread A has a lock, thread B calls fork(), thread B tries to acquire the lock. The lock is held, but thread A is not running in…
On paper, all code between a fork and exec (in a multithreaded process) must be async-signal safe as well. I'm not sure if this is also true for the callbacks with pthread_atfork (I've personally never used it), but it's something to keep in mind.
Re: Linux Namespaces and Go Don't Mix
#167Earlier quoted context omitted.
> You don't really know if your code will work until you compile. This is true in every text-based programming language. s/compile/execute/ for interpreted or repl-based languages.
Even then you don't know if program is correct. There have been pieces of software running in production for 20 or 30 only to fail because of some unforeseen and planned for error condition. My favorite was the Comair christmas failure back in 2004 or 2005. I tried googling the root cause, but I think it was an Integer overflow in the 16 bit integer they used for storing the flight number. The system was designed in…
No one disagreed with this comment and it is one of the least controversial thing I have said on HN. I think I have a downvote fairy, someone who just downvotes everything I say. If so, why bother? If I don't have one then when you downvoted me, why didn't you respond?
Re: Linux Namespaces and Go Don't Mix
#168Earlier quoted context omitted.
* Coercion: large down vote/propaganda community against any negative rust idea..evidenced here. * IP Holders, well this is speculative. I can't understand anyone who would compare Rust against C in a positive way without possibly profiting from it. Rust is painful to write, painful to learn and makes you ask yourself: Can I not write a well designed and correct program in C? Of course I can. Why Am I Learning Rust?…
> Can I not write a well designed and correct program in C? We have decades of coredumps and exploits to convince us that nobody can. How many well-known apps can you name that have never blown up randomly? I can't think of a single one. How much more failure until it's reasonable to think that C requires an inhuman level of perfection and almost any alternative would be an improvement? I hear good things about seL4…
1. Do not trust user input. This is a cardinal rule in whatever source. If the rule were followed vigorously in every case there would be 90% less exposure. When you take user input, filter. 2. Learn the standard and stick to it.
Finally #3 (unix) Write an application to do a certain thing well.
Re: Linux Namespaces and Go Don't Mix
#169Earlier quoted context omitted.
> Can I not write a well designed and correct program in C? We have decades of coredumps and exploits to convince us that nobody can. How many well-known apps can you name that have never blown up randomly? I can't think of a single one. How much more failure until it's reasonable to think that C requires an inhuman level of perfection and almost any alternative would be an improvement? I hear good things about seL4…
GNU ls has never segfaulted or otherwise blown up for me. :)
Rust and Go advocates will have you think that they have conquered security via the memory mgmt and API front but there is still the off chance they haven't..or that the SA (what is left of that maligned profession outside playbooks and the devops marketing you read here and online) has allowed you a chance for glory.
Re: Linux Namespaces and Go Don't Mix
#170Earlier quoted context omitted.
> Can I not write a well designed and correct program in C? We have decades of coredumps and exploits to convince us that nobody can. How many well-known apps can you name that have never blown up randomly? I can't think of a single one. How much more failure until it's reasonable to think that C requires an inhuman level of perfection and almost any alternative would be an improvement? I hear good things about seL4…
There are basically two rules to a well written C program (if I am now allowed to speak despite the public outcry). 1. Do not trust user input. This is a cardinal rule in whatever source. If the rule were followed vigorously in every case there would be 90% less exposure. When you take user input, filter. 2. Learn the standard and stick to it. Finally #3 (unix) Write an application to do a certain thing well.
On #1, having just stumbled across a deserializer that can be commanded to allocate a 2^63 byte buffer, I agree 110%.
On #2, the problem is that the standard says things like "walking off the end of an array is undefined behavior" and "use after free is undefined behavior" yet we don't seem to have any programmers who can be trusted to reliably avoid these problems with zero runtime checking.