
Hello, I'm having a little weird situation here. Whenever I call exitFailure under forkProcess, I get: (progname): forkProcess: uncaught exception Test program: import System.Exit import System.Posix.Process main = forkProcess exitFailure If you remove the forkProcess call, the program properly silently exits with exit code 1. I have no idea why it is displaying this message to stderr when called under forkProcess. I argue that it shouldn't. It is perfectly valid to return with an error code from a child process, and absolutely necessary in some cases when your later waitpid() call needs to know what happened. (Or, in Haskell, getProcessStatus()). Thoughts?

John Goerzen writes:
(progname): forkProcess: uncaught exception
Quoting from the documentation: forkProcess :: IO () -> IO ProcessID [...] On success, forkProcess returns the child's ProcessID to the parent process; in case of an error, an exception is thrown. What I assume is happening is this: The child terminates _right away_, with a non-success error code, no less. And my guess is that forkProcess interprets this as the /fork/ having failed and throws an exception. The following program, for example, works fine:
import System.Exit import System.Posix.Process import Control.Concurrent main = forkProcess (threadDelay (10*1000000) >> exitFailure)
Peter

On 2004-10-26, Peter Simons
John Goerzen writes:
(progname): forkProcess: uncaught exception
Quoting from the documentation:
forkProcess :: IO () -> IO ProcessID
[...] On success, forkProcess returns the child's ProcessID to the parent process; in case of an error, an exception is thrown.
What I assume is happening is this: The child terminates _right away_, with a non-success error code, no less. And my guess is that forkProcess interprets this as the /fork/ having failed and throws an exception.
If you follow this a little bit, you'll find that forkProcess is *NOT* throwing the exception that is being reported here. The message is being printed by the RTS of the child process. No exception is thrown in the parent. (Believe me, I've tried to catch it!) I thought about that a bit, and realized that is the only way it could be; there's no way an exception could propogate from a child forked process to the parent. In my particular case, in my child process, the problem was occuring when I detected an exec failure and wnated to terminate with an error code for the parent to catch. I called dupTo a few times in the child before that, so while it failed fast, it was doing things.
The following program, for example, works fine:
import System.Exit import System.Posix.Process import Control.Concurrent main = forkProcess (threadDelay (10*1000000) >> exitFailure)
I have no idea why that would be. Sigh.
Peter
-- John Goerzen Author, Foundations of Python Network Programming http://www.amazon.com/exec/obidos/tg/detail/-/1590593715

John, just to avoid any possible confusion: I emailed my reply to you and posted it to the list as well, but unfortunately I hit the wrong button so that my mail to you doesn't _say_ that it is a carbon copy. Sorry about the mess. By the way: It's good to know I'm not the only one wrestling with Haskell's concurrency code. :-) Peter

On 2004-10-26, Peter Simons
just to avoid any possible confusion: I emailed my reply to you and posted it to the list as well, but unfortunately I hit the wrong button so that my mail to you doesn't _say_ that it is a carbon copy. Sorry about the mess.
OK, thanks.
By the way: It's good to know I'm not the only one wrestling with Haskell's concurrency code. :-)
Yes. Its POSIX interface is, uhm, weird. I can't quite put my finger on it, but things like setting up a pipe to a child process's stdin just seem brittle and fragile with all sorts of weird errors. I can do this in my sleep in C, Perl, or Python but in Haskell I can barely make it work when I'm fully conscious :-) Oh also, I would very much appreciate Haskell interfaces to realpath() and readlink(). And a system/rawSystem that returns the waitpid() exit status like system(3) does, not just the exit code. (What happens if the process died because of a signal?) -- John

On Wed, Oct 27, 2004 at 12:56:12AM +0000, John Goerzen wrote:
If you follow this a little bit, you'll find that forkProcess is *NOT* throwing the exception that is being reported here. The message is being printed by the RTS of the child process. No exception is thrown in the parent. (Believe me, I've tried to catch it!)
I don't have any idea what's happening here, except that it semes an obvious bug. I found that a work-around is to use exitImmediately in the child. This is often advisable after a fork anyway, because you don't want finalizers or atexit hooks to run in the child. Presumably, it is some such code causing the breakage, though I can't imagine how. On Wed, Oct 27, 2004 at 01:02:52AM +0000, John Goerzen wrote:
Yes. Its POSIX interface is, uhm, weird. I can't quite put my finger on it, but things like setting up a pipe to a child process's stdin just seem brittle and fragile with all sorts of weird errors. I can do this in my sleep in C, Perl, or Python but in Haskell I can barely make it work when I'm fully conscious :-)
Actually, your bug aside, and despite minimal documentation, I've found the Posix modules straightforward. I wrote a version of system that returns the output of the command as a string, and it worked on the first try! I'm sure there are better versions around, but: system :: [String] -> IO String system (cmd:args) = do (read, write) <- createPipe pid <- forkProcess (closeFd read >> child write) closeFd write ret <- fdToHandle read >>= hGetContents length ret `seq` getProcessStatus True False pid return ret where child write = do dupTo write stdOutput closeFd write executeFile cmd True -- path args Nothing -- env Andrew

John Goerzen wrote:
Oh also, I would very much appreciate Haskell interfaces to realpath() and readlink().
I don't know about realpath() (which is a BSD-ism, and included in GNU
libc, but I'm not sure about other Unices), but readlink() exists as
System.Posix.readSymbolicLink.
--
Glynn Clements

On 27 October 2004 10:13, Glynn Clements wrote:
John Goerzen wrote:
Oh also, I would very much appreciate Haskell interfaces to realpath() and readlink().
I don't know about realpath() (which is a BSD-ism, and included in GNU libc, but I'm not sure about other Unices), but readlink() exists as System.Posix.readSymbolicLink.
The System.Posix library is severely lacking in documentation. Ideally for each function it would list the POSIX equivalent, and a table with the mapping in the other direction would be useful too. Cheers, Simon

On Wed, Oct 27, 2004 at 11:30:12AM +0100, Simon Marlow wrote:
The System.Posix library is severely lacking in documentation. Ideally for each function it would list the POSIX equivalent, and a table with the mapping in the other direction would be useful too.
One idea on this topic: Many POSIX wrappers, eg getProcessStatus[1], take mysterious Boolean arguments. I commonly create a new type when I have a function like this, so it might be data Blocking = Blocking | NonBlocking data IncludeStopped = IncludeStopped | ExcludeStopped getProcessStatus :: Blocking -> IncludeStopped -> ProcessID -> IO (Maybe ProcessStatus) Would this make these functions easier to use, or would it just be an additional annoyance to have to look up the values of these type? (I may have picked an extreme example, because even someone who knows POSIX by heart couldn't be sure which Boolean is which!) Andrew [1] http://haskell.org/ghc/docs/latest/html/libraries/unix/System.Posix.Process....

On 27 October 2004 02:03, John Goerzen wrote:
On 2004-10-26, Peter Simons
wrote: By the way: It's good to know I'm not the only one wrestling with Haskell's concurrency code. :-)
Yes. Its POSIX interface is, uhm, weird. I can't quite put my finger on it, but things like setting up a pipe to a child process's stdin just seem brittle and fragile with all sorts of weird errors. I can do this in my sleep in C, Perl, or Python but in Haskell I can barely make it work when I'm fully conscious :-)
*laughs* Is there anything concrete we can do? The POSIX layer is supposed to be pretty minimal, so in theory most POSIX idioms should not be harder in Haskell, and hopefully should be easier. We're open to suggestions, and even more open to code...
Oh also, I would very much appreciate Haskell interfaces to realpath() and readlink(). And a system/rawSystem that returns the waitpid() exit status like system(3) does, not just the exit code. (What happens if the process died because of a signal?)
system(3) is one of the things missing from the current System.Posix API. To see what else we consider to be missing, take a look at the comment in the System.Posix source: http://cvs.haskell.org/cgi-bin/cvsweb.cgi/~checkout~/fptools/libraries/u nix/System/Posix.hs?rev=1.11;content-type=text%2Fplain Since realpath() isn't POSIX, it would have to go into System.Posix.Exts. Cheers, Simon

Simon Marlow wrote:
Yes. Its POSIX interface is, uhm, weird. I can't quite put my finger on it, but things like setting up a pipe to a child process's stdin just seem brittle and fragile with all sorts of weird errors. I can do this in my sleep in C, Perl, or Python but in Haskell I can barely make it work when I'm fully conscious :-)
*laughs*
Is there anything concrete we can do? The POSIX layer is supposed to be pretty minimal, so in theory most POSIX idioms should not be harder in Haskell, and hopefully should be easier.
Part of the problem is that you can't always consider the use of
individual POSIX functions in isolation. Things which are done
(possibly unknowingly) in one place might affect the way in which
other system calls behave.
One major issue is the way in which fork() has global consequences.
E.g. if a library has file descriptors for internal use, fork() will
duplicate them. If the library subsequently closes its copy of the
descriptor, but the inherited copy (which the child may not even know
exists) remains open, the file (socket, device, etc) will remain open.
Another example of this is the interaction between buffered streams
and descriptors. If a process forks while "unflushed" data remains in
a stream, the data may be written twice. This can be quite serious if
the stream corresponds to some form of control channel (i.e. a pipe or
socket communicating with another process).
Ultimately, the only real solution to such issues is to ensure that
any high-level functionality provides a sufficient level of
cooperation with lower-level code, e.g. allowing it to be
"synchronised", or at least shut down into a state such that it
doesn't interfere, ensuring that it doesn't hide "unnecessary" details
which may actually be necessary in more involved programs, etc.
--
Glynn Clements

On 2004-10-27, Glynn Clements
One major issue is the way in which fork() has global consequences.
E.g. if a library has file descriptors for internal use, fork() will duplicate them. If the library subsequently closes its copy of the descriptor, but the inherited copy (which the child may not even know exists) remains open, the file (socket, device, etc) will remain open.
This is not a novel problem with Haskell, but it certainly bears remembering.
Another example of this is the interaction between buffered streams and descriptors. If a process forks while "unflushed" data remains in a stream, the data may be written twice. This can be quite serious if the stream corresponds to some form of control channel (i.e. a pipe or socket communicating with another process).
And this *is* a novel problem I hadn't thought of before. It's quite possible this has been causing some of my troubles. I would think that createProcess should automatically flush all open handles before it does the fork, and that it should probably be considered a bug if it doesn't. If it does, then the exec stuff should too. If it doesn't, then the exec stuff must not. (If the exec stuff did but forkProcess didn't, it would break the idiom of fork() then exec() that happens to work now because the exec() discards all those buffers.) I wonder what the behavior of fwrite() in this situation is. I don't know if it ever performs buffering such that write() is never called during a call to fwrite(). I also wonder if the forkIO and friends suffer from the same problem.
Ultimately, the only real solution to such issues is to ensure that any high-level functionality provides a sufficient level of cooperation with lower-level code, e.g. allowing it to be "synchronised", or at least shut down into a state such that it doesn't interfere, ensuring that it doesn't hide "unnecessary" details which may actually be necessary in more involved programs, etc.
Yup. -- John

John Goerzen
I wonder what the behavior of fwrite() in this situation is. I don't know if it ever performs buffering such that write() is never called during a call to fwrite().
On Linux it duplicates unflushed output (hmm, I thought they fixed
this a few years ago). X/Open specification doesn't say anything
about this.
#include

John Goerzen wrote:
I wonder what the behavior of fwrite() in this situation is. I don't know if it ever performs buffering such that write() is never called during a call to fwrite().
fwrite() is no different to other stdio functions in this regard. If
the stream is buffered, a call to fwrite() may simply result in data
being appended to the buffer; it doesn't guarantee a call to write().
--
Glynn Clements
participants (6)
-
Andrew Pimlott
-
Glynn Clements
-
John Goerzen
-
Marcin 'Qrczak' Kowalczyk
-
Peter Simons
-
Simon Marlow