10 ms·
I'm glad to see that setjmp() and longjmp() are still allowed. I'm just kidding by the way. For those C programmers who haven't encountered these before, it i
by alister 7y ago
I'm glad to see that setjmp() and longjmp() are still allowed.
I'm just kidding by the way. For those C programmers who haven't encountered these before, it is a powerful way to do a "goto" in C. Powerful in the sense that you can jump anywhere, not limited to the same function. If it's used at all these days, it's used for exception handling.
More info:
https://en.wikipedia.org/wiki/Setjmp.h https://en.wikipedia.org/wiki/Setjmp.h
- saagarjha 7y agoI see zero use of those in the codebase, so perhaps they should be banned too. Or maybe they’re not on this list because nobody was using them already?
- susam 7y agoI have worked in large code bases (RSA NetWitness, Oracle Database, etc.) where setjmp/longjmp were used to create macros for exception handling using TRY-CATCH-ENDTRY blocks in C.
- saagarjha 7y agoPersonally, I feel like wrapping exception handlers in macros makes it easy for people to do unsafe things with it like dynamically allocating memory or calling system functions that can’t handle it correctly.
- LukeShu 7y agoThe way I see the list is that it isn't meant to be exhaustive, it's meant to be functions that contributors are likely to try to use. A new contributor might not realize that they should be using Git's internal strbuf.h instead of the libc string functions. It's a way to give them feedback on that from their compiler, before they spend more time on the patch and send it to the mailing list.
- rkagerer 7y agoMight be nice if the error messages suggested such alternatives instead of just saying "banned"?
- grogenaut 7y agoI believe the git project welcomes suggestions and discussion via the mailing list, and also accepts patches that like you suggested above.
- masklinn 7y agoAlso seems mostly for functions which look innocent and are likely to be used but are very easy to misuse. setjmp/longjmp is not in this category, it might be argued to be easy to misuse but doesn't look innocent and is not likely to be used in normal codebases.
- slovenlyrobot 7y agoThese are used /everywhere/ in PostgreSQL, exactly for exception handling. The result isn't bad at all, but indeed, too powerful a tool for 99% of developers
- fanf2 7y agoAnd Lua’s exceptions are somewhat odd because they are based on setjmp/longjmp. https://www.lua.org/manual/5.3/manual.html#4.6 https://www.lua.org/manual/5.3/manual.html#4.6
- crankylinuxuser 7y agoAnd that's the perfect usage for goto. Error catching and jump to fail block...
- johannes1234321 7y agoGoto requires the fail block to be in the same frame. It can be a working error strategy for failing critical errors in such a way from a failing module. You have to be aware that lots of stuff is prone to leak (which can be avoided by i.e. using some memory arena) and locks/mutexes/... might be in unclear state, so code has to be aware of that.
- userbinator 7y agoThe destination of the goto is usually the clean-up code.
- johannes1234321 7y agoThat's nice for the single frame. Let's take the example of PostgreSQL, which was mentioned in this thread: A potential implementation (I have no knowledge of pgsql internals) might be that for a single user session all memory is from a single arena, all locks and other handles are tied to the session handle, then it can be an efficient strategy to longjmp out on a critical error (i.e. IO error) and clean the arena as well as things tied to the session. instead of bubbling this up through all frames.
- ryandrake 7y agoI think reasonable people can debate this one. setjmp()/longjmp() are very useful when working with libpng [1] and jpeglib [2], as you mentioned, as a crude exception handling mechanism. I have seen these functions used in production safely for that purpose. I can imagine other horrible uses for them though. Unlike things like strcpy() which are security-holes-by-design, setjmp()/longjmp() should be in a "carefully code review the exact way these are used" list rather than a banned list. 1: http://www.libpng.org/pub/png/libpng.html http://www.libpng.org/pub/png/libpng.html 2: https://www.ijg.org https://www.ijg.org
- slovenlyrobot 7y agoThe trouble is how they compose with the expectations of code you don't write. It's just not normal in C to expect execution of your function to abort halfway through, and so memory leaks and broken state are almost guaranteed when they're mixed with the wrong third party library. It might work great when you wrote it, but it might not even survive the next change to the codebase, and 10 years out, who is to know what programmers will come along and plug new stuff in without noticing the stack magic, etc edit: another aspect of the same issue is that it can be difficult or impossible to write generic longjmp-safe code for many kinds of tasks. In C++ you have stack unwinding to e.g. delete large temporary heap allocations during a complex operation, but no such facility exists in C.
- megous 7y agolongjmp will not interrupt third party code (well maybe if it uses callbacks and you use it from a callback). setjmp/longjmp will almost certainly require tracking any resource use carefully so that resources can be released at the setjmp point, when execution gets back there. I mostly use it for terminating the recursive descent parsing on error. Otherwise it gets tedious to check return values everywhere in hand written code.
- deleted 7y ago[deleted]
- 7y ago
- mlyle 7y agoYou can build coroutines from them, too, which is really cool and useful.
- vasama 7y agoI think you mean fibers, and no you can't.
- tlb 7y agoHere is an implementation of coroutines on top of setjmp/longjmp: https://fanf.livejournal.com/105413.html https://fanf.livejournal.com/105413.html
- vasama 7y agoLooks more like an implementation of undefined behaviour to me.
- monocasa 7y agoThe underlying implementations define the behavior here very well.
- fanf2 7y agoWell, I would not go that far. My coroutine hack relies on “unwarranted chumminess with the implementation” (http://c-faq.com/struct/structhack.html http://c-faq.com/struct/structhack.html) and there are lots of incidental things that can break it. It is best treated as an example of the principles of thread switching and stack allocation.
- craftinator 7y agoDon't be an ass you 'fiber' cretin.
- dang 7y ago"Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something." https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- lcuff 7y agoI remember setjmp() and longjmp() being the only way to do a STREAMS device driver I wrote 30 years ago. Don't know if things have changed since then. Ah, what memories.
- chris_wot 7y agoI've always wondered why goto is actually considered harmful. I really should read the original paper by Djikstra...
- saagarjha 7y agoIt encourages spaghetti code.
- Someone1234 7y agoNot using it can also create spaghetti code. For example the "alternatives" often suggesting for exiting a multi-tiered loop (which often add additional boilerplate, additional variables, etc that all need to be maintained/bug free).
- FartyMcFarter 7y agoIndeed. Despite not having actual goto, Java went out of their way to have labeled loops and the "break label_name" syntax in order to avoid this awkwardness.
- jjuhl 7y ago[RAII](https://en.cppreference.com/w/cpp/language/raii https://en.cppreference.com/w/cpp/language/raii) to the rescue..
- GrumpyYoungMan 7y agoRead David Tribble's "Go To Statement Considered Harmful: A Retrospective" instead: http://david.tribble.com/text/goto.html http://david.tribble.com/text/goto.html . It contains the entire text of Dijkstra's paper and goes over its meaning in a modern context on a paragraph-by-paragraph basis. It's very well written and makes clear the "encourages spaghetti code" argument, while true at the time, applied in a era where basic control flow constructs that we now take for granted did not exist in commonly used languages.
- 7y ago
- Throw_Away_4384 7y ago> If it's used at all these days, it's used for exception handling. What about co-routines?
- f00zz 7y agoYeah, I remember some green threads libraries based on longjmp/setjmp.
- zzo38computer 7y agoI don't use setjmp() and longjmp() often, but sometimes I do; I use goto more often than those (but still not all the time). (I want goto in JavaScript too. I figured out a algorithm to do so, but have not implemented it.) One use of setjmp/longjmp I have used is in ZORKMID, to deal with the debugger. At the beginning of the execute() function I have: while(setjmp(exception_buffer)); (The semicolon is correct; the loop body is supposed to be empty.) Normally, if you enter the debugger and then you exit the debugger to continue the execution, then it will continue from where it left off, which is likely in the middle of the execution of some instruction, and may result the same error again. But, if you change the program counter before continuing execution, then the debugger will keep track of that and will use longjmp instead when continuing execution, therefore skipping the rest of the instruction that stopped.
- pault 7y agoWhat can you accomplish with goto that you can't accomplish with try/catch and custom errors?
- hibbelig 7y ago> What can you accomplish with goto that you can't accomplish with try/catch and custom errors? You can program in C. C doesn't have try/catch...
- dtech 7y agoGoto sounds like a generally bad idea in a more expressive language like Javascript. Can you give an example of some Javascript-with-goto code that would be better than vanilla Javascript?
- zzo38computer 7y agoI have sometimes encountered such thing (but I do not remember right now), but what I do know is that goto is more helpful in C than in JavaScript. That doesn't mean that goto isn't worth anything in JavaScript, but only that there are less circumstances where it is useful compared to the other stuff. (The same is true of macros.)
- jlg23 7y ago> If it's used at all these days, it's used for exception handling. And in all of the CPS code.
- Lerc 7y agoI had quite some fun playing around with it to obfuscate code. I did find that jumping to a different position in the same expression broke on a lot of compilers as soon as optimization was turned on. Here's a simple program to output a string with the characters in an unusual order. #include <stdio.h> #include <setjmp.h> #define J(x,y) (longjmp(x,y),0) int main(int argc, char **argv) { jmp_buf j[011]; int x,X=0; signed char* i = " eehcef lo u ef'oYurls " +021; (x=setjmp(j[X++]))?J(j[x],*++i):((x=setjmp(j[X++]))?J(x[j],*--i):((x= setjmp(j[X++]))?J(x[j],(putchar(*i),*i+=128)):(x=setjmp(j[++X]))?((x< 0)?J(1[j],5):J(2[j],3)):(x=setjmp(j[++X]))?((x>>X)?J(1[j],6):J(1[j],3 )):(x=setjmp(j[++X]))?((x&0x80)?J(1[j],7):J(1[j],5)):(x=setjmp(j[++X] ))?(((X<<5)&x)?J(1[j],4):J(2[j],X)):(x=setjmp(j[++X]))?((x&128)?J(0[j ],X):J(2[j],4)):(setjmp(j[3])>=0)?J(j[2],6):0)); return 0; }
- kazinator 7y agoISO C says that any local variables that are not declared volatile whose values are mutated after the context is saved with setjmp then have indeterminate values when the context is restored with longjmp. So yes, that interacts with optimization. Any variable that is cached in a register may revert to its old value when that register is restored blindly from jmp_buf.
- begriffs 7y ago> the same expression broke on a lot of compilers as soon as optimization was turned on C is not a "try it and see" language. If you stray from the spec then things will act differently and break between compilers. Section 7.13.2 specifies that only local variables declared as volatile will have a determinate value after a longjmp.
- mcherm 7y agoYou, sir, have a crafty and evil mind. Well done.
- valleyer 7y agoThis was super fun to decipher! 1. The first time through this giant ternary defines 9 different small subroutines. Each subroutine can consider the value of "x" to be their argument. X is used as an index into the jmp_buf (subroutine) array during setup, but after that it's always just equal to 8 2. Subroutines 0, 1, and 2 are special. Their argument is the index of another subroutine to jump to. They mutate the character buffer state and then jump to the specified subroutine 3. Before jumping, subroutine 0 moves back a character, subroutine 1 moves forward a character, and subroutine 2 prints the current character and marks it by setting its high bit. Once a character is marked, it will never be printed again 4. All other subroutines get the current character value as their argument 5. Subroutines 3, 4, 5, 6, 7, and 8 all check, in different ways, whether the current character has been marked: x < 0, x >> 8, x & 0x80, (8 << 5) & x, x & 128, x > 0 6. Execution starts at subroutine 3 So you can annotate the code as: // Subroutine 0: // move to next char; // goto x; (x=setjmp(j[X++])) ? J(j[x], *++i) : // Subroutine 1: // move to prev char; // goto x; ((x=setjmp(j[X++])) ? J(x[j], *--i) : // Subroutine 2: // print and mark (set high bit on) curr char; // goto x; ((x=setjmp(j[X++])) ? J(x[j], (putchar(*i), *i+=128)) : // Subroutine 4: // if (curr char is marked) { // // goto 1(5); // move to prev char; // goto 5; // } else { // print and mark curr char; // goto 3; // } (x=setjmp(j[++X])) ? ((x<0) ? J(1[j],5) : J(2[j],3)) : // Subroutine 5: // if (curr char is marked) { // // goto 1(6); // move to prev char; // goto 6; // } else { // // goto 1(3); // move to prev char; // goto 3; // } (x=setjmp(j[++X])) ? ((x >> X) ? J(1[j], 6) : J(1[j], 3)) : // Subroutine 6: // if (curr char is marked) { // // goto 1(7); // move to prev char; // goto 7; // } else { // // goto 1(5); // move to prev char; // goto 5; // } (x=setjmp(j[++X])) ? ((x & 0x80) ? J(1[j], 7) : J(1[j], 5)) : // Subroutine 7: // if (curr char is marked) { // // goto 1(4); // move to prev char; // goto 4; // } else { // print and mark curr char; // goto 8; // } (x=setjmp(j[++X])) ? (((X << 5) & x) ? J(1[j], 4) : J(2[j], X)) : // Subroutine 8: // if (curr char is marked) { // // goto 0(8); // move to next char; // goto 8; // } else { // print and mark curr char; // goto 4; // } (x=setjmp(j[++X])) ? ((x & 128) ? J(0[j], X) : J(2[j], 4)) : // Subroutine 3: // if (curr char is not marked) { // print and mark curr char; // goto 6; // } else { // exit; // } (setjmp(j[3])>=0) ? J(j[2],6) : 0)); // X = 8 // goto 3(0); A more straightforward way of writing this would be #define IS_CURR_CHAR_MARKED() (*i < 0) #define PRINT_AND_MARK_CURR_CHAR() do { putchar(*i); *i += 128; } while (0) int curr_sub = 3; for (;;) { switch (curr_sub) { case 3: if (!IS_CURR_CHAR_MARKED()) { PRINT_AND_MARK_CURR_CHAR(); curr_sub = 6; } else { return 0; } break; case 4: if (IS_CURR_CHAR_MARKED()) { i--; curr_sub = 5; } else { PRINT_AND_MARK_CURR_CHAR(); curr_sub = 3; } break; case 5: if (IS_CURR_CHAR_MARKED()) { i--; curr_sub = 6; } else { i--; curr_sub = 3; } break; case 6: if (IS_CURR_CHAR_MARKED()) { i--; curr_sub = 7; } else { i--; curr_sub = 5; } break; case 7: if (IS_CURR_CHAR_MARKED()) { i--; curr_sub = 4; } else { PRINT_AND_MARK_CURR_CHAR(); curr_sub = 8; } break; case 8: if (IS_CURR_CHAR_MARKED()) { i++; curr_sub = 8; } else { PRINT_AND_MARK_CURR_CHAR(); curr_sub = 4; } break; } }
- smcameron 7y agolibpng uses setjmp and longjump as part of its freakin' API.
- begriffs 7y ago> you can jump anywhere, not limited to the same function Outside of the current function, yes. Anywhere, no. Longjmp allows you to return to places you’ve already been and have saved with setjmp. Also attempting to longjmp to a function that has since returned is undefined behavior. Thus longjmp’s usual use case is like exceptions in other languages, going back up the call chain, skipping intermediate functions.
- fortran77 7y agoThere really are use-cases for it, especially when implementing a Forth-style interpreter in C.
- kijiki 7y agoIt is also the only way for an xlib program to survive losing its connection to the X server. xlib will let you register a callback to handle loss of connection with XSetIOErrorHandler, but unconditionally calls exit() after your callback returns. Which is extremely anti-social behavior for a library, but that is what it does. So the solution is to setjmp before each call to an X function that might notice the connection is closed, and longjmp out of the error callback. Ugly, but it works. These days, you should really just use XCB, or even Wayland.
- LorenPechtel 7y agoThat's nuke-from-orbit level stupidity!
- CorvusCrypto 7y agoCould be wrong but I believe s lot of coroutine implementations use this as well since the call preserves the frame info in the result.
- gnulinux 7y agoI had really fun implementing coroutines, (almost-zero cost) exceptions, iterators etc with setjmp and longjmp. I think they're too useful to be banned outright (especially since C has no view on stack) since there are cases where you need to either use them or inline assembly, but I agree that they're the evilest of evilest std C functions.