Meta-Call with Limit on Memory Use

The spaceout package, which may be loaded by the query

     | ?- use_module(library(spaceout)).
     

contains the predicate:

space_out(:Goal, +Space, ?Result)

The Goal is executed as if by call/1. If computing any solution causes the global stack usage to increase more than Space bytes, the goal will be aborted and Result unified with the atom space_out. If the goal succeeds without exceeding that space limit, Result is unified with the atom success. Space must be a positive integer.

Note that only the global stack usage is considered, i.e. the value reported by statistics(global_stack, [Used|_]) (see State Info).

space_out/3 is implemented by raising and handling memory resource_error exceptions, so any exception handler in the scope of Goal must be prepared to pass on such exceptions. Note that the format of such exceptions varies depending on the execution mode, Error and Exception Handling.

Before deciding whether to interrupt the computation a garbage collection is performed to try to reduce the size of the global stack enough to allow the computation to continue. As usual, garbage collection can be prevented by setting the Prolog flag gc to off; see State Info.

             %% Call foo/0 with space_out and do not allow GC.
             %% Ensure the gc flag is restored when we no longer need it
             %% By wrapping the goal foo in once/1 we ensure that call_cleanup
             %% will call the clean-up goal immediately when foo succeeds.
             ...
             Size is 10*1024,                % 10kB
             prolog_flag(gc, OldGC),
             call_cleanup((set_prolog_flag(gc,off),
                           space_out(once(foo), Size, Flag)),
                          set_prolog_flag(gc,OldGC)),
             %% prolog_flag(gc, OldGC) will be true here
             ...
     

The precision of space_out can be improved by calling explicitly garbage_collect/0 before the call to space_out/3.

             %% Call foo/0 with space_out
             ...
             Size is 10*1024,                % 10kB
             garbage_collect, % ensure no garbage for foo/0 to use
             space_out(foo, Size, Flag),
             (   Flag == space_out
             ->  write('foo used too much space'), nl
             ;   write('foo used less than 10kB'), nl
             ),
             ...
     

A memory resource error triggered by a real out of memory condition will not be caught by space_out/3.

The Goal will not be interrupted before the global stack has grown by Size bytes. However, the global stack may, under unusual circumstances, grow more than Space bytes before Goal is interrupted. Such growth is unlikely to exceed 10kB.