Can I open a GC window in SWI-Prolog? (Something like an Isolate)

(1) Assume SWI-Prolog calls C code. (2) And then C code calls SWI-Prolog. All inside the same process, even inside the same thread.

Can I call SWI-Prolog in such a way that it opens a new garbage collection (GC) window? So that only what gets produce by the call to SWI-Prolog is marked and sweeped, and the rest stays untouched.

Reason for this question: Assume in transition from (1) to (2) something dirty happens. i.e. Prolog terms are created but garbage collection anchors are not registered.

An isolate would help that these Prolog terms are not eliminated.

Yes, that is what c('PL_open_foreign_frame') and it’s partners like c('PL_close_foreign_frame') are for.

I do know that nesting frames are fine but we are responsible for closing them in them in the correct nested order (reverse from the order we create them) per PL_engine

% +=open     -=close
+fid1
  +fid2
  -fid2
  +fid3
    +fid4
    -fid4
  -fid3
-fid1

Will immediately collect any term_refs created between the open/close. non term_ref data (such as your foreign code added attributes to a prolog variable present before your foreign frame (thus newly Heaped att/3 compound terms)) will not be marked to be collectable without the PL_frame_unwind.

Maybe a question is if we did

+fid1
  +fid2
   +fid3
    +fid4
-fid1

term_refs get collected… But the question is if no longer used Heaped data created in fid2,fid3,fid4 will get marked to be collectable? (I suspect it secretly does)

Sometimes I’ve written foreign functions that dont need to use the foreign frames open/close (as they dont create term_refs)

No. Actually closing is not really required as long as some outer foreign frame is closed in time. The foreign frames form a stack. PL_close_foreign_frame(fid) simply sets the top of this stack, so anything later is discarded as well.

And to @j4n_bur53: foreign code including nested C/Prolog/C execution is fully transparent to GC. Also data of the outer Prolog environments is garbage collected. GC can happen at any time as the result of one of the PL_* functions running out of space. The foreign frames tell the systems which term_t handles are accessible and GC will update their content when needed.

The term_t is not subject to GC, It makes the referenced term visible to GC. The term_t itself is only made invalid using PL_close_foreign_frame() or PL_reset_term_refs(). Note that a foreign predicate call has a PL_open_foreign_frame() / PL_close_foreign_frame() pair around it (well, a simplified version).

You can stop referencing some term using PL_put_nil(t) or any other of the PL_put_* family that stores a value that is not subject to GC such as a small integer.

All this was (AFAIK) invented at Quintus. I mainly added the PL_unify_() and PL_get__ex() families that simplifies using term_t for simple tasks a lot easier. Quintus passed native types for that. We have had that discussion. Quintus also lacked the PL_open_foreign_frame() and friends. These are practically necessary for C programs that embed Prolog, keeping the main control in C and making regular calls to Prolog. Using PL_open_foreign_frame() we can do a callback without building up term references. Without one would have to setup some infrastructure to reuse these handles to avoid running out of them.