84 points · 101 comments · 9 years ago · zdw
evanjones.cacaf
rdtsc
Here are the details of how it works:
https://github.com/erlang/otp/blob/a5256e5221aff30f6d2cc7fab...
They also claim a 3-5x speedup for launching external commands because of it. So there is a nice performance boost as well.
So basically can add 4th strategy -- fork once a small program at the start, then fork from there from then on.
qwertyuiop924
In places where you need the speed, threads are useful, but they're harder to use than forks.
Annoyingly, threads and forks don't work well together. zzzcpan already talked about how to fork threaded code. As for the problem of libraries using threads, if a library you're using is using threads, and it's documented (it probably is), and you didn't know about it, I'll pencil that in as your fault.
Other comments in this thread have dismissed fork(2) as a bad job entirely, but I don't think I agree. It's an effective way to do simple multiprocessing, and it's a lot simpler than threads in many contexts.
kostyash
antirez
So if you control very closely the libs you link with and what they do, as well as the threads you use yourself, it is possible to use fork() in a reliable way.
ambrop7
EDIT: Never mind, seems like initgroups() is also nonstandard but generally available on Unix-like systems.
datenwolf
I see two implementation challenges with that suggestion:
1.) implementing that transaction mechanism as a kernel feature: When entering a CS mark all pages CoW, upon leaving the CS merge modified pages (problem: Whole pages are then mutual exclusive, dealing with this is the challenge)
2.) battling with user space implemented locks that use atomics.
-----
An immediate mitigation I see is, that fork() itself is a CS on _all_ the locks of a process. If we consider that only the standard locking mechanisms are used, then whenever a CS is entered (which includes the creation process of a locking primitive) it raises/posts a global fork-lock semaphore. And upon leaving that semaphore is lowered.
This still leaves the DIY-locking primitives problem open. But it should be more or less straightforward to add this to the system libc/pthread libraries' locking primitive implementation and fork() syscall wrappers.
Or did I miss something essential here? Talk is cheap, so if nobody has any obvious objections I'd actually go ahead implement it.
EDIT: Okay, one immediate problem I see is, that this would pose a challenge for calling fork inside a CS. Technically this is a situation where thread recursive locks would help, but as we all know, recursive locks are highly problematic.
ahh
Even fork _with_ exec can be real trouble. This is one of my bugaboos at work: due to poor life choices and high pain tolerance, I own the infrastructure we use to spawn subprocesses (carefully.) For various reasons (security most notably) we have to do some very tricky things in and to the forked child before exec(), and pretty much all of this code is a disaster waiting to happen. Every so often I get feature requests for more stupid pet tricks people would like out of subprocesses, and they're always surprised by what their "simple" change would entail.
I'd like it if Linux had native support for posix_spawn, but even that would require a lot of extensions to be useful.
Don't get me started on the teams that want to break forking rules and thus ask me how to guarantee a process has no non-main threads. There are few ways you can make me more upset than by building software that breaks if some one else happens to call pthread_create and doesn't tell you.
hyperbovine
saynsedit
xroche
Typically calls such as malloc(), printf() etc. are strictly forbidden in the child after a fork().
lsiebert
zzzcpan
How to use fork safely: 1. Only use fork to immediately call exec. 2. Fork a worker at the beginning of your program, before there can be other threads. 3. Only use fork in toy programs.
4. Stop writing broken multithreaded code altogether or if you must at least run an event loop per core/thread and use a wrapper for fork() to put the system into a fork()able state before forking. It's nice and reliable.
Multithreading by itself is just not a high-level concept to be used reliably by programs.
known