Coder Social home page Coder Social logo

guillermocalvo / exceptions4c Goto Github PK

View Code? Open in Web Editor NEW
270.0 15.0 27.0 2.75 MB

:sheep: An exception handling framework for C

Home Page: https://exceptions4c.guillermo.dev/

License: GNU Lesser General Public License v3.0

C 59.93% C++ 32.84% Makefile 5.45% M4 0.87% CSS 0.01% HTML 0.90%
c library exceptions exception-handling error-handling signal-handling try-catch c-library

exceptions4c's Introduction

exceptions4c's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

exceptions4c's Issues

Add an option not to throw ContextNotEnded

Use case: add e4c to an library/extension/plugin with an entry point and an optional exit point.

I've tested this:

  • init code: e4c_context_begin(E4C_TRUE);
  • exit code: 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.

Signal handling not compatible with exceptions

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.

Background

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.

Problem: Signals are asynchronous

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

More robust frames

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

Context has not begun yet?

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.

Exception context lost while using dynamic exception-aware libraries

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

Is e4c_lite pthread safe?

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?

Signal is not handled twice with a specific order of headers.

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.

Mutli-threading fails, though E4C_THREADSAFE is defined

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?

Uncaught ContextHasNotBegunYet: E4C_TRY

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.