diff: documentation for trailing interface
Fergus Henderson
fjh at hydra.cs.mu.oz.au
Fri Sep 19 16:55:57 AEST 1997
doc/reference_manual.texi:
Document the trailing interface.
Document the `mdi', `muo', and `mui' modes.
NEWS:
Point to the documentation for trailing.
Could someone please review this change?
cvs diff NEWS doc/reference_manual.texi
Index: NEWS
===================================================================
RCS file: /home/staff/zs/imp/mercury/NEWS,v
retrieving revision 1.72
diff -u -r1.72 NEWS
--- NEWS 1997/09/11 07:08:11 1.72
+++ NEWS 1997/09/19 06:45:17
@@ -36,10 +36,11 @@
There is a new set of grades `*.tr' (e.g. `asm_fast.gc.tr') which
provide support for trailing. This could be used by predicates or
functions defined using the C interface to perform such things as
- constraint solving, backtrackable destructive update, and automatic
- unwinding of database transactions on backtracking. See the declarations
- and comments at the end of runtime/mercury_trail.h for documentation
- of the interface.
+ constraint solving, backtrackable destructive update, or even automatic
+ unwinding of database transactions on backtracking. See the
+ documentation in the "Trailing" section of the Mercury language
+ reference manual (it's at the end of the "C interface" section,
+ which is in the chapter on "Pragmas").
* We've added support for backtrackable destructive update on arrays.
Index: doc/reference_manual.texi
===================================================================
RCS file: /home/staff/zs/imp/mercury/doc/reference_manual.texi,v
retrieving revision 1.68
diff -u -r1.68 reference_manual.texi
--- reference_manual.texi 1997/09/19 01:02:26 1.68
+++ reference_manual.texi 1997/09/19 06:53:27
@@ -1381,6 +1381,20 @@
forward execution are counted - it is OK for @samp{mostly_unique} or
@samp{mostly_dead} values to be needed again on backtracking.
+Mercury defines some standard modes for manipulating ``mostly unique''
+values, just as it does for unique values:
+
+ at example
+% mostly unique output
+:- mode muo :: free -> unique.
+
+% mostly unique input
+:- mode mui :: unique -> unique.
+
+% mostly destructive input
+:- mode mdi :: unique -> dead.
+ at end example
+
@node Limitations of the current implementation
@section Limitations of the current implementation
@@ -1389,6 +1403,8 @@
modes in which anything but the top level of a variable is unique.
If you do, you will get unique mode errors when you try
to get a unique field of a unique data structure.
+It is also not possible to use unique-input modes;
+only destructive-input and unique-output modes work.
The Mercury compiler does not (yet) reuse @samp{dead}
values. The only destructive update in the current implementation occurs
@@ -1396,14 +1412,6 @@
implement structure reuse and compile-time garbage collection
in the very near future.
-Reusing @samp{mostly_dead} values would require keeping a trail, which
-might negatively impact on the performance of code which didn't use
-backtrackable destructive update. Furthermore, it wouldn't have such a
-big impact as reuse of @samp{dead} values; it really pays off only for
-large structures or arrays. As a result, implementing reuse of
- at samp{mostly_dead} values is a much lower priority on our
-implementation schedule.
-
@node Determinism
@chapter Determinism
@@ -2681,6 +2689,7 @@
structures in Mercury code.
* Memory management:: Caveats about passing dynamically
allocated memory to or from C.
+* Trailing:: Undoing side-effects on backtracking.
@end menu
The Mercury distribution includes a number of examples of the
@@ -3020,6 +3029,271 @@
for use when interfacing with other languages that can work for all
methods of memory management, but more implementation experience is
needed before we can formulate such an interface.
+
+ at node Trailing
+ at subsection Trailing
+
+In certain compilation grades (see the ``Compilation model options''
+section of the Mercury User's Guide), the University of Melbourne
+Mercury implementation supports trailing. Trailing is a means
+of having side-effects such as destructive updates to data structures
+undone on backtracking. The basic idea is that during forward
+execution, whenever you perform a destructive modification to
+a data structure that may still be live on backtracking
+you should record whatever information is necessary on
+a stack-like data structure called the ``trail''. Then, if
+a computation fails, and execution backtracks to before those
+those updates were performed, the Mercury runtime engine will
+traverse the trail back to the most recent choice point,
+undoing all those updates.
+
+The interface used is a set of C functions (which are actually
+implemented as macros) and types. Typically these will be
+called from C code within @samp{pragma c_code} declarations
+in Mercury code.
+
+For examples of the use of this interface, see the modules
+ at file{extras/trailed_update/tr_array.m} and
+ at file{extras/clpr/cfloat.m} in the Mercury distribution.
+
+ at menu
+* Choice points::
+* Value trailing::
+* Function trailing::
+* Delayed goals and floundering::
+* Avoiding redundant trailing::
+ at end menu
+
+ at node Choice points
+ at subsubsection Choice points
+
+A "choice point" is a point in the computation to
+which execution might backtrack. The "current"
+choice point is the one that was most recently
+encountered; that is also the one to which execution
+will branch if the current computation fails.
+
+When you trail an update, the Mercury engine will ensure that if
+execution ever backtracks to the choice point that was current
+at the time of trailing, then the update will be undone.
+
+Note that if the Mercury compiler determines that it will never
+need to backtrack to a particular choice point, then it will
+"prune" away that choice point. If a choice point is pruned,
+the trail entries for those updates will be discarded;
+any updates occurring after that choice point will not be undone.
+Execution may subsequently backtrack to a @emph{prior} choice point,
+but if so, the values updated will not be live at that point, so
+there is no need to undo the updates.
+
+For example:
+
+ at example
+:- mode q(out) is nondet.
+:- mode r(in) is det.
+
+p :-
+ q(X), r(X), fail.
+ at end example
+
+Here any choice points created in @code{q/1} will be pruned away
+before the call to @code{r/1}. At the call to @code{fail},
+execution will backtrack; any updates performed inside @code{q/1}
+will not be undone, but that should be OK, since the variable
+ at samp{X} will no longer be live.
+
+ at node Value trailing
+ at subsubsection Value trailing
+
+The simplest form of trailing is value trailing.
+This allows you to trail updates to memory and have
+the Mercury runtime engine automatically undo them
+on backtracking.
+
+ at table @b
+ at item @bullet{} @code{MR_trail_value()}
+Prototype:
+ at example
+void MR_trail_value(Word *@var{address}, Word @var{value});
+ at end example
+
+Ensures that if future execution backtracks to the
+current choice point, then @var{value} will be placed in @var{address}.
+
+ at item @bullet{} @code{MR_trail_current_value()}
+Prototype:
+ at example
+void MR_trail_current_value(Word *@var{address});
+ at end example
+
+Ensures that if future execution backtracks to the
+current choice point, the value currently in @var{address}
+will be restored.
+
+ at samp{MR_trail_current_value(@var{address})} is equivalent to
+ at samp{MR_trail_value(@var{address}, *@var{address})}.
+
+ at end table
+
+ at node Function trailing
+ at subsubsection Function trailing
+
+For more complicated uses of trailing, you can store the address
+of a C function on the trail and have the Mercury runtime call your
+function back whenever future execution backtracks to the current choice point,
+or whenever that choice point is pruned, either because execution
+commits to never backtracking over that point, or because an
+exception was thrown, or because the garbage collector determined
+that the value being trailed is no longer accessible.
+
+Note that currently Mercury does not support exception handling,
+and (as with most logic programming implementations) the garbage
+collector in the current Mercury implementation does not garbage-collect
+the trail; these two cases are mentioned only so that we can cater
+for possible future extensions.
+
+ at table @b
+ at item @bullet{} @code{MR_trail_function()}
+Prototype:
+ at example
+typedef enum @{
+ MR_undo,
+ MR_exception,
+ MR_commit,
+ MR_gc
+@} MR_untrail_reason;
+
+void MR_trail_function(
+ void (*@var{untrail_func})(Word, MR_untrail_reason),
+ Word @var{value}
+);
+ at end example
+ at noindent
+A call to @samp{MR_trail_function(@var{untrail_func}, @var{value})}
+ensures that
+if future execution ever backtracks to current choicepoint,
+then @code{(*@var{untrail_func})(@var{value}, MR_undo)} will called.
+It also ensures that if the current choice point is pruned because
+execution commits to never backtracking to it,
+then @code{(*@var{untrail_func})(@var{value}, MR_commit)} will be called.
+
+MR_exception and MR_gc are currently not used ---
+they are reserved for future use.
+
+ at end table
+
+Typically if the @var{untrail_func} is called with @var{reason} being
+ at samp{MR_undo} or @samp{MR_exception}, then it should undo the effects
+of the update(s) specified by @var{value}, and the free any resources
+associated with that trail entry. If it is called with @var{reason}
+being @samp{MR_commit} or @samp{MR_gc}, then it should not undo the
+effects, instead it should merely free any resources associated with
+the trail entry.
+
+ at node Delayed goals and floundering
+ at subsubsection Delayed goals and floundering
+
+Another use for the function trail is check for floundering
+in the presence of delayed goals.
+
+Often, when implementating certain kinds of constraint solvers, it may
+not be possible to actually solve all of the constraints at the time
+they are added. Instead, it may be necessary to simply delay their
+execution until a later time, in the hope the constraints may become
+solvable when more information is available. If you do implement a
+constraint solver with these properties, then at certain points in
+the computation --- for example, after executing a negated goal --- it
+is important for the system to check that their are no outstanding
+delayed goals which might cause failure, before execution commits
+to this execution path. If this check was omitted, then it could
+lead to unsound behavior, such as a negation failing even though
+logically speaking it ought to have succeeded.
+
+The check for floundering can be implemented using the function trail,
+by simply calling @samp{MR_trail_function()} to add a function trail
+entry whenever you create a delayed goal, and putting the appropriate
+check for floundering in the @samp{MR_commit} case of your function.
+(For an example of this, see the @samp{ML_cfloat_untrail_func()}
+function in the file @samp{extras/clpr/cfloat.m} in the Mercury
+distribution.)
+
+ at node Avoiding redundant trailing
+ at subsubsection Avoiding redundant trailing
+
+If a mutable data structure is updated multiple times, and each update
+is recorded on the trail using the functions described above, then
+some of this trailing may be redundant. It is generally not necessary
+to record enough information to recover the original state of the
+data structure for @emph{every} update on the trail; instead, it is
+enough to record the original state of each updated data structure
+just once for each choice point, rather than once for each update.
+
+The functions described below provide a means to avoid
+redundant trailing.
+
+ at table @b
+ at item @bullet{} @code{MR_ChoicepointId}
+ at itemx @bullet{} @code{MR_current_choicepoint_id()}
+ at itemx @bullet{} @code{MR_null_choicepoint_id()}
+Prototypes:
+ at example
+typedef ... MR_ChoicepointId;
+MR_ChoicepointId MR_current_choicepoint_id(void);
+MR_ChoicepointId MR_null_choicepoint_id(void);
+ at end example
+
+The type @code{MR_ChoicepointId} is an abstract type used
+to hold the identity of a choice point. Values of this
+type can be compared using C's @samp{==} operator.
+
+ at code{MR_current_choicepoint_id()} returns a value indicating
+the identity of the most recent choice point; that is, the
+point to which execution would backtrack if the current computation
+failed.
+
+ at code{MR_null_choicepoint_id()} returns a "null" value that is
+distinct from any value ever returned by @code{MR_current_choicepoint_id}.
+(Note that @code{MR_null_choicepoint_id()}
+is a macro that is guaranteed to be suitable for use as a
+static initializer, so that it can for example be used to
+provide the initial value of a C global variable.)
+
+ at end table
+
+The way these functions are generally used is as follows.
+When you create a mutable data structure, you should call
+ at code{MR_current_choicepoint_id()} and save the value it returns
+as a @samp{prev_choicepoint} field in your data structure.
+(If your mutable data structure
+is a C global variable, then you can use MR_null_choicepoint_id()
+for the initial value of this @samp{prev_choicepoint} field.)
+When you are about to modify your mutable data structure,
+you can then call @code{MR_current_choicepoint_id()} again and
+compare the result from that call with the value saved in
+the @samp{prev_choicepoint} field in the data structure.
+If they are different, then you must trail the update,
+and update the prev_choicepoint field with the new value.
+But if they are equal, then you can avoid trailing the
+update.
+
+Note that there is a cost to this -- you have to include
+an extra field in your data structure for each part of
+the data structure which you might update, and you
+need to perform a test for each update to decide whether
+or not to trail it. Whether or not the benefits from
+avoiding redundant trailing outweigh these costs
+will depend on your application.
+
+ at c @item @code{void MR_untrail_to(MR_TrailEntry *@var{old_trail_ptr}, MR_untrail_reason @var{reason});}
+ at c
+ at c Apply all the trail entries between @samp{MR_trail_ptr} and
+ at c @var{old_trail_ptr}, using the specified @var{reason}.
+ at c
+ at c This function is called by the Mercury engine after backtracking,
+ at c after a commit, or after catching an exception.
+ at c There is probably little need for user code to call this function,
+ at c but it might be needed if you're doing certain low-level things
+ at c such as implementing your own exception handling.
@node Inlining
@section Inlining
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3 | -- the last words of T. S. Garp.
More information about the developers
mailing list