Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.programming.threads > #2001 > unrolled thread
| Started by | Markus Elfring <Markus.Elfring@web.de> |
|---|---|
| First post | 2013-11-23 19:05 +0100 |
| Last post | 2014-01-26 03:45 +0000 |
| Articles | 19 — 7 participants |
Back to article view | Back to comp.programming.threads
Open issues in the specification for fork()? Markus Elfring <Markus.Elfring@web.de> - 2013-11-23 19:05 +0100
Re: Open issues in the specification for fork()? Måns Rullgård <mans@mansr.com> - 2013-11-23 18:32 +0000
Re: Open issues in the specification for fork()? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2013-11-23 22:13 +0000
Re: Open issues in the specification for fork()? Markus Elfring <Markus.Elfring@web.de> - 2013-11-24 06:56 +0100
Re: Open issues in the specification for fork()? Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> - 2013-11-24 10:27 +0000
Re: Open issues in the specification for fork()? Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2013-11-24 15:39 +0000
Re: Open issues in the specification for fork()? Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> - 2013-11-25 00:23 +0000
Re: Open issues in the specification for fork()? Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2013-11-25 01:19 +0000
Re: Open issues in the specification for fork()? Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> - 2013-11-25 12:08 +0000
Re: Open issues in the specification for fork()? Drazen Kacar <dave@fly.srk.fer.hr> - 2013-11-25 18:46 +0000
Re: Open issues in the specification for fork()? Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> - 2013-11-25 19:55 +0000
Re: Open issues in the specification for fork()? Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2013-11-25 22:16 +0000
Re: Open issues in the specification for fork()? Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> - 2013-11-26 08:33 +0000
Re: Open issues in the specification for fork()? Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2013-11-26 15:46 +0000
Re: Open issues in the specification for fork()? Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2013-11-25 12:21 +0000
Re: Open issues in the specification for fork()? Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2013-11-25 15:52 +0000
Re: Open issues in the specification for fork()? Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> - 2013-11-25 16:55 +0000
Re: Open issues in the specification for fork()? Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2013-11-25 17:36 +0000
Re: Open issues in the specification for fork()? Kaz Kylheku <kaz@kylheku.com> - 2014-01-26 03:45 +0000
| From | Markus Elfring <Markus.Elfring@web.de> |
|---|---|
| Date | 2013-11-23 19:05 +0100 |
| Subject | Open issues in the specification for fork()? |
| Message-ID | <bfc933Fq0crU1@mid.individual.net> |
Hello, I have read the document "http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html" once more. I find that some details will need further clarification, won't it? 1. Do you interpret the wording "... If a multi-threaded process calls fork(), ..." from the section "DESCRIPTION" in the way that it is not specified which behaviour should be expected for the address space in an ordinary single-threaded process? 2. Is the wording "... The fork() function is thus used only to run new programs, and the effects of calling functions that require certain resources between the call to fork() and the call to an exec function are undefined. ..." from the section "RATIONALE" a contradiction to the previous information "... Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. ..."? 3. Is asynchronous-signal-safety also relevant for the single-threaded use case here? Do you know interesting software design challenges or "realistic problems" for this situation? Regards, Markus
[toc] | [next] | [standalone]
| From | Måns Rullgård <mans@mansr.com> |
|---|---|
| Date | 2013-11-23 18:32 +0000 |
| Message-ID | <yw1xmwkvotnt.fsf@unicorn.mansr.com> |
| In reply to | #2001 |
Markus Elfring <Markus.Elfring@web.de> writes: > Hello, > > I have read the document > "http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html" > once more. I find that some details will need further clarification, > won't it? > > 1. Do you interpret the wording > "... If a multi-threaded process calls fork(), ..." > from the section "DESCRIPTION" in the way that it is not specified which > behaviour should be expected for the address space in an ordinary > single-threaded process? No. See the top of the DESCRIPTION section: The fork() function shall create a new process. The new process (child process) shall be an exact copy of the calling process (parent process) except as detailed below. This adequately defines the single-threaded case. The part you quote details the behaviour in a multi-threaded process. > 2. Is the wording > "... The fork() function is thus used only to run new programs, and > the effects of calling functions that require certain resources > between the call to fork() and the call to an exec function are > undefined. ..." > from the section "RATIONALE" a contradiction to the previous information > "... Consequently, to avoid errors, the child process may only execute > async-signal-safe operations until such time as one of the exec functions is > called. ..."? No. The part you quote from the RATIONALE section is an observation of the typical use of fork() in a multi-threaded program (before the comma), and a note about the consequences of the restrictions given in the DESCRIPTION (after the comma). The RATIONALE section exists only as background information which can be useful for understanding why a particular behaviour is specified. If it appears to contradict the DESCRIPTION, the latter wins. > 3. Is asynchronous-signal-safety also relevant for the single-threaded > use case here? No. In the single-threaded case the state of the child process is known so no limitations apply beyond any already present in the parent. Note however that ordering of operations on resources shared between the two processes (e.g. file descriptors) is not defined unless explicitly synchronised by other means. -- Måns Rullgård mans@mansr.com
[toc] | [prev] | [next] | [standalone]
| From | Chris Vine <chris@cvine--nospam--.freeserve.co.uk> |
|---|---|
| Date | 2013-11-23 22:13 +0000 |
| Message-ID | <20131123221330.3816a0ce@laptop.homenet> |
| In reply to | #2001 |
On Sat, 23 Nov 2013 19:05:22 +0100 Markus Elfring <Markus.Elfring@web.de> wrote: [snip] > 3. Is asynchronous-signal-safety also relevant for the > single-threaded use case here? > Do you know interesting software design challenges or "realistic > problems" for this situation? After fork() the child process is single threaded, consisting only of a continuation of the thread which called fork(); however, it inherits the state of the whole parent process. In a multi-threaded program, only the parent remains multi-threaded. This means that if, for example, a thread other than the forking() thread holds a lock or other thread resource, it will never be released in the child process. If the child process subsequently attempts to lock a lock so held, such as used by malloc(), it will deadlock. This is the principal reason for the async-signal-safe requirement: async-signal-safe functions do not use such resources and so will not misbehave. This is irrelevant to single threaded programs. They can call whatever they like after a fork(). Chris
[toc] | [prev] | [next] | [standalone]
| From | Markus Elfring <Markus.Elfring@web.de> |
|---|---|
| Date | 2013-11-24 06:56 +0100 |
| Message-ID | <bfdip6F3gjdU1@mid.individual.net> |
| In reply to | #2003 |
> This is irrelevant to single threaded programs. They can call whatever > they like after a fork(). Unless the processing context will be switched to the implementation of a signal handler. (Asynchronous-signal-safety should be considered in an other situation then, shouldn't it?) ;-) Regards, Markus
[toc] | [prev] | [next] | [standalone]
| From | Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> |
|---|---|
| Date | 2013-11-24 10:27 +0000 |
| Message-ID | <5291d497$0$15884$e4fe514c@news2.news.xs4all.nl> |
| In reply to | #2001 |
Markus Elfring <Markus.Elfring@web.de> writes:
>Hello,
>I have read the document
>"http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html" once more.
>I find that some details will need further clarification, won't it?
>1. Do you interpret the wording
> "... If a multi-threaded process calls fork(), ..."
> from the section "DESCRIPTION" in the way that it is not specified which
>behaviour should be expected for the address space in an ordinary
>single-threaded process?
Well, the description starts with:
" .... The new process (child process) shall be an exact copy of
the calling process (parent process) except as detailed below:"
So what is listed for the multi-threaded process is one of the
exceptions; specifically, I believe it says that the stacks of the other
threads aren't part of the new process.
>2. Is the wording
> "... The fork() function is thus used only to run new programs, and the
>effects of calling functions that require certain resources between the call to
>fork() and the call to an exec function are undefined. ..."
> from the section "RATIONALE" a contradiction to the previous information
> "... Consequently, to avoid errors, the child process may only execute
>async-signal-safe operations until such time as one of the exec functions is
>called. ..."?
This is all about multi-threaded programs; and in such programs what functions
you can call in the child after fork() is restricted to async-signal-safe
functions.
>3. Is asynchronous-signal-safety also relevant for the single-threaded use case
>here?
No, except when fork() is called from a signal handler.
> Do you know interesting software design challenges or "realistic problems"
>for this situation?
Yes. Any form of malloc(), exit() instead of _exit(), etc, may lead
to problems which likely happen randomly and probably after you've
shipped the code to a customer.
Casper
[toc] | [prev] | [next] | [standalone]
| From | Rainer Weikusat <rweikusat@mobileactivedefense.com> |
|---|---|
| Date | 2013-11-24 15:39 +0000 |
| Message-ID | <87hab1kdvw.fsf@sable.mobileactivedefense.com> |
| In reply to | #2005 |
Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> writes: > Markus Elfring <Markus.Elfring@web.de> writes: [...] >>2. Is the wording >> "... The fork() function is thus used only to run new programs, and the >>effects of calling functions that require certain resources between the call to >>fork() and the call to an exec function are undefined. ..." >> from the section "RATIONALE" a contradiction to the previous information >> "... Consequently, to avoid errors, the child process may only execute >>async-signal-safe operations until such time as one of the exec functions is >>called. ..."? > > This is all about multi-threaded programs; and in such programs what functions > you can call in the child after fork() is restricted to async-signal-safe > functions. This is all about the people who wrote the pthreads-specification fighting the other tribe of people who are Violently(!!1) opposed to multi-threading but purposely not defining any sensible semantics for fork in multi-threaded processes. Consequently, the UNIX(*) standard is useless here and best ignored: For any existing platform, the behaviour is necessarily defined and code can be written to work in such an environment. [...] >> Do you know interesting software design challenges or "realistic problems" >>for this situation? > > Yes. Any form of malloc(), exit() instead of _exit(), etc, may lead > to problems which likely happen randomly and probably after you've > shipped the code to a customer. If the failures are demonstrably random, that is, they occur because the people who wrote the system code added stuff like if (random() % 15 < 3) *(int *)(random()) = 12; with some guard to trigger only in multi-threaded programs, that would be a problem of the customer who should be more careful when chosing suppliers.
[toc] | [prev] | [next] | [standalone]
| From | Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> |
|---|---|
| Date | 2013-11-25 00:23 +0000 |
| Message-ID | <52929869$0$15987$e4fe514c@news2.news.xs4all.nl> |
| In reply to | #2006 |
Rainer Weikusat <rweikusat@mobileactivedefense.com> writes: >This is all about the people who wrote the pthreads-specification >fighting the other tribe of people who are Violently(!!1) opposed to >multi-threading but purposely not defining any sensible semantics for >fork in multi-threaded processes. Consequently, the UNIX(*) standard is >useless here and best ignored: For any existing platform, the behaviour >is necessarily defined and code can be written to work in such an >environment. I think this is completely unfair as it are those who worked on the implementation and those who wrote the standard knew exactly the two choices they had; they picked one of the two choices and you seem to disagree with that particular choice. >If the failures are demonstrably random, that is, they occur because the >people who wrote the system code added stuff like You don't quite get exactly what I'm saying; any program using threads no longer has a well-defined order in which all the instructions are evaluated. In such a problem errors in logic, such as those not adhering with "calling only calling async-signal-safe after fork", will occur seemingly randomly as it depends on what the other threads are doing. Casper
[toc] | [prev] | [next] | [standalone]
| From | Chris Vine <chris@cvine--nospam--.freeserve.co.uk> |
|---|---|
| Date | 2013-11-25 01:19 +0000 |
| Message-ID | <20131125011903.172db979@laptop.homenet> |
| In reply to | #2007 |
On 25 Nov 2013 00:23:05 GMT Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> wrote: [snip] > I think this is completely unfair as it are those who worked on the > implementation and those who wrote the standard knew exactly the > two choices they had; they picked one of the two choices and you > seem to disagree with that particular choice. It was also in my view the better choice - I just don't like forkall(), as it can make the program more difficult to reason about, particularly for programs using a shared resource such as I/O (and what don't), which can turn forkall() into a bug factory. The only purpose for calling POSIX fork() in a multi-threaded program is to follow it with a call to exec*() (perhaps after setting up pipes with dup2() and the like), in which case the async-signal-safe restriction is fine: you do your other set up (including any memory allocation) before the fork() and not after. For other purposes, in a multi-threaded program conforming to POSIX you start a thread, not a process. pthread_atfork() is completely broken. That can cause problems with single threaded programs that use multi-threaded libraries without taking the care to understand that what they are linking to is multi-threaded, but there we are. Know what you are linking to is, I guess, the best answer. Chris
[toc] | [prev] | [next] | [standalone]
| From | Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> |
|---|---|
| Date | 2013-11-25 12:08 +0000 |
| Message-ID | <52933db8$0$15988$e4fe514c@news2.news.xs4all.nl> |
| In reply to | #2008 |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> writes: >It was also in my view the better choice - I just don't like forkall(), >as it can make the program more difficult to reason about, particularly >for programs using a shared resource such as I/O (and what don't), >which can turn forkall() into a bug factory. Originally, Solaris did came with its own threads library and in that thread implemetation had fork1() and fork() (aka forkall()). Later they added a pthread implementation and the pthread implementaton needed to use fork1() for fork(), so there were two different libraries. If you didn't link with -lthread or -lpthread, you got stubs for many of the thread calls (such as mutex primitives) so a library could be written as if it was multi-threaded but would work in a single threaded application. >The only purpose for calling POSIX fork() in a multi-threaded program is >to follow it with a call to exec*() (perhaps after setting up pipes with >dup2() and the like), in which case the async-signal-safe restriction >is fine: you do your other set up (including any memory allocation) >before the fork() and not after. For other purposes, in a >multi-threaded program conforming to POSIX you start a thread, not a >process. And so we now have posix_spawn() which does much of what you generally do between fork() and exec(). >pthread_atfork() is completely broken. That can cause problems with >single threaded programs that use multi-threaded libraries without >taking the care to understand that what they are linking to is >multi-threaded, but there we are. Know what you are linking to is, I >guess, the best answer. That is perhaps more a question quality of implementation and not so much an issue with pthread_atfork(). In Solaris 10 there is no longer a difference between a single threaded program and a multi-threaded program with only one thread. So there is really no issue with linking with libraries which may use threads. As a result of making threads the standard, fork() is now *always* fork1() though forkall() is still available. Casper
[toc] | [prev] | [next] | [standalone]
| From | Drazen Kacar <dave@fly.srk.fer.hr> |
|---|---|
| Date | 2013-11-25 18:46 +0000 |
| Message-ID | <slrnl976oc.msc.dave@fly.srk.fer.hr> |
| In reply to | #2009 |
Casper H.S Dik wrote:
> If you didn't link with -lthread or -lpthread, you got stubs for many
> of the thread calls (such as mutex primitives) so a library could be
> written as if it was multi-threaded but would work in a single threaded
> application.
Not really. Things worked that way and it was generally known that stubs
in libc exist[1], but that was neither documented nor supported. So the
only entity who could safely write libraries that way was Sun.
I don't know if anyone else wrote them that way. I decided not to, because
the method was not supported and thus likely to cause trouble if the
implementation changes.
[1] And, of course, there were people who wrote multi-threaded program,
but forgot to add one of the thread libraries to the link line. Then the
program would link fine (because of stubs in libc), but none of the
thread functions would work. That was a lot of fun for everyone. :-)
--
.-. .-. Yes, I am an agent of Satan, but my duties are largely
(_ \ / _) ceremonial.
|
| dave@fly.srk.fer.hr
[toc] | [prev] | [next] | [standalone]
| From | Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> |
|---|---|
| Date | 2013-11-25 19:55 +0000 |
| Message-ID | <5293ab24$0$15963$e4fe514c@news2.news.xs4all.nl> |
| In reply to | #2014 |
Drazen Kacar <dave@fly.srk.fer.hr> writes: >Not really. Things worked that way and it was generally known that stubs >in libc exist[1], but that was neither documented nor supported. So the >only entity who could safely write libraries that way was Sun. And they didn't all work either; e.g., pthread_once() didn't work. >I don't know if anyone else wrote them that way. I decided not to, because >the method was not supported and thus likely to cause trouble if the >implementation changes. >[1] And, of course, there were people who wrote multi-threaded program, >but forgot to add one of the thread libraries to the link line. Then the >program would link fine (because of stubs in libc), but none of the >thread functions would work. That was a lot of fun for everyone. :-) Yes; fortunately, that was fixed when Solaris 10 was released (in 2005) But that couldn't be done until we had created a multi-thread library which behaved more sanely in the presence of just the one thread; such a library was first shipped with Solaris 8 and was made the default in Solaris 9. Casper
[toc] | [prev] | [next] | [standalone]
| From | Rainer Weikusat <rweikusat@mobileactivedefense.com> |
|---|---|
| Date | 2013-11-25 22:16 +0000 |
| Message-ID | <87k3fww2jc.fsf@sable.mobileactivedefense.com> |
| In reply to | #2015 |
Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> writes: > Drazen Kacar <dave@fly.srk.fer.hr> writes: [...] >>[1] And, of course, there were people who wrote multi-threaded program, >>but forgot to add one of the thread libraries to the link line. Then the >>program would link fine (because of stubs in libc), but none of the >>thread functions would work. That was a lot of fun for everyone. :-) > > Yes; fortunately, that was fixed when Solaris 10 was released (in 2005) > > But that couldn't be done until we had created a multi-thread library > which behaved more sanely in the presence of just the one thread; > such a library was first shipped with Solaris 8 and was made the default > in Solaris 9. This is described in some detail in a whitepaper named 'Multithreading in the Solaris Operating Environment' published around the time of Solaris 9 which is still worth a read (I actually just reread it). The main change in order to get a 'sanely behaving threading library' was switching from M:N threading to 1:1 threading and thus getting rid of the necessity to use hidden userspace threads in order to try to solve problems inherent in the M:N approach. This includes the remarkable feat of accepting that threads are useful because they enable applications to use multiprocessors AND NOT because they enable university eggheads from 'research assistant' upwards to avoid dealing with state machines in their code (that's a gross oversimplification, mainly because I really don't know how to describe this in a more accurate way). Commercial operating system tend to oscillate between these two models (with the occasional idiot reimplementing cooperative userspace threading for the upteempth time), cf the following statement from HP on 'MxN threading in HP-UX 11i': MxN threads were implemented in response to the perception that enterprise applications creating large numbers of threads perform better with MxN threads than with 1x1. However, The only real way to know how using the MxN model affects the performance of your particular application is to benchmark the two models and compare the results. Or, to put this into other words "While there's a strong perception that such applications ought to exist, there's little real evidence that they actually do".
[toc] | [prev] | [next] | [standalone]
| From | Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> |
|---|---|
| Date | 2013-11-26 08:33 +0000 |
| Message-ID | <52945ce1$0$15995$e4fe514c@news2.news.xs4all.nl> |
| In reply to | #2016 |
Rainer Weikusat <rweikusat@mobileactivedefense.com> writes: >Commercial operating system tend to oscillate between these two models >(with the occasional idiot reimplementing cooperative userspace >threading for the upteempth time), cf the following statement from HP on >'MxN threading in HP-UX 11i': > MxN threads were implemented in response to the perception that > enterprise applications creating large numbers of threads > perform better with MxN threads than with 1x1. >However, > The only real way to know how using the MxN model affects the > performance of your particular application is to benchmark the > two models and compare the results. >Or, to put this into other words "While there's a strong perception that >such applications ought to exist, there's little real evidence that they >actually do". The argument for "M:N is better" was very strong because, theoretically, it could be faster and synchronization wouldn't need to use the kernel. A "1:1" implementation, in practice, however, uncontended locks don't need a trip to the kernel either and the enormous amount of extra work needed in the MxN implementation (handling SIGWAITING, a second implementation of context switching, which actually often did require kernel involvement) It should also be noticed that when a comparison was done, it was often using the same libthread library but run so that M == N; but that wasn't a proper comparison as in that case you still pay for the penalty for the M:N support. Casper
[toc] | [prev] | [next] | [standalone]
| From | Rainer Weikusat <rweikusat@mobileactivedefense.com> |
|---|---|
| Date | 2013-11-26 15:46 +0000 |
| Message-ID | <87li0bmahy.fsf@sable.mobileactivedefense.com> |
| In reply to | #2017 |
Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> writes: > Rainer Weikusat <rweikusat@mobileactivedefense.com> writes: [M:N/ 1:1 threading] > The argument for "M:N is better" was very strong because, theoretically, > it could be faster and synchronization wouldn't need to use the kernel. > > A "1:1" implementation, in practice, however, uncontended locks don't need a > trip to the kernel either and the enormous amount of extra work needed in > the MxN implementation (handling SIGWAITING, a second implementation of > context switching, which actually often did require kernel involvement) Some people think that 'threads are a way to structure programs', that is, they're primarily supposed to be helpful for developers. The idea is typically that 'a thread of execution' can be dedicated to a single task which progresses intermittently and concurrently with a lot of other tasks of the same kind: In a single-threaded process, this requires storing the state of each task in some 'globally accessible' data structure and continuing to work on some other tasks based on the recorded state of that whenever the 'current task' can't make progress anymore. If each task had a dedicated thread, this wouldn't be necessary because 'the scheduler' would transparently suspend one thread and switch to another whenever things would otherwise come to a standstill, thus providing the illusion that each task really runs on a dedicated computer without having to consider the needs of other tasks. This, of course, requires creating as many threads as there are concurrent tasks and for something like a network server, this could easily mean thousands or even tenthousands of them. Consequently, each thread needs to be as cheap as possible, in particular, it shouldn't have any kernel resources like 'a kernel stack' permanently allocated to it. Also, in order to make programming easy, actual concurrent execution of threads should rather be avoided: For as long as a thread is busy, it just keeps running, and once it invokes 'a blocking call' of some kind, another thread can start to run. In order to avoid starving tasks, threads are supposed to be 'nice to each other', that is, explictly yield the processer every now and then when being busy for a long time. This means cooperative userspace threading. Because there's no concurrency, synchronization isn't really needed and because there's no preemption, no complicated userspace scheduler is needed, either. At least in theory. In practice, once the process blocks in the kernel, all threads stop running and hence, each and every posssible blocking system call must be wrapped in some library routine which accomplishes the same end in some non-blocking way and runs other threads in the meantime. The net effect of this is usually a lot of borderline insane library hackery and some rather bizarre restrictions as to what a thread can do or must not do (the GNU pth paper provides a nice example of that). Some other people think that threads are supposed to be useful for users in the sense that they enable these to utilize processing resources such as multiple processors and/or multiple cores which might be available to them. This implies that 'a thread' runs code which looks pretty much like that of a single-threaded in processes wrt to 'multi-tasking'. In order to keep scheduling overhead down, there will be relatively few threads (somewhat more than available 'processing things') but they're both supposed to execute in parallell and to work with 'shared global resources'. This means synchronization is needed and the kernel scheduler has to deal with them because the kernel is the privileged piece of coding managing the actual hardware and thus, the only software capable of doing things like assigned threads to available processors for execution. Further, this is supposed to work in an environment shared with other application which also seek to use the available resources and which don't have been written to cooperate, hence, preemption is necessary. The most natural choice for this is 1:1 threading where each 'application thread' corresponds with a 'kernel scheduled entity' of some sort. Trying to satisfy both groups of people leads to M:N threading which really just combines the disadvantages of both models without providing the advantages of either. It ends up as expensive way to screw everyone alike but only in practice, not in theory. I was planning to illustrate this using the pre-Solaris 8/9 'threading workarounds' as example but this text is too long already ...
[toc] | [prev] | [next] | [standalone]
| From | Rainer Weikusat <rweikusat@mobileactivedefense.com> |
|---|---|
| Date | 2013-11-25 12:21 +0000 |
| Message-ID | <87bo18y8ml.fsf@sable.mobileactivedefense.com> |
| In reply to | #2008 |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> writes: > On 25 Nov 2013 00:23:05 GMT > Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> wrote: > [snip] >> I think this is completely unfair as it are those who worked on the >> implementation and those who wrote the standard knew exactly the >> two choices they had; they picked one of the two choices and you >> seem to disagree with that particular choice. [...] > The only purpose for calling POSIX fork() in a multi-threaded program is > to follow it with a call to exec*() (perhaps after setting up pipes with > dup2() and the like), For some people, the only purpose of fork they can possibly imagine is as halfway useless incumbment on the way to executing another program and - suprisingly and certainly by mere coincidence - the people who wrote the phtreads specification and produced this litte gem are among them. This makes two extreme standpoints, namely, "multi-threading is useless and evil and 'cooperating sequential processes' is all God ever wanted men to use" and "multiple processes running the same program are totally useless, dangerous, outlandish, alien and socialist(!!1) and our nice, shiny threads are a much better modern solution to any problem one could conceivably need to solve" and neither of both is particularly helpful in the real world (as extreme standpoints are wont to be), especially considering all the moss which has been growing on this controversy since it originated last century. When a process forks, the contents of the memory in the address space of the new process will be identical to that of the parent at the time of the fork. Because only the forking thread is duplicated the child, the effect on any other threads is that they stopped asynchronously at some unpredictable (as seen from the 'sequential flow of time' of each particular thread) time between executeing two instructions. This is exactly what also happens when a signal handler is invoked because of an asynchronous signal, hence, without special precautions, only async-signal-safe functions can be called safely in the new process. Another problem would be that POSIX demands that mutexes can only be unlocked by the thread which locked them which implies that a mutex which was held by some other thread than the one which forked cannot ever be unlocked in the new process. But signal handlers are not really restriced to async-signal safe calls because they're free to do anything provided that 'main thread of execution' was interrupted in an 'async-signal safe section' of the code (the requirement is [paraphrase] 'all functions defined by this specification shall work as described in the presence of asynchronous signal except when function which is not async-signal safe is executed from a signal handler which interrupted another not async-signal safe function. In this case, the behaviour is undefined'). Likewise, there's no restriction regarding what the forked process may safely do provided all threads were executing something async-signal safe at the time of the fork and no mutexes held by any of them will be touched in the new process. And this is perfectly doable, especially considering that - practically - some things are async-signal safe despite they're not required to be, eg, blocking on a locked mutex (or semaphore). In an ideal universe, implementations would be required to document how they deal with multi-threaded access to shared resources and one could even envision an API for safe forking modelled similar to the existing facilities for dealing with asynchronous signals. In absence of both, it is still possible to confine all other threads of a process to some safe location during the fork and release them afterwards. This is obviously not going to work for the most general imaginable case, where 'the process' runs arbitrary, binary only third-party code whose actions can neither be controlled nor predicted but this is not the only case.
[toc] | [prev] | [next] | [standalone]
| From | Rainer Weikusat <rweikusat@mobileactivedefense.com> |
|---|---|
| Date | 2013-11-25 15:52 +0000 |
| Message-ID | <871u24xyup.fsf@sable.mobileactivedefense.com> |
| In reply to | #2007 |
Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> writes:
> Rainer Weikusat <rweikusat@mobileactivedefense.com> writes:
>
>>This is all about the people who wrote the pthreads-specification
>>fighting the other tribe of people who are Violently(!!1) opposed to
>>multi-threading but purposely not defining any sensible semantics for
>>fork in multi-threaded processes. Consequently, the UNIX(*) standard is
>>useless here and best ignored: For any existing platform, the behaviour
>>is necessarily defined and code can be written to work in such an
>>environment.
>
> I think this is completely unfair as it are those who worked on the
> implementation and those who wrote the standard knew exactly the
> two choices they had; they picked one of the two choices and you
> seem to disagree with that particular choice.
I 'disagree' with this here:
There are two reasons why POSIX programmers call fork(). One
reason is to create a new thread of control within the same
program (which was originally only possible in POSIX by creating
a new process);
[...]
When a programmer is writing a multi-threaded program, the first
described use of fork(), creating new threads in the same
program, is provided by the pthread_create() function. The
fork() function is thus used only to run new programs,
because while this is not an outright lie, it's a carefully
weasel-worded piece of disinformation supposed to lent some appearance
of rationality to the entirely political bias of the author: Fork does
not create 'a new thread of control running the same program' (which has
a process chained to its ankle for some shitty, historical reason nobody
in his right mind could possibly understand) but it creates a new
process running the same program and this includes creating a new thread
of control among some other things, most prominently, a new address
space.
There's also the practical concern that adding a new thread to some
existing code unleashes 'immediate pthread madness' which is supposed to
change the semantics of the working code retroactively: Something which
used to be a perfectly legitimate use of fork is now considered to have
undefined behaviour, regardless of how the interactions between the
existing 'single-threaded process code' and the code running in the new
thread actually happen to work. This means 'using pthreads' is meant to
be an all-or-nothing choice: Either the process remains strictly
single-threaded. Or everything has to be rewritten to be 'strictly
pthreaded' insofar this is possible (which might not be the case since
the code could rely on properties of fork pthread_create does not have)
and this looks a lot like a useless effort to me, IOW, 'pthreads' trying
to punish me for using 'traditionally written UNIX(*) code'.
[toc] | [prev] | [next] | [standalone]
| From | Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> |
|---|---|
| Date | 2013-11-25 16:55 +0000 |
| Message-ID | <529380fe$0$15963$e4fe514c@news2.news.xs4all.nl> |
| In reply to | #2011 |
Rainer Weikusat <rweikusat@mobileactivedefense.com> writes: >I 'disagree' with this here: > There are two reasons why POSIX programmers call fork(). One > reason is to create a new thread of control within the same > program (which was originally only possible in POSIX by creating > a new process); The changes for the threads didn't change how fork() worked or didn't work. Even before threads were common, fork() had many, many issues, such as fork()ing the whole status of the stdio library; atexit() handlers. fork() as a second thread in the same program was always full of risk. You almost sound like someone who laments the loss of Linux' clone model to the pthread model. It is true, I think, that starting threads from a library is frowned upon, except if these threads are extremely well-behaved (and that might include not calling malloc() after control has been back to the main thread) Casper
[toc] | [prev] | [next] | [standalone]
| From | Rainer Weikusat <rweikusat@mobileactivedefense.com> |
|---|---|
| Date | 2013-11-25 17:36 +0000 |
| Message-ID | <87ob58wfhg.fsf@sable.mobileactivedefense.com> |
| In reply to | #2012 |
Casper H.S. Dik <Casper.Dik@OrSPaMcle.COM> writes: > Rainer Weikusat <rweikusat@mobileactivedefense.com> writes: > > >>I 'disagree' with this here: > >> There are two reasons why POSIX programmers call fork(). One >> reason is to create a new thread of control within the same >> program (which was originally only possible in POSIX by creating >> a new process); > > The changes for the threads didn't change how fork() worked or > didn't work. > > Even before threads were common, fork() had many, many issues, such > as fork()ing the whole status of the stdio library; atexit() handlers. > > fork() as a second thread in the same program was always full of > risk. > > You almost sound like someone who laments the loss of Linux' clone > model to the pthread model. I wrote something specific about the whole 'semantic unit of text' the first paragraph of which you quoted above and about another problem with the pthreads 'make no prisoners' approach. I don't see how the contents of your text would relate to that. I'm also not aware of any 'losses' affecting the Linux clone system call which were caused by adding the functionality necessary for supporting the pthreads signal handling model (which, while sensible in itself, is another 'disaster as designed' because it runs counter the intuition of a lot of people, who presumably use dedicate signal handling threads, thus, limiting the scalability of their code, despite there's no real reason for that except that they don't understand the purpose of the pthreads signal handling approach and consider it "scary and dangerous" because of that). While I don't consider stdio particularly useful, I wouldn't go so far to label it as 'risky on UNIX(*)' and the same is true for 'atexit handlers'. Using binary-only code with unknown behaviour is 'risky' but even these dangers can be dealt with.
[toc] | [prev] | [next] | [standalone]
| From | Kaz Kylheku <kaz@kylheku.com> |
|---|---|
| Date | 2014-01-26 03:45 +0000 |
| Message-ID | <20140125193706.829@kylheku.com> |
| In reply to | #2001 |
["Followup-To:" header set to comp.unix.programmer.] On 2013-11-23, Markus Elfring <Markus.Elfring@web.de> wrote: > Hello, > > I have read the document > "http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html" once more. > I find that some details will need further clarification, won't it? > > 1. Do you interpret the wording > "... If a multi-threaded process calls fork(), ..." > from the section "DESCRIPTION" in the way that it is not specified which > behaviour should be expected for the address space in an ordinary > single-threaded process? > > 2. Is the wording > "... The fork() function is thus used only to run new programs, and the > effects of calling functions that require certain resources between the call to > fork() and the call to an exec function are undefined. ..." > from the section "RATIONALE" a contradiction to the previous information > "... Consequently, to avoid errors, the child process may only execute > async-signal-safe operations until such time as one of the exec functions is > called. ..."? > > 3. Is asynchronous-signal-safety also relevant for the single-threaded use case > here? > Do you know interesting software design challenges or "realistic problems" > for this situation? The issue is that when a copy is made of a multithreaded process by fork, it's as if the child is in a parallel universe where all the threads except for the forking one have suddenly disappeared dead in their tracks. So for instance, what if one of the threads in the parent was in the middle of calling malloc, and some pointers in the malloc data structure are in a bad state? The child inherits them that way. You can write MT programs that handle fork and keep going. In the global initialization routine of each module, have it register fork handlers with pthread_atfork. Have every module do some correct thing when a fork is going on. The pre-fork handler can ensure that threads are are "parked" in some quiescent state, where they are known not to be updating anything that will result in a trashed copy. The post-fork handler in the parent can then clear the emergency so that things continue chugging along. The post-fork handler in the child can do something similar, keeping in mind that there are no threads there. It's a lot of responsibility, possibly complex coding and ... good luck getting third-party components to cooperate.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.programming.threads
csiph-web