This refers to something that is more theory that practice. It says that if you create a term reference, use e.g., PL_put_functor() to put a term in it and than somehow backtrack to before the PL_put_functor() without hitting the normal invalidation of term_t, the term_t becomes invalid. The simples (but not so realistic example) is
term_t t = PL_new_term_ref();
fid_t f = PL_open_foreign_frame();
PL_put_functor(t, ...);
PL_discard_foreign_frame(f);
Now t
points at an invalid location to the global stack as the created term is gone. It is realistically possible to get into this scenario, but should be uncommon. If you do and get a GC, the system crashes.
I think we should reconsider (and simplify) all this a bit. First the default PL_PASS_EXCEPTION
is a little dubious. There are two main scenario, the query is created while we are in a foreign predicate, which means we are called from Prolog and the second, the control is in C++. Please do not forget that that is an important use case and, although I think it is often not the preferred one, probably the most common. When in the first scenario (defining a predicate), callbacks to Prolog are actually fairly rare. If it happens, we are often dealing with a call back (C++ refined virtual method) from some C++ library that we want to replay to Prolog. This can be a nasty case.
One case is simple: if we are in a predicate and make a callback to Prolog we have to decide what we want with exceptions, which is 99% of the cases to pass it on. Use PL_Q_PASS_EXCEPTION
, check a failing PL_next_solution() is due to an exception and return from the predicate (with exception). If you want, you can access the exception as PL_exception(0) after the PL_cut_query(), but you can only inspect it, not change the flow.
We could get the desire to catch the exception. That is typically not wise as it also leads to ignoring abort and timeout. If we do, we must act on it in C++. We could throw some other exception (after PL_cut_query()), but we must consume the original one before the PL_cut_query().
The other is also quite easy: if main control is in C++, PL_Q_PASS_EXCEPTION
makes no sense as there is nothing to pass to. Use PL_Q_CATCH_EXCEPTION
if you want to do something with it or use nothing and let Prolog handle the exception and return FALSE
.
If, from C++, it is desirable to use PL_Q_PASS_EXCEPTION
in this scenario, this is fine as the system will nevertheless consider the exception as not caught because there is no environment outside this one that can catch it.
The scenario with something in the middle is complicated. It depends who must handle the exception and whether or not it can be propagated. For example RocksDB has a merge operator that may be called from an arbitrary RocksDB thread that can or can not be associated with a Prolog thread. You basically cannot pass on the Prolog exception.