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

resource_error(stack) isn't caught if PL_exception(0) is called #1138

Open
kamahen opened this issue Mar 4, 2023 · 5 comments
Open

resource_error(stack) isn't caught if PL_exception(0) is called #1138

kamahen opened this issue Mar 4, 2023 · 5 comments

Comments

@kamahen
Copy link
Member

kamahen commented Mar 4, 2023

If Pl_exception(0) is called when stack overflow occurs, the return value is a term that is a variable (as determined by PL_term_type()). Also, it seems to remove some exception information, so that return "false" results in failure rather than an exception. (That is, if I don't do the PL_exception(0) call, I get a resource error; if I do a PL_exception(0) and then return "false", I just get failure and no exception.)

When I don't call PL_exception(0), this is the error term I get (the foreign code is from test_cpp.cpp, but uses a "work-in-progress" version of SWI-cpp2.h):

catch(plunit_cpp:with_small_stacks(5000000,square_roots(1000000000,Z)), E, true), print_term(E, []).
error(resource_error(stack),
      stack_overflow{ choicepoints:5,
		      depth:14,
		      environments:14,
		      globalused:3499,
		      localused:585,
		      stack:[ frame(14,user:square_roots(1000000000,[74655|_]),[]),
			      frame(13,system:setup_call_cleanup((:)/2,(:)/2,(:)/2),[]),
			      frame(12,plunit_cpp:with_small_stacks(5000000,'<garbage_collected>'),[]),
			      frame(11,system:catch((:)/2,_,(:)/2),[]),
			      frame(10,system:'<meta-call>'('<garbage_collected>'),[])
			    ],
		      stack_limit:4885,
		      trailused:0
		    })

If you need a reproducible example, I can make one. But I was hoping that the reason can be determined by quickly looking at the code. Is it perhaps that PL_new_term_ref() doesn't work when there's a stack overflow, and the ensureLocalSpace() call confuses things? (I notice that there's no check for the PL_new_term_ref(), which presumably has returned 0; but before that it should have (recursively?) called raiseStackOverflow()?

I'm OK with getting a variable if the exception term can't be determined or allocated; the problem is that PL_exception(0) ends up removing some information from the exception, so a return false produces a failure in Prolog and not an exception.

@kamahen
Copy link
Member Author

kamahen commented Mar 5, 2023

Hmmm ... the problem doesn't seem to be the PL_new_term_ref() because I'm calling PL_exception() with qid==0, so PL_exception(0) should just return exception_term (which is LD->exception.term). It puzzles me why calling PL_exception(0) would cause the exception to become a variable.

However, in adding some debug print statements, I found that if I subsequently try to get the error term using get_nchars(CVT_ALL|CVT_WRITEQ|BUF_STACK|static_cast<unsigned int>(enc)), then that changes the exception to a "variable" (type=1). I suppose that when there's no more room on the local stack, any operation can cause something bad or strange to happen. But when I tried changing this to BUF_MALLOC, I had the same problem, so presumably something else is changing the exception. It's not clear if it's my code or something inside Prolog.

So, the question becomes: can I detect that a local stack overflow has happened, so that I don't do something that changes the exception into a variable? (That would be the easiest workaround, I think)

@kamahen
Copy link
Member Author

kamahen commented Mar 5, 2023

There's one obvious work-around: check for exception equal error(resource_error(stack),, _). This seems to work for now, so if there aren't wider implications of this issue, this issue can be closed.

@JanWielemaker
Copy link
Member

First of all, BUF_STACK has nothing to do with the Prolog stacks. It is about a heap based stack for temporary strings that is managed using the PL_STRINGS_MARK()/PL_STRINGS_RELEASE() macros.

PL_exception(0) surely cannot reset the exception to a variable. The get_nchars() can replace the exception. Possibly failing half-way can it to become a variable? Did you update to at least 308c922? This commit and the one before provide some updates to deal with using the last bit of space better to reduce the probability that resource exceptions get overruled by exceptions that occur while processing them.

@kamahen
Copy link
Member Author

kamahen commented Mar 6, 2023

Yes, I have pulled 308c922 ... I get the same result.
The work-around is pretty straightforward. For now, I'm not going to try to track this down because I'm concentrating on something else.

Because there appear to be some subtle issues with the Prolog lifetimes of terms and the C++ lifetimes, I'm temporarily isolating that issue by serializing the exception in PlException (using PL_get_nchars() because it's easy to debug); when I get that all working (including exceptions from PlQuery), I'll revisit the resource_error(stack) error.

@JanWielemaker
Copy link
Member

I would just block the test for now. I'm a bit busy. In due time I can create the C equivalent to test and show what should be done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants