Skip to content

Commit

Permalink
Allow registering global handlers for GAP_TRY/GAP_CATCH.
Browse files Browse the repository at this point in the history
  • Loading branch information
rbehrends authored and fingolfin committed Jul 23, 2020
1 parent 9fe4735 commit e6f3801
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Makefile.rules
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ testbugfix: all
check: all
$(TESTGAP) $(top_srcdir)/tst/testinstall.g

LIBGAPTESTS := $(addprefix tst/testlibgap/,basic api wscreate wsload)
LIBGAPTESTS := $(addprefix tst/testlibgap/,basic api wscreate wsload trycatch)

# run a test in tst/testlibgap
tst/testlibgap/%: build/obj/tst/testlibgap/%.c.lo build/obj/tst/testlibgap/common.c.lo libgap.la
Expand Down
32 changes: 27 additions & 5 deletions src/sysjmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@ enum { signalSyLongjmpFuncsLen = 16 };

static voidfunc signalSyLongjmpFuncs[signalSyLongjmpFuncsLen];

Int RegisterSyLongjmpObserver(voidfunc func)
int RegisterSyLongjmpObserver(voidfunc func)
{
Int i;
for (i = 0; i < signalSyLongjmpFuncsLen; ++i) {
for (int i = 0; i < signalSyLongjmpFuncsLen; ++i) {
if (signalSyLongjmpFuncs[i] == func) {
return 1;
}
Expand All @@ -43,10 +42,33 @@ Int RegisterSyLongjmpObserver(voidfunc func)
return 0;
}

enum { tryCatchFuncsLen = 16 };

static TryCatchHandler tryCatchFuncs[tryCatchFuncsLen];

int RegisterTryCatchHandler(TryCatchHandler func)
{
for (int i = 0; i < tryCatchFuncsLen; ++i) {
if (tryCatchFuncs[i] == func) {
return 1;
}
if (tryCatchFuncs[i] == 0) {
tryCatchFuncs[i] = func;
return 1;
}
}
return 0;
}

void InvokeTryCatchHandler(TryCatchMode mode)
{
for (int i = 0; i < tryCatchFuncsLen && tryCatchFuncs[i]; ++i)
(tryCatchFuncs[i])(mode);
}

void GAP_THROW(void)
{
Int i;
for (i = 0; i < signalSyLongjmpFuncsLen && signalSyLongjmpFuncs[i]; ++i)
for (int i = 0; i < signalSyLongjmpFuncsLen && signalSyLongjmpFuncs[i]; ++i)
(signalSyLongjmpFuncs[i])();
longjmp(STATE(ReadJmpError), 1);
}
2 changes: 1 addition & 1 deletion src/sysjmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@
** This function is idempotent -- if a function is passed multiple times
** it is still only registered once.
*/
Int RegisterSyLongjmpObserver(voidfunc);
int RegisterSyLongjmpObserver(voidfunc);

#endif // GAP_SYSJMP_H
32 changes: 31 additions & 1 deletion src/trycatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,37 @@

#include "funcs.h" // for SetRecursionDepth
#include "gapstate.h"
#include "system.h" // for NORETURN

#include <string.h> // for memcpy

/****************************************************************************
**
*T TryCatchMode
*T TryCatchHandler
*F RegisterTryCatchHandler()
*F InvokeTryCatchHandler()
**
** The function RegisterTryCatchObserver() allows the installation of
** global exception handlers that are being called whenever GAP_TRY or
** CALL_WITH_CATCH() code is executed. It returns 1 if installing the
** handler was successful, 0 otherwise. Installation can only fail if one
** attempts to install more handlers than the allotted maximum (currently
** 16).
**
** The mode parameter of the handler function signals whether it has been
** called at the beginning of the section, at the end of the section
** without an error being raised, or at the end of a section with an error
** being raised, respectively. The function InvokeTryCatchObserver() is
** used to invoke those handlers as needed.
*/
typedef enum { TryEnter = 0, TryLeave = 1, TryCatch = 2 } TryCatchMode;

typedef void (*TryCatchHandler)(TryCatchMode mode);

int RegisterTryCatchHandler(TryCatchHandler func);
void InvokeTryCatchHandler(TryCatchMode mode);


/****************************************************************************
**
Expand Down Expand Up @@ -60,12 +88,14 @@
jmp_buf gap__jmp_buf; \
volatile Int gap__recursionDepth = GetRecursionDepth(); \
memcpy(gap__jmp_buf, STATE(ReadJmpError), sizeof(jmp_buf)); \
InvokeTryCatchHandler(TryEnter); \
if (!setjmp(STATE(ReadJmpError))) \
for (gap__i = 1; gap__i; gap__i = 0, \
InvokeTryCatchHandler(TryLeave), \
gap_restore_trycatch(gap__jmp_buf, gap__recursionDepth))

#define GAP_CATCH \
else for (gap__j = 1, \
else for (gap__j = 1, InvokeTryCatchHandler(TryCatch), \
gap_restore_trycatch(gap__jmp_buf, gap__recursionDepth); \
gap__j; gap__j = 0)

Expand Down
42 changes: 42 additions & 0 deletions tst/testlibgap/trycatch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Small program to test libgap linkability and basic working
*/
#include "trycatch.h"
#include "common.h"

static int level = 0;

static void handle_trycatch(TryCatchMode mode)
{
switch (mode) {
case TryEnter:
level++;
if (level == 1)
printf("Entering GAP_TRY section\n");
break;
case TryLeave:
if (level == 1)
printf("Leaving GAP_TRY section\n");
level--;
break;
case TryCatch:
if (level == 1)
printf("Caught error in GAP_TRY section\n");
level--;
break;
}
}

int main(int argc, char ** argv)
{
printf("# Initializing GAP...\n");
GAP_Initialize(argc, argv, 0, 0, 1);
RegisterTryCatchHandler(handle_trycatch);
test_eval("OnBreak := false;;");
// Necessary to redirect error printing to stdout.
test_eval("MakeReadWriteGVar(\"ERROR_OUTPUT\");");
test_eval("ERROR_OUTPUT := MakeImmutable(\"*stdout*\");;");
test_eval("Display(CALL_WITH_CATCH(function() return 314; end, []));;");
test_eval("Display(CALL_WITH_CATCH(function() return [][1]; end, []));;");
return 0;
}
18 changes: 18 additions & 0 deletions tst/testlibgap/trycatch.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Initializing GAP...
gap> OnBreak := false;;

gap> MakeReadWriteGVar("ERROR_OUTPUT");

gap> ERROR_OUTPUT := MakeImmutable("*stdout*");;

gap> Display(CALL_WITH_CATCH(function() return 314; end, []));;
Entering GAP_TRY section
Leaving GAP_TRY section
[ true, 314 ]

gap> Display(CALL_WITH_CATCH(function() return [][1]; end, []));;
Entering GAP_TRY section
Error, List Element: <list>[1] must have an assigned value
Caught error in GAP_TRY section
[ false, 0 ]

0 comments on commit e6f3801

Please sign in to comment.