5 ms·
Posts like this always perplex me, the behavior is clearly documented in the man page and clearly indicated by example demonstration code (e.g. [0]), so how cou
by dooglius 7y ago
Posts like this always perplex me, the behavior is clearly documented in the man page and clearly indicated by example demonstration code (e.g. [0]), so how could someone fall under the impression that this wasn't the case?
[0] https://en.wikipedia.org/wiki/Fork_(system_call) https://en.wikipedia.org/wiki/Fork_(system_call)
- xeromal 7y agoPSAs don't hurt anyone and might help one who happened to miss it.Do you see anything negative with the post?
- dooglius 7y agoI don't have a problem with the post, I'm just wondering how one gets into a situation where it needs to be said.
- chousuke 7y agoI think the actual problem here is that fork has a bad API that is easy to misuse accidentally; and when something can go wrong, it eventually will go wrong. Raising awareness about bad APIs will also help people realize when they're about to create one themselves, which might cause them to switch designs.
- ColanR 7y agoFork has an extremely standard API, and that's a good thing. What needs to be quantified is how much ignorance a programmer can be expected to have, before errors they make are their own responsibility.
- chousuke 7y agoThe API being "standard" doesn't change it being bad. I mean, it is what it is and probably couldn't have been much better given historical reasons, but I think it's still useful to think on how it fails and how to improve on it.
- ColanR 7y agoAt the low level of C and Unix, text is the language of the API. Something has to be that low-level - either the OS and the language it's written in, or the extra level of abstraction supporting the OS and its implementation language (in which case the complexity is much higher, and a lot more bugs would be exposed). Either way, there's going to be some point in the layers of abstraction where these kinds of errors are possible. I'm not seeing how an overall improvement could be made to the API without adding abstractions. To me, it seems better that there are fewer layers so that the architecture is clear, the elements of the OS and language are known, and the points where such errors are possible are clearly marked - as was done in this article submission.
- bachmeier 7y agoAre you claiming you read the full documentation and the corresponding Wikipedia article every time you call a function in your program?
- dooglius 7y agoIf I didn't remember how to use a function, I would certainly look at either the documentation or an example, yes.
- dagenix 7y agoAnd you've never run into a case where you thought you remembered how to use a function, but there was actually something you didn't realize you had forgotten?
- noelwelsh 7y agoComments like this always perplex me. Every driver knows to not run red lights, yet people regularly do it. How could someone fall under the impression that people always act with full care and concentration on every task they undertake?
- dooglius 7y agoA post telling people they aren't supposed to run red lights would also be strange, since that is well known. Are you saying that people who don't check fork for errors are doing so knowingly? That is not the impression I got from the post.
- dubcanada 7y agoIf you write code implementing a unix function (fork, sleep, blah, blah) you should know how the underlying function works and implement it properly and with correct error checking.
- im3w1l 7y agoI think you mean "calling"? Anyway this advice is very clearly not being followed. It's not uncommon that correct error handling is impossible. Because of "specification bugs" interacting edge cases can cause undetectable failures. And those problems have been this way for decades. The world you wish for doesn't exist. Not saying it shouldn't.
- jolmg 7y agoYet, there are commercials and other publications about not texting/drinking and driving.
- erobbins 7y agoShould cars prevent people from running red lights? There are a few unusual circumstances when this is something you want to do intentionally and knowing the risks.
- jcranmer 7y agoThe point of this blog post isn't so much "hey, fork can fail" but pointing out that "if you fail to handle fork failing, the outcome is really bad." Fork's error result is a legal input to kill, but one which has really nasty semantics. It's also a legal input to wait, but has somewhat more benign semantics.
- DoofusOfDeath 7y agoI'm having trouble understanding the mindset / reasoning a programmer would use for not checking the return code of a syscall that could fail. For someone who codes for a living and would be inclined to not check `fork` for an error code, would you mind sharing a bit about why you use that approach?
- thestoicattack 7y agoIf I didn't know it could fail, I wouldn't check for an error in its return code.
- DoofusOfDeath 7y agoCool. So here's what I'm trying to understand... Usually when I write code, I assume it's my job to ensure the code works as designed in all reasonable circumstances. That requires understanding the failure modes of any external code I'm calling, at least as far as if/how it could fail. IIUC, your approach is more like what I'd call prototyping. I.e., you accept that the code might be wrong, but you prefer to deal with that if/when it comes up in testing, so that you can move faster up-front. Is that how you reason about it?
- thestoicattack 7y agoSorry, I see I didn't answer your question. I am not one of those programmers that would deliberately ignore an error code, so I don't know what they're thinking. My suspicion (and naive hope) is that most people would not ignore that case if they knew it was dangerous. But I would expect them to ignore it if they didn't know it existed. Hence this article, I guess, which serves to remind everyone that it can in fact happen and is quite dangerous (due to a potential later interaction with kill).
- MaulingMonkey 7y agoDo you error check printf? I don't - the default behavior of simply ignoring I/O failures if, say, stdout's pipe was broken is usually what I want. In fact, I've had bugs in exception throwing languages where such stdout write failures threw, and I failed to explicitly catch and ignore them. I also may skip error checking malloc. A null pointer exception / sigsegv / access violation "must" be fine if malloc is failing - even if I handle it nonfatally in our code, some of our closed source middleware doesn't, and neither do some system libraries. At best I can make slightly better fatal error messages for a subset of the resulting failures. If I'm trying to build super reliable software, I need to avoid exhausting/fragmenting memory badly enough for malloc to fail in the first place. I have a decent chance of checking fork() for failure as I'm on the more paranoid end of error checking. I've seen enough weird junk like SetCurrentDirectory on a real directory failing due to NTFS filesystem corruption leading to an infinite loop - that I assume all documented error conditions will eventually occur somehow, as well as some undocumented ones. But I've never seen it fail, and I'm probably just going to make it a fatal error.
- MaulingMonkey 7y ago> the behavior is clearly documented Disagree - the unfortunate interaction with kill is not clearly documented. Do you implement error checking when calling printf, unlike every C codebase I've ever encountered which uses it? If not, you've implicitly acknowledged some error cases just aren't worth handling, or useful to handle. The question is then - when is it important? PSAs like this one make it clear where the documentation may not have: Error handling fork() is important, and unlike error handling malloc where free(nullptr)ing later is a safe noop, kill(-1)ing later is an unsafe hazard that must be avoided. Additionally, it's frequently the case that the documentation is poor and would not help you even when you do bother to read it. Here's me previously ranting that the vast majority of documentation about atoi fail to clearly and adequately call out that atoi("a") is undefined behavior and citing my sources: https://news.ycombinator.com/item?id=14861447 https://news.ycombinator.com/item?id=14861447 > so how could someone fall under the impression that this wasn't the case? Continuing past my atoi example... Maybe they looked at alternative, poorer documentation. Maybe they looked at poor example code that didn't bother with error handling. Maybe they looked at decent documentation that failed to adequately stress the importance of error checking (EDIT: I'd argue this includes your wikipedia example). Hell, maybe they looked at great documentation - about a specific platform's implementation of fork, which perhaps makes fork() failing fatal to the calling process and thus "infalliable". Maybe they looked at the documentation for their favorite language's wrapper of fork, which throws an exception instead. Maybe they didn't look at the documentation at all. Maybe they learned of fork through word of mouth when the internet was down on a system without manpages. "Can fork ever fail?" "Hmm... I've never seen it fail." "Good enough for me!" Perhaps this lack of knowledge can only come about by foolishness - but human nature and statistics mean at least one of your generally smart coworkers has probably fallen prey to such foolishness.
- wahern 7y ago> Do you implement error checking when calling printf, unlike every C codebase I've ever encountered which uses it? There's a reason that Unix sends EPIPE to a process when writing to a broken pipe, and why the default handler for SIGPIPE is to terminate the process. Interestingly, the Rust runtime blocks SIGPIPE, which was a naive and dumb thing to do [1], but which is now impossible to undo. Similarly, in C the fail flag for FILE objects is persistent to permit alternative error management strategies. This is even carried over into Go, AFAIU. Basically, it's okay to leave a series of I/O statements unchecked so long as you check at the end of the block or transaction, or at the very least at close time. The printf case is a bad example because the inconvenience of checking for failure on every call has already been accounted for. I don't think I've ever seen C code that fails to check fork for an error condition, though I'm usually only ever reading my code and the code of widely used open source projects. [1] Considering how much Rust touts the ease of FFI and integration with C and C++ projects.
- salgernon 7y agoTo me the takeaway shouldn’t be that fork() can fail, rather that kill(-1, ...) has effects that extend outside of the scope of your process. This is documented too, but it far less intentionally used. (Giving a pid_t an initial value of -1 could be considered a good practice, right up until you hit a code path that fails to check this and you kill your parent!)
- nneonneo 7y agoA saner design would have been to split that API up, e.g. “killgrp” to kill groups of processes by whatever identifier (current process group, foreign process group, all processes, etc.). This way, your intent is encoded in the function you call, which is much harder to screw up.