Skip to content
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
7 changes: 7 additions & 0 deletions src/gasman.c
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,13 @@ void SetExtraMarkFuncBags(TNumExtraMarkFuncBags func)
}



void SetStackBottomBags(void * StackBottom)
{
StackBottomBags = StackBottom;
}


void InitBags (
UInt initial_size,
Bag * stack_bottom,
Expand Down
2 changes: 2 additions & 0 deletions src/gasman.h
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,8 @@ typedef void (* TNumCollectFuncBags) ( void );
extern void InitCollectFuncBags (
TNumCollectFuncBags before_func,
TNumCollectFuncBags after_func );

extern void SetStackBottomBags(void * StackBottom);
#endif

// ExtraMarkFuncBags, if not NULL, is called during garbage collection
Expand Down
65 changes: 65 additions & 0 deletions src/libgap-api.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// LibGAP API - API for using GAP as shared library.

#include <signal.h>

#include "libgap-api.h"

#include "ariths.h"
#include "bool.h"
#include "calls.h"
#include "gap.h"
#include "gapstate.h"
#include "gasman.h"
#include "gvars.h"
#include "integer.h"
#include "lists.h"
Expand Down Expand Up @@ -354,3 +357,65 @@ Obj GAP_CharWithValue(UChar obj)
{
return ObjsChar[obj];
}

syJmp_buf * GAP_GetReadJmpError(void)
{
return &(STATE(ReadJmpError));
}


static volatile sig_atomic_t EnterStackCount = 0;


// These are wrapped by the macros GAP_EnterStack() and GAP_LeaveStack()
// respectively.
void GAP_EnterStack_(void * StackTop)
{
if (EnterStackCount < 0) {
EnterStackCount = -EnterStackCount;
}
else {
if (EnterStackCount == 0) {
#ifdef USE_GASMAN
SetStackBottomBags(StackTop);
#endif
}
EnterStackCount++;
}
}

void GAP_LeaveStack_(void)
{
EnterStackCount--;
}

void GAP_EnterDebugMessage(char * message, char * file, int line)
{
fprintf(stderr, "%s: %d; %s:%d\n", message, EnterStackCount, file,
line);
}

int GAP_Error_Prejmp_(const char * file, int line)
{
GAP_ENTER_DEBUG_MESSAGE("Error_Prejmp", file, line);
if (EnterStackCount > 0) {
return 1;
}
return 0;
}

/* Helper function for GAP_Error_Postjmp_ (see libgap-api.h) which manipulates
* EnterStackCount in the (generally unlikely) case of returning from a longjmp
*/
void GAP_Error_Postjmp_Returning_(void)
{
/* This only should have been called from the outer-most
* GAP_EnterStack() call so make sure it resets the EnterStackCount;
* We set EnterStackCount to its negative which indicates to
* GAP_EnterStack that we just returned from a long jump and should
* reset EnterStackCount to its value at the return point rather than
* increment it again */
if (EnterStackCount > 0) {
EnterStackCount = -EnterStackCount;
}
}
128 changes: 128 additions & 0 deletions src/libgap-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,134 @@
#include "system.h"


#ifdef __GNUC__
#define GAP_unlikely(x) __builtin_expect(!!(x), 0)
#else
#define GAP_unlikely(x) (x)
#endif


#ifndef GAP_ENTER_DEBUG
#define GAP_ENTER_DEBUG 0
#endif


extern syJmp_buf * GAP_GetReadJmpError(void);
extern void GAP_EnterDebugMessage_(char * message, char * file, int line);
extern void GAP_EnterStack_(void *);
extern void GAP_LeaveStack_(void);
extern int GAP_Error_Prejmp_(const char *, int);
extern void GAP_Error_Postjmp_Returning_(void);


static inline int GAP_Error_Postjmp_(int JumpRet)
{
if (GAP_unlikely(JumpRet != 0)) {
GAP_Error_Postjmp_Returning_();
return 0;
}

return 1;
}


#if GAP_ENTER_DEBUG
#define GAP_ENTER_DEBUG_MESSAGE(message, file, line) \
GAP_EnterDebugMessage_(message, file, line)
#else
#define GAP_ENTER_DEBUG_MESSAGE(message, file, line) \
do { \
} while (0)
#endif


// Code which uses the GAP API and/or which keeps references to any GAP
// objects in local variables must be bracketed by uses of GAP_EnterStack()
// and GAP_LeaveStack(), in particular when using the GASMAN garbage
// collector; otherwise GAP objects may be garbage collected while still in
// use.
//
// In general user code should use the more general GAP_Enter()/Leave() macros
// defined below, as these also specify a terminal point for unhandled GAP
// errors to bubble up to. However, GAP_EnterStack() and GAP_LeaveStack()
// should still be used in the defintion of a custom error handling callback as
// passed to GAP_Initialize(). Using the more general GAP_Enter() in this case
// will result in crashes if the error handler is entered recursively (you
// don't want the GAP error handling code to cause a longjmp into the error
// callback itself since then the error callback will never be returned from).
#ifdef __GNUC__
#define GAP_EnterStack() \
do { \
GAP_ENTER_DEBUG_MESSAGE("EnterStack", __FILE__, __LINE__); \
GAP_EnterStack_(__builtin_frame_address(0)); \
} while (0)
#elif defined(USE_GASMAN)
#error GASMAN requires a way to get the current stack frame base address \
for the GAP_EnterStack() macro; normally this uses the \
__builtin_frame_address GNU extension so if this is not available \
it is necessary to provide your own implementation here.
#else
// If we're not using GASMAN in the first place GAP_EnterStack_() is not
// strictly needed, and can just be called with a dummy value
#define GAP_EnterStack() \
do { \
GAP_ENTER_DEBUG_MESSAGE("EnterStack", __FILE__, __LINE__); \
GAP_EnterStack_(0); \
} while (0)
#endif

#define GAP_LeaveStack() \
GAP_LeaveStack_();


#define GAP_Error_Setjmp() \
(GAP_unlikely(GAP_Error_Prejmp_(__FILE__, __LINE__)) || \
GAP_Error_Postjmp_(sySetjmp(*GAP_GetReadJmpError())))


// Code which uses the GAP API exposed by this header file should sandwich any
// such calls between uses of the GAP_Enter() and GAP_Leave() macro as follows:
//
// int ok = GAP_Enter();
// if (ok) {
// ... // any number of calls to GAP APIs
// }
// GAP_Leave();
//
// This is in particular crucial if your code keeps references to any GAP
// functions in local variables: Calling GAP_Enter() ensures that GAP is aware
// of such references, and will not garbage collect the referenced objects.
// Failing to use these macros properly can lead to crashes, or worse, silent
// memory corruption. You have been warned!
//
// Note that due to the implementation of these macros, you unfortunately
// cannot "simplify" the above example code to:
//
// if (GAP_Enter()) { ... } GAP_Leave();
//
// Some notes on the implementation:
//
// GAP_Enter() is a combination of GAP_Error_Setjmp() and GAP_EnterStack().
// It must call GAP_Error_Setjmp() first, to ensure that writing
// ``int ok = GAP_Enter();'' works as intended (the value assigned to ok then
// is the return value of GAP_Error_Setjmp).
//
// * GAP_EnterStack() defined and explained above must be a macro since it
// needs to figure out (to the extent possible) the base address of the stack
// frame from which it is called.
//
// * GAP_Error_Setjmp() effectively calls setjmp to the STATE(ReadJmpError)
// longjmp buffer, so that read errors which occur in GAP that are not
// otherwise "handled" by a TRY_IF_NO_ERROR { } block have a logical place
// to return to. It returns 1 if no error occurred, and 0 if returning from
// an error.
#define GAP_Enter() \
GAP_Error_Setjmp(); \
GAP_EnterStack()

#define GAP_Leave() GAP_LeaveStack()


////
//// Setup and initialisation
////
Expand Down