One design decision I made here that I’ve come to regret is basing error handling on setjmp/longjmp; I did this, somewhat embarrassingly, mostly for the sake of syntactic convenience. This pair of functions causes a number of headaches:
- They are somewhat expensive:
setjmp has to write the whole register file into jmp_buf, which is 200 bytes on x86-64 and 156 on x86-32;
- They are hard for static analysers to reason about: I have seen cppcheck emit a spurious warning about using an uninitialised
struct bsp_ec in the error branch, even though I know bsp_die would initialise it;
- They are hard to use safely: there are some hazards with
setjmp clobbering variables stored in registers that I haven’t read much on; to avoid those, one should declare all local variables at the setjmp call site volatile. I only manage to get away with not doing this because glibc marks setjmp with a returns_twice attribute that ensures no variables are stored in registers across this function call. (I didn’t want to apply this attribute in my own code though, which is why bsp_try is a macro and not a function.)
- They may cause issues in code that isn’t aware of them: each time I write code that may be
longjmped over, I must be careful not to introduce resource leaks. The implementation of the menu opcode is one example where the code is much more complicated than I’d like: I have to create another error context just to be able to free the memory allocation and pass the error along using bsp_rethrow.
I should probably switch to using error codes as return values. They aren’t too pretty, but they are at least safer.
(Good thing I haven’t committed to a stable API/ABI yet!)
One design decision I made here that I’ve come to regret is basing error handling on
setjmp/longjmp; I did this, somewhat embarrassingly, mostly for the sake of syntactic convenience. This pair of functions causes a number of headaches:setjmphas to write the whole register file intojmp_buf, which is 200 bytes on x86-64 and 156 on x86-32;struct bsp_ecin the error branch, even though I knowbsp_diewould initialise it;setjmpclobbering variables stored in registers that I haven’t read much on; to avoid those, one should declare all local variables at thesetjmpcall sitevolatile. I only manage to get away with not doing this because glibc markssetjmpwith areturns_twiceattribute that ensures no variables are stored in registers across this function call. (I didn’t want to apply this attribute in my own code though, which is whybsp_tryis a macro and not a function.)longjmped over, I must be careful not to introduce resource leaks. The implementation of themenuopcode is one example where the code is much more complicated than I’d like: I have to create another error context just to be able to free the memory allocation and pass the error along usingbsp_rethrow.I should probably switch to using error codes as return values. They aren’t too pretty, but they are at least safer.
(Good thing I haven’t committed to a stable API/ABI yet!)