guillermocalvo / exceptions4c Goto Github PK
View Code? Open in Web Editor NEW:sheep: An exception handling framework for C
Home Page: https://exceptions4c.guillermo.dev/
License: GNU Lesser General Public License v3.0
:sheep: An exception handling framework for C
Home Page: https://exceptions4c.guillermo.dev/
License: GNU Lesser General Public License v3.0
Use case: add e4c to an library/extension/plugin with an entry point and an optional exit point.
I've tested this:
e4c_context_begin(E4C_TRUE);
e4c_context_end();
If the main software exits property unloading the module, all is fine.
But if it exists without unloading it, e4c_context_end throws a ContextNotEnded exception.
A mechanism to end the context implicitely or ignore the issue would be interesting.
`
int ut_example()
{
e4c_using_context(E4C_TRUE) {
int * pointer = NULL;
try {
int oops = *pointer;
}
catch (BadPointerException) {
printf("No problem ;-)");
fflush(stdout);
}
}
}
int main()
{
while (true)
{
ut_example();
sleep(5);
}
}
`
Output:
No problem ;-)
Segmentation fault (core dumped)
Hi @guillermocalvo,
thank you for publishing exceptions4c
, this is a very nice project. I have some fundamental concerns though, so I hope you take the time to look at the following with an open mind, it is meant with the best of intentions.
I've developed something very similar to exceptions4c
30 years ago, under Win16/Win32s, using macros. Later that was replaced by SEH under Win32, just by redefining the macros. SEH apparently also provides safe capturing of some hardware exceptions like dereferencing null pointers. Now I'm porting to Linux and I've seen your nice exceptions4c
including signals, which would again allow me to capture null pointers, and perhaps new UNIX specific stuff.
But now I'm struggling to understand how exceptions4c
could ever work correctly with signals. After encountering some problems, I read about signals in The Linux Programming Interface (which is brilliant), and unless I'm missing something fundamentally, I don't think this can ever work correctly at all.
Most signals are asynchronous, i.e. the thread will be preempted at any time, very unlike explicitly throwing exceptions synchronously. Normally, a signal handler (callback) will either abort the process, or do minimal stuff, like setting a flag, and then return from the handler, whereupon the thread is resumed from wherever it was interrupted. The resumed thread can then check the flag periodically, and again act synchronously when needed.
Signal handlers are only allowed to call a very limited set of so called "async-signal-safe functions", i.e. the use of most library functions and even some system calls is forbidden, because they may have been interrupted in the middle of an inconsistent transient state (regarding their global/heap data).
https://man7.org/linux/man-pages/man7/signal-safety.7.html
By using non-local goto a.k.a. longjmp()
from the signal handler, exceptions4c
is virtually extending the signal handler indefinitely, i.e. the remainder or the program is subject to the "async-signal-safe functions" restriction, which of course is not practical, except maybe for exception handlers that merely report a failure and then abort()
.
Even if this was not an issue, I believe you would also have to protect the signal handler from being interrupted itself, by more signals, i.e. you should then use sigaction()
with signal masking instead of just signal()
, and make sigsetjmp()
and siglongjmp()
mandatory, instead of alternatively allowing plain setjmp()
and longjmp()
.
Similarly, you would have to mask the phase from sigsetjmp()
until you pushed the new e4c_frame
from being interrupted by signals (and likewise the popping phase). Otherwise, a signal could disrupt the frames (see also next section for more robust frames).
Finally, I also found that the default signal handling is disruptive. Standard functions such as system()
, popen()
, fork()
etc. no longer work, due to SIGCHLD
exceptions being thrown, and wait()
no longer working and hanging. Took me very, very long to figure this one out, it was hidden in a closed-source library and it just hanged itself (ODBC driver, wanting to start the DB engine).
So to sum it up, I don't think exceptions based signal handling can be done. Even C++ cannot do it:
https://stackoverflow.com/questions/6535258/c-exceptions-and-signal-handlers#6536536
As already mentioned in the context of signal-interrupted frame push/pop: It would be better to find a design that no longer works with heap allocated memory and global context->current_frame
but with local variables in the loop that can always be referred to correctly (i.e. locally) before/after any long jump. The e4c_frame
should be allocated on the stack and automatically be "unwound" along with the long jump. This would also automatically "self-repair" when one is mistakenly return
-ing or break
-ing from the try loop. Obviously, the finally
handler (if present) would still be skipped, but having the context->current_frame
off by one indefinitely, is surely worse.
My simple 30-year-old design worked like that, but had its own restrictions, like only one try{ }
allowed per function due to local variable name conflicts (back then using C89). I was happy with the restriction at the time, and guess it could be overcome, using extra nested blocks, more modern C, variable shadowing etc.
Again, hope you take it sportingly, and in case I should have missed something, just say it out loud.
_Mark
Is there any initialization function I need to call before using a try...catch
clause? I'm getting this error:
Uncaught ContextHasNotBegunYet: E4C_TRY: The exception context for this thread has not begun yet.
thrown at custache_compile (/home/derek/code/custache/src/custache.c:281)
The value of errno was 0.
Exception hierarchy
________________________________________________________________
RuntimeException
|
+--ExceptionSystemFatalError
|
+--ContextHasNotBegunYet
________________________________________________________________
This is an unrecoverable programming error; the thread will be terminated
immediately.
Exception system errors occurred during program execution.
I'm compiling my library with E4C_THREADSAFE
enabled.
Hello
I tried exceptions4c in an application with dynamic libraries like this:
program |_ lib1.so (uses exception framework) |_lib2.so (uses exception framework)
In my case, the context is lost between lib1 and lib2 even if the macro e4c_reusing_context
is used.
The first tests were OK but with static libraries only.
I tested test_h08, edited to simulate my case, and here are the results in a static and dynamic way :
** static **
ext1:__ext1_the_context_WAS_NOT_ready ext1:__ext1_before_REUSING_CONTEXT ext1:__ext1_before_TRY_block ext1:__ext1_before_CALL_FUNCTION_ext2 ext2:____ext2_the_context_WAS_ready ext2:____ext2_before_REUSING_CONTEXT ext2:before_CALL_FUNCTION_aux aux:_____aux_no_exception_was_thrown ext2:after_CALL_FUNCTION_aux ext2:after_REUSING_CONTEXT_ext2 ext2:____ext2_the_context_IS_ready ext2:____ext2_there_was_no_error ext1:__ext1_after_CALL_FUNCTION_ext2 ext1:__ext1_inside_FINALLY_block ext1:__ext1_after_TRY_block ext1:__ext1_after_REUSING_CONTEXT ext1:__ext1_the_context_IS_NOT_ready ext1:__ext1_there_was_no_error main:after_CALL_FUNCTION_ext1 main:result_was_0
** dynamic **
main:before_CALL_FUNCTION_ext1 ext1:__ext1_the_context_WAS_NOT_ready ext1:__ext1_before_REUSING_CONTEXT ext1:__ext1_before_TRY_block ext1:__ext1_before_CALL_FUNCTION_ext2 ext2:____ext2_the_context_WAS_NOT_ready ext2:____ext2_before_REUSING_CONTEXT ext2:before_CALL_FUNCTION_aux aux:_____aux_no_exception_was_thrown ext2:after_CALL_FUNCTION_aux ext2:after_REUSING_CONTEXT_ext2 ext2:____ext2_the_context_IS_NOT_ready ext2:____ext2_there_was_no_error ext1:__ext1_after_CALL_FUNCTION_ext2 ext1:__ext1_inside_FINALLY_block ext1:__ext1_after_TRY_block ext1:__ext1_after_REUSING_CONTEXT ext1:__ext1_the_context_IS_NOT_ready ext1:__ext1_there_was_no_error main:after_CALL_FUNCTION_ext1 main:result_was_0
I noticed that in e4c_lite.h you mark e4c
as threadprivate, which looks like an OpenMP directive. Does that mean it will be safe with multiple threads even if I'm not doing anything else OpenMP related?
Consider the following program:
#include "e4c.h"
#include <stdio.h>
#include <stdlib.h>
void run(){
e4c_using_context(E4C_TRUE){
try{
int *s = NULL;
*s = 0;
}catch(BadPointerException){
printf("signal handled\n");
}
}
}
void main(){
run();
run();
}
The first signal is handled while the second is not. It crashes the program with SIGSEGV. This also happens in the case of ArithemticException (dividing by zero for instance).
Place "e4c.h" after the standard headers and the program works as it should do.
I am evaluating the excpetion library and so far it looks great, except that I can't get it to work in a mult-threaded program. Consider this simple experiment:
static void *sleepThread(void *arg)
{
e4c_context_begin(E4C_TRUE);
printf("sleeping...\n");
sleep(5);
printf("done...\n");
e4c_context_end();
}
int main()
{
for (int i=0; i<10; i++)
pthread_create(&pid[i], NULL, sleepThread, NULL);
sleep(60);
return 0;
}
The main thread fires 10 threads that do nothing but sleep (for now), but they support a context for generating exceptions (which I want to handle in the same thread).
The program runs until it comes to e4c_context_end(). There it crashes with various error messages:
*** Error in
./text': double free or corruption (fasttop): 0x00007f5d100008c0 ***
and/or
`Uncaught ContextHasNotBegunYet: e4c_context_end: The exception context for this thread has not begun yet.
When I run the context_begin command with 'false' I usually get the following:
sleeping...
sleeping...
sleeping...
sleeping...
sleeping...
sleeping...
sleeping...
sleeping...
sleeping...
done...
done...
done...
done...
Uncaught ContextHasNotBegunYet: e4c_context_end: The exception context for this thread has not begun yet.
The value of errno was 0.
Exception hierarchy
________________________________________________________________
RuntimeException
done...
|
+--ExceptionSystemFatalError
|
+--ContextHasNotBegunYet
________________________________________________________________
This is an unrecoverable programming error; the thread will be terminated
immediately.
done...
Aborted
Am I doing something wrong, or using the library in the wrong way?
Yes, I read the first issue, but the faq it links to is gone from this repo.
I'm trying a helloworld exception -
#include "e4c.h"
int main(int argc, char **argv) {
int foo;
try{
throw(UserQuitException, "I Quit");
foo = 1;
}catch(UserQuitException){
foo = 123;
}
return 0;
}```
But it fails when it get's to the 'try'. I tried compiling with and without thread support, and I get the same error.
clang version 7.0.0 (tags/RELEASE_700/final)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:\msys64\mingw64\bin
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.