NOTE — A built-in predicatecall_cleanup/2
with 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/2
can cause leakage of resources: A processor receiving interrupts between resource allocation andcall_cleanup/2
is unable to free the resource in a timely manner. To overcome this problem,setup_call_cleanup/3
protects 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 inS
andG
may happen due to callingonce/1
orcall/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).