NOTE — A built-in predicatecall_cleanup/2with similar functionality is implemented in many existing processors. It is often used to free temporary resources. In the presence of interrupts (7.12 note c),call_cleanup/2can cause leakage of resources: A processor receiving interrupts between resource allocation andcall_cleanup/2is unable to free the resource in a timely manner. To overcome this problem,setup_call_cleanup/3protects resource allocation from interrupts.
setup_call_cleanup(S, G, C) is true
iff once(S), call(G) is true.
Procedurally, the control construct shall be executed as follows:
once(S) is executed while being protected from interrupts:
once(S) is executed
entirely and the cleanup handler C is installed
upon success;
once(S) is interrupted by an
implementation
dependent interrupt (7.12 note c). In this case, once(S) does not leave an
observable
effect.
call(G) is called.
G. Earlier moments are:
G is true or false, C is
called at an implementation dependent moment after the last
solution and after the last observable effect of G.
G or the continuation of G
is interrupted by a call of throw/1 (7.8.9 a,
b), whose corresponding call of catch/3 is above G.C, throw/1
continues to search the corresponding call of catch/3,
regardless of the outcome of C.
Thus, if C is interrupted by another call of
throw/1, that throw/1 is lost.
G executes a cut
explicitly or implicitly that is associated with the cutparent of G
(7.7.2, 7.8.4.1 Note 1b).C is called in
the place of the cut.
A cut is performed implicitly for (->)/2 - if-then
(7.8.7), (;)/2 - if-then-else (7.8.8), (\+)/1
(8.15.1), once/1 (8.15.2).
C is called as once(C). Failure
of C is ignored. An explicit or
implicit throw/1 is ignored in case 7.8.11.1 c2;
otherwise (case 7.8.11.1 c1 or
7.8.11.1 c3), it is passed towards the corresponding call of catch/3.
C shares bindings and variables
with S, with G and with the continuation. The
bindings up to and including S are always present. No further bindings
are present when C is executed at 7.8.11.1 c2 and at the
latest moment of 7.8.11.1 c1. At 7.8.11.1 c2, and at the
earliest moment of 7.8.11.1 c1, all bindings are present at the time of calling
the cut. All other cases of 7.8.11.1 c1 are implementation dependent.
A processor may restrict execution of C in an
implementation defined manner. For example, a processor may restrict the
handling of interrupts within C or limit resource
consumption.
NOTES
1
Existing processors locate c1 either when the last solution is found,
or upon failure.
The precise moment is implementation dependent due to the
varying ability of Prolog processors to detect determinism. For
example, a processor may execute call((call(G);fail))
as call(G) and vice versa.
2
For nonterminating goals like repeat (8.15.3) only c2 and
c3 can trigger the cleanup. And if a nonterminating goal does not find a
solution, only c2 can trigger.
3
A throw/1 of G has priority over one
of C (7.8.11.1 c2). This is an advantage when recovering
errors, like resource errors (7.12.2 h), where the error
of C is often a consequence of the error
of G.
setup_call_cleanup(goal, goal, goal)
NOTE — The arguments should eventually be
+callable_term. However, neither type nor mode is
checked when this control construct is executed (8.1.2.2).
S is a callable term that is not permitted due
to an implementation defined restrictionrepresentation_error(setup_goal).
S finds a solution, but C is
a variableinstantiation_error. Goal G is not executed.
S finds a solution, but C is
neither a variable nor a callable termtype_error(callable, C). Goal G is not executed.
NOTE — Further errors inSandGmay happen due to callingonce/1orcall/1(see 7.8.11.1).
write/1 uses the standard operator table
(6.3.4.4, table 7). Unique variables are written
as _ which is one possible way to
realize 7.10.5 a. The different outcomes (labeled either/or) are due to the
implementation dependence in c1.
setup_call_cleanup(fail, _, _).
Fails. Neither the goal nor the cleanup is executed.
setup_call_cleanup(throw(ex), _, _).
System error due to uncaught ex.
setup_call_cleanup(true, throw(unthrown),_).
Instantiation error.
setup_call_cleanup(true, true, ( true ; throw(x) )).
Succeeds. No system error.
setup_call_cleanup(true, X = 1, X = 2).
Succeeds, unifying X = 1.
setup_call_cleanup(true, true, X = 2).
Either: Succeeds, unifying X = 2.
Or: Succeeds.
setup_call_cleanup(true, X=true,X).
Instantiation error.
setup_call_cleanup(X=throw(ex), true, X).
Either: System error due to uncaught ex.
Or: Succeeds. System error on backtracking.
setup_call_cleanup(true, true, fail).
Succeeds.
setup_call_cleanup(S=1, G=2, C=3).
Either: Succeeds, unifying S = 1, G = 2, C = 3.
Or: Succeeds, unifying S = 1, G = 2.
setup_call_cleanup((S=1;S=2), G=3, C=4).
Either: Succeeds, unifying
S = 1, G = 3, C = 4.
Or: Succeeds, unifying S = 1, G = 3.
setup_call_cleanup(S=1,G=2,write(S+G)).
Succeeds, unifying S = 1, G = 2.
Either: outputs '1+2'
Or: outputs on backtracking '1+_' prior to failure.
Or (?): outputs on backtracking '1+2' prior to failure.
setup_call_cleanup(S=1,(G=2;G=3),write(S+G)).
Succeeds, unifying S = 1, G = 2.
On backtracking, succeeds unifying S = 1 and G = 3.
Either: outputs '1+3'
Or: on backtracking outputs '1+_' prior to failing
Or (?): on backtracking outputs '1+3' prior to failing.
setup_call_cleanup(S=1,G=2,write(S+G>A+B)), A=3, B=4.
Succeeds, unifying S=1,G=2,A=3,B=4.
Either: Outputs one of the following before succeeding
1+2>_+_. 1+2>3+_. 1+2>3+4. Disputable: 1+2>_+4.
Or: outputs one of the above outputs on backtracking
prior to failing.
setup_call_cleanup(S=1,(G=2;G=3,throw(x)),write(S+G)).
Succeeds, unifying S = 1, G = 2.
On backtracking, outputs '1+_' prior
to system error due to uncaught x.
setup_call_cleanup(open(f,read,S),read(S,X),close(S)).
Opens file f for reading, reads a term and closes the file.
Succeeds, unifying S with an implementation dependent stream
term, unifying X with the term read. The file is closed, either
immediately, or on backtracking, and even if there is an error in
read.
setup_call_cleanup(S=1,(G=2;G=3),write(S+G>B)), B=4, !.
Outputs '1+2>4'. Succeeds, unifying S = 1, G = 2, B = 4.
setup_call_cleanup(S=1,G=2,write(S+G>B)),B=3,!.
Either: Outputs '1+2>_'. Succeeds, unifying
S = 1, G = 2, B = 3.
Or: Outputs '1+2>3'. Succeeds, unifying
S = 1, G = 2, B = 3.
setup_call_cleanup(S=1,(G=2;fail),write(S+G>B)), B=3, !.
Same as above.
setup_call_cleanup(S=1,(G=2;S=2),write(S+G>B)), B=3, !.
Same as above.
setup_call_cleanup(S=1,(G=2;G=3), write(S+G>B)), B=4, throw(x).
Outputs '1+_>_'. system_error due to uncaught x.
setup_call_cleanup(S=1,(G=2;G=3), write(S+G>B)), B=4, !, throw(x).
Outputs '1+2>4'. system_error due to uncaught x.
setup_call_cleanup(true, (X=1;X=2), write(a)),
setup_call_cleanup(true,(Y=1;Y=2),write(b)), throw(x).
Outputs 'ba'. system_error due to uncaught x.
setup_call_cleanup(true, (X=1;X=2), write(a)),
setup_call_cleanup(true,(Y=1;Y=2),write(b)), !.
Outputs 'ba'. Succeeds, unifying X = 1, Y = 1.
catch(setup_call_cleanup(true,throw(goal),throw(cl)), Pat, true).
Succeeds unifying Pat = goal.
catch(( setup_call_cleanup(true,(G=1;G=2),throw(cl)), throw(cont)), Pat, true).
Succeeds unifying Pat = cont.
catch(
setup_call_cleanup(true, throw(a),
setup_call_cleanup(true,fail,throw(b))
),
Pat, true).
Succeeds unifying Pat with a.
setup_call_cleanup(S, G, C) :- once(S), call_cleanup(G, C).