Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow registering global handlers for GAP_TRY/GAP_CATCH #4080

Merged
merged 1 commit into from
Jul 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
}

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 ]