Live data from Hacker News

fork() can fail

rachelbythebay.com

131–140 of 320 posts

Re: fork() can fail

#131
post #124
post #51

Earlier quoted context omitted.

This isn't really fork() failing per se -- but rather a failed program/script that did not understand the well defined and clearly documented behavior of fork().

Taking the square root of a negative number removing all the files in your home directory could be "well defined and clearly documented behavior". Would you blame the API author at that point or would it still be strictly your fault? At what point do API authors share the blame for a needlessly harsh punishment delivered upon a predictably common error? I certainly prefer to work with systems produced by people tendi…

I could argue, however, that in this particular case, it's the user's fault for failing to understand the full and defined behavior of fork() in addition to failing to understand the full and defined behavior of other functions, ...say, kill().

It's just as wrong to feed kill() -1 as it would be to feed it -48585 or "babdkd" (unless that is explicitly your intention). A simple sanity check of if [ "${pid} > "0" ]; is all that's necessary to protect against this behavior.

So, I would argue, the fault lays on the users, not the creator, for not understanding the API when all materials necessary to understand said API are freely available.

(with that said, I think it's safe to say, we've all been bitten by not fully understanding some function before)

Re: fork() can fail

#132
post #29
post #7

This reminds me of one of the most epic bugs I've ever run into: mkdir("/foo", 0700); chdir("/foo"); recursively_delete_everything_in_current_directory(); Running as root, this usually worked fine: It would create a directory, move into it, and clean out any garbage left behind by a previous run before doing anything new. Running as non-root, the mkdir failed, the chdir failed, and it started eating my home directory…

In those times I wish I could use the emacs lisp way: (let (dir "/foo") (create-directory dir) (with-current-directory dir (delete-all-files-recursively))) Factor recognized the value of dynamically scoped variables: http://concatenative.org/wiki/view/Factor/FAQ/What's%20Facto... A lot of code became much simpler because of that decision.

What's wrong with the C version?

    char *dir = "/foo";
    mkdir(dir, 0700);
    if (chdir(dir) == 0)
        delete_all_files();

Re: fork() can fail

#133
post #29

Earlier quoted context omitted.

In those times I wish I could use the emacs lisp way: (let (dir "/foo") (create-directory dir) (with-current-directory dir (delete-all-files-recursively))) Factor recognized the value of dynamically scoped variables: http://concatenative.org/wiki/view/Factor/FAQ/What's%20Facto... A lot of code became much simpler because of that decision.

What's wrong with the C version? char *dir = "/foo"; mkdir(dir, 0700); if (chdir(dir) == 0) delete_all_files();

Nothing, of course. I just find the macros that give you a "modified environment" to run some code short and sweet.

with-temp-buffer is another example: a macro that bridges the functions for "string manipulation" and the ones for "buffer manipulation", since you start writing stuff this way:

  (defun replace-in-string (str from to) 
    (with-temp-buffer
      (insert str)
      (beginning-of-file)
      ;;; Here you can use all your normal text editing commands
      (replace-regexp from to nil t)         
      (buffer-string)))
Lots of dirty manipulation, but from outside its a pure function, and doesn't change the editor state in any way after it runs.

Re: fork() can fail

#134
post #81
post #79

I don't use fork() that often, but my own paranoia is why I always test for if len(some_list) But it's just my way of covering my ass in case the laws of physics change during execution, or just in case weird bugs exist like those found in this article.

Perhaps I'm misunderstanding what you're saying, but that's wrong too. With fork() you need to always test 3 cases: * -1: error * 0: success, in child * > 0: success, in parent Testing for <= 0 would cause you to think there's an error when you're really just the child process.

You only need 3 cases if you need to distinguish between the parent and the child. I can imagine designs where the parent and child are both going to exec the same program and so all you need to check on the fork is for success or failure.

Re: fork() can fail

#135
post #20

If a function be advertised to return an error code in the event of difficulties, thou shalt check for that code, yea, even though the checks triple the size of thy code and produce aches in thy typing fingers, for if thou thinkest "it cannot happen to me", the gods shall surely punish thee for thy arrogance. [0] [0]: http://www.lysator.liu.se/c/ten-commandments.html

Counterexample: pthread_mutex_unlock. That function returns an error code, but it cannot possibly fail in a well-formed program. Checking for an error for mutex unlock is pointless: what would you do in response?

In the late 90s I had an app ported to multiple unixes. We were having intermittent problems with our HP/UX port which was caused by a mutex being unlocked by a different thread than which had locked it. In that case (which only happened under heavy worker contention) unlock happily returned an error and the mutex was left locked.

Be careful about "can't possibly fail".

Re: fork() can fail

#136
post #101

Earlier quoted context omitted.

Similarly python throws an exception, and I bet other languages have their own behaviors, but in case of C this is the only way (or at least it is the only non complicated way to do it). When I read this article I thought it was preaching to a choir. I'm actually quite surprised people programming C don't check for errors. That's the only way the functions can provide a feedback.

Nothing in C forces the API designer to use -1 as "bad PID" in one place and as "the set of all PIDs" in another, however. Perl's undef isn't that different from returning, gosh, -2 or any other bloody number except -1 in C.

Actually this is not true.

fork() returns pid_t type which is usually mapped to int32_t. For this type there's no equivalent of Perl's "undef", the -1 is standardized as an error in all system calls that return an integer.

As for the argument why not send -2 instead, well guess what? Other negative values also have a meaning. Negative values in kill send signal to a process group instead of a process.

It's not libc responsibility to predict all possible things the programmer can do. Also unlike perl, C doesn't have exceptions so it can't exactly quickly terminate on error showing what went wrong.

Imagine C throwing SIGSEGV every single time a function failed.

Re: fork() can fail

#139
post #73

And this right there is why exceptions are a superior mechanism of announcing errors...

Multiple return would be fine too. pid,err = fork()

Are you sure?

And what if $programmer forgets to check what's in err? What would pid contain in that case?

I mention this because I guess you quoted a kind of syntax that matches the one from Go.

So then I'm guessing that Go would simply ignore the error in this case.

However, having a proper exception mechanism, if you don't catch the problem, then it bubbles up, and the program doesn't continue with wrong data (which is a good thing => fail fast!).

This is the biggest downside of GoLang IMNSHO.

Re: fork() can fail

#140

Earlier quoted context omitted.

Or that you're the child process when really there's an error, which might go undetected longer.

Thankfully that case will never happen because you can't be a child if there was an error. :) Also the check as presumably written would never miss an error, it would just potentially assume valid return values were also errors.

"Thankfully that case will never happen because you can't be a child if there was an error. :)"

Uh, no. If there is an error, there will be no child process but the parent process will think it is the child.

From the fork man page, emphasis mine: "On success, the PID of the child process is returned in the parent, and 0 is returned in the child."

"Also the check as presumably written would never miss an error, it would just potentially assume valid return values were also errors."

That was already discussed as a possibility; I was addressing the other. In the case you describe the software would never work at all, even when fork successfully forks, because the child will always think there was an error and presumably fall over rather than getting things done. That's probably the better case, in terms of development progress, because it would be spotted and fixed right away. But hopefully fixed correctly, and not converted to the broken-but-working-when-fork-succeeds other variant that also uses "<= 0".

Post reply on HN