-
Notifications
You must be signed in to change notification settings - Fork 175
Prototype for GAP_Enter/Leave macros #3096
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,13 +10,16 @@ | |
|
|
||
| // 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" | ||
|
|
@@ -26,6 +29,7 @@ | |
| #include "streams.h" | ||
| #include "stringobj.h" | ||
|
|
||
|
|
||
| // | ||
| // Setup and initialisation | ||
| // | ||
|
|
@@ -346,3 +350,65 @@ Obj GAP_CharWithValue(UChar obj) | |
| { | ||
| return ObjsChar[obj]; | ||
| } | ||
|
|
||
| syJmp_buf * GAP_GetReadJmpError(void) | ||
| { | ||
| return &(STATE(ReadJmpError)); | ||
| } | ||
|
|
||
|
|
||
| static volatile sig_atomic_t EnterStackCount = 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this need to be
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. None specifically, but my concern here is that one could: E.g. a signal handler installed by GAP which runs and results in an error, which can in turn result in running code that does modify this variable. I could try to concoct a real example, but something like that seems plausible.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GAP never installs such a signal handler, and indeed, calling any kind of GAP code from a signal handler would be very dangerous and is unlikely to work. The reason I am asking is not that I want to pain the bikeshed; but rather it is that using This is why I ask whether it is really necessary, and if so, that a comment be added explaining it. Even if that comment says something like "// It is unclear whether we need sig_atomic_t here, but just out of paranoia, we use it anyway". That said, I'd still prefer if it was not used; I honestly can't think of any example where it might be used that is not horribly unsafe anyway. So if you can think of any, I'd be most interested to hear about it, even if it is just a rough sketch. The argument that one "could" need it seems week to me: with that argument, perhaps we should also add a mutex protecting access to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure on what basis you can assert that it "adds considerable...complexity". Exactly what complexity is it adding? It's really just a guarantee that if this code happens to be interrupted by a signal (with a signal handler that returns) it will leave things in a sane state. For a signal handler that doesn't return--i.e. exits the process or longjumps to somewhere arbitrary) you can't make any further guarantees, but at the very least you can safely increment and decrement it even if a signal occurs. That's all. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Just to clarify this: the type So I actually agree with @fingolfin here: better not make any guarantees about atomicity.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True--in that case it's probably best avoided. I know in Cysignals itself Regarding your idea, I'm not saying it can't be done. I just don't understand exactly what you're proposing. Keep in mind these macros should be able to work properly recursively. Though the exact value of EnterStackCount is less important than just tracking whether or not we're in a recursive call. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Obvious question: do we really need to support nesting for these macros? It would certainly simplify things if we could say that nesting is not allowed.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In theory one could write code such that they never have to be nested. For example there are cases where we could refactor a function So technically it's possible. But it does put a higher strain on the user in exactly how they structure their code, and it's ugly (imagine if sig_on and sig_off could not be nested). You would also have to actively prevent the user from nesting them somehow if it did not work (otherwise this would lead to hard to trace bugs). I think it's better to allow nesting. Any complication that arises from that seems better and less probable than not allowing it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now, I propose a simple and concrete solution:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which is essentially the status quo (we don't have the Sage part yet but that's easy, and I agree with it). So I'm fine with that for lack of a better solution. Perhaps at most this should be documented, and left to individual users to worry about. E.g. for the GAP-Julia interface something might want to be done, but it would be just as Julia-specific as Cysignals is Python-specific. Other applications may not care as much about handling signals at all. |
||
|
|
||
|
|
||
| // 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; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,16 +6,20 @@ | |
| void test_eval(const char * cmd) | ||
| { | ||
| Obj res, ires; | ||
| Int rc, i; | ||
| Int rc, i, ok; | ||
| printf("gap> %s\n", cmd); | ||
| res = GAP_EvalString(cmd); | ||
| rc = GAP_LenList(res); | ||
| for (i = 1; i <= rc; i++) { | ||
| ires = GAP_ElmList(res, i); | ||
| if (GAP_ElmList(ires, 1) == GAP_True) { | ||
| Char * buffer = GAP_CSTR_STRING(GAP_ElmList(ires, 5)); | ||
| if (buffer) | ||
| printf("%s\n", buffer); | ||
| ok = GAP_Enter(); | ||
| if (ok) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm... I wonder if the generic fallback code for To test this, I'd disable the GNU C version for it, and then change the loop below to perform some allocations and trigger garbage collections explicitly (we may need to expose a Anyway, the "fix" in calling code would be to move all local variables storing GAP object references to inside the block between
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you might be right. Do you even know off the top of your head (I'm not sure) if there is any guarantee whatsoever that there is any relation between the order some local variables are declared in C code, and what order they are placed on the stack (of course it could use registers for some of them too in which case it's irrelevant)? AFAIK the C standard doesn't even know anything about a "stack" as that's a platform-specific implementation detail. But realistically speaking this discussion is only relevant where there is one. So I think there's only so much we can do about this--it's just a best guess but not fool-proof.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we already talked about exposing It's not exactly clear to me what you want me to do here though. Just manually test this case, or actually update the test to test this case in general somehow (e.g. have a copy of these tests that are compiled in such a way that forces the fallback)?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The C standard definately puts no requirements on the order of variables on the stack, and will mix them up however it likes. If you are capturing a reference to a local variable in your fallback, the old remotely safe option would be to call GAP_enter, and then call a non-inlined function to actually do the work. I'd probably just not put in a fallback which captures a reference to a local variable.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be clear, I'm 100% positive that fallback is going to lead to hard-to-track and horrible crashes, if there are any GAP variables in the current function scope, or in any inlined functions.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, and of course we don't expect you, @embray , to provide fallbacks fo "every imaginable architecture". My stance on that is that it's better to wait for somebody who actually uses those architectures anyway.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I have noted this as well, at least with gcc. That's why I thought it was an "acceptable" fallback in the first place.
It does, again at least in the case of gcc (and actually I can't think of any other reasonable thing for any compiler to do in that case: how are you supposed to pass a pointer to a value in a register?) And of course, for locals that the compiler puts in registers this is a moot point (GASMAN handles that case separately with its setjmp hack).
+1 I don't like it, but it does seem like the safest option for now, alas.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW Boehm GC does use a similar fallback (they also declare the dummy variable volatile, which I intended to do as well but forgot): https://github.com/ivmai/bdwgc/blob/c50de12ab045b57953e152545836c23f76e7bc24/mark_rts.c#L502
I think that may be the case anyways. Is there really an advantage to continuing to maintain a separate GC when something more heavily used and robust like Boehm can be used? Or at least, maybe, a customized fork thereof if there's really some fine-tuning necessary?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gasman is much faster than Boehm, and various people's attempt to tune it haven't closed the gap.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be worth at least borrowing bits from, such as those linked above, where applicable (and assuming license compatibility). It would be great, longer-term, to get rid of this EnterStack/LeaveStack stuff entirely. |
||
| res = GAP_EvalString(cmd); | ||
| rc = GAP_LenList(res); | ||
| for (i = 1; i <= rc; i++) { | ||
| ires = GAP_ElmList(res, i); | ||
| if (GAP_ElmList(ires, 1) == GAP_True) { | ||
| Char * buffer = GAP_CSTR_STRING(GAP_ElmList(ires, 5)); | ||
| if (buffer) | ||
| printf("%s\n", buffer); | ||
| } | ||
| } | ||
| } | ||
| GAP_Leave(); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.