17.8 Lower-level Primitives
Suspended goals are actually represented by a special
opaque data type, called suspension, which can be explicitly
manipulated under program control using the primitives defined in
Although usually a suspended goal waits for some waking condition
in order to be reactivated, the primitives for suspension handling
do not enforce this. To provide maximum flexibility of use,
the functionalities of suspending and waking/scheduling are
separated from the trigger mechanisms that cause the waking.
17.8.1 Suspensions and Suspension Lists
A suspension represents a goal that is part of the resolvent.
Apart from the goal structure proper, it holds information that
is used for controlling its execution.
The components of a suspension are:
Suspensions which should be woken by the same event are grouped
together in a suspension list.
Suspension lists are either stored in an attribute of
an attributed variable or attached to a symbolic trigger.
The goal structure
A term representing the goal itself, eg. X >Y.
- The goal module
The module from which the goal was called.
- The goal priority
The priority with which the goal will be scheduled when
it becomes woken.
- The state
This indicates the current position of the suspension within
the resolvent. It is either suspended (sleeping), scheduled
or executed (dead).
- Additional data
E.g. debugging information.
17.8.2 Creating Suspended Goals
The most basic primitive to create a suspension is
make_suspension(Goal, Priority, Susp [, Module])
where Goal is the goal structure,
Priority is a small integer denoting the priority with which
the goal should be woken and Susp is the resulting suspension.
Note that usually
is not used directly, but implicitly via
(described in section 17.6) which in addition attaches the suspension to a
A suspension which has not yet been scheduled
for execution and executed, is called sleeping,
a suspension which has already been executed is called
executed or dead (since it disappears from the resolvent,
but see section 17.9 for an exception).
A newly created suspension is always sleeping, however
note that due to backtracking, an executed suspension
can become sleeping again.
Sometimes we use the term waking, which is less precise and
denotes the process of both scheduling and eventual execution.
By default, suspensions are printed as follows (the variants with invocation
numbers are used when the debugger is active):
It is possible to change the way suspensions are printed by defining a
transformation for the term type goal.
|| sleeping suspension with id _78
|| scheduled suspension with id _78
|| dead suspension with id _78
|| sleeping suspension with invocation number 123
|| scheduled suspension with invocation number 123
|| dead suspension with id invocation number 123
17.8.3 Operations on Suspensions
The following summarises the predicates that can be used to create, test,
decompose and destroy suspensions.
make_suspension(Goal, Priority, Susp)
- make_suspension(Goal, Priority, Susp, Module)
Create a suspension Susp with a given priority from a given goal.
The goal will subsequently show up as a delayed goal.
Succeeds if Susp is a sleeping or scheduled suspension,
fails if it is not a suspension or a suspension that has been already executed.
- type_of(S, goal)
Succeeds if S is a suspension, no matter if it is
sleeping, scheduled or executed.
- get_suspension_data(Susp, Name, Value)
Extract any of the information contained in the suspension:
Name can be one of
invoc (debugger invocation number).
- set_suspension_data(Susp, Name, Value)
invoc (debugger invocation number) fields
of a suspension can be changed using this primitive.
If the priority of a sleeping suspension is changed,
this will only have an effect at the time the suspension gets
scheduled. If the suspension is already scheduled, changing
priority has no effect, except for future schedulings of demons
Convert the suspension Susp into an executed
one, ie. remove the suspended goal from the resolvent.
This predicate is meta-logical as its use may
change the semantics of the program.
17.8.4 Examining the Resolvent
The system keeps track of all created suspensions and it
uses this data e.g. in the built-in predicates
and to detect floundering of the query given to the ECLiPSe top-level loop.
17.8.5 Attaching Suspensions to Variables
Suspensions are attached to variables by means of the attribute mechanism.
For this purpose, a variable attribute needs to have one or more slots
reserved for suspension lists.
Suspensions can then be inserted into one or several of those lists using
insert_suspension(Vars, Susp, Index)
Insert the suspension Susp into the Index'th
suspension list of all attributed variables occurring in Vars.
The current module specifies which of the attributes will be taken.
- insert_suspension(Vars, Susp, Index, Module)
Similar to insert_suspension/3,
but it inserts the suspension into the attribute specified by Module.
inserts the suspension into the instlist of the (system-predefined) suspend
attribute of all variables that occur in Vars, and
insert_suspension(Vars, Susp, inst of suspend, suspend)
would insert the suspension into the max list of the finite-domain
attribute of all variables in Vars.
insert_suspension(Vars, Susp, max of fd, fd)
Note that both predicates
find all attributed variables which occur in the general term Vars and for each of them,
locate the attribute which corresponds to the current module or the
Module argument respectively.
This attribute must be a structure, otherwise an error
is raised, which means that the attribute has to be initialised
Finally, the Index'th argument of the attribute
is interpreted as a suspension list and the suspension
Susp is inserted at the beginning of this list.
A more user-friendly interface to access suspension lists is
provided by the
17.8.6 User-defined Suspension Lists
Many important attributes and suspension lists are either provided by
the suspend-attribute or by libraries like the interval solver library lib(ic).
For those suspension lists, initialisation and waking is taken care of
by the library code.
For the implementation of user-defined suspension lists,
the following low-level primitives are provided:
Initialises argument Position of Attribute to an empty suspension list.
- merge_suspension_lists(+Pos1, +Attr1, +Pos2, +Attr2)
Destructively appends the first suspension list (argument Pos1 of Attr1) to
the end of the second (argument Pos2 of Attr2).
- enter_suspension_list(+Pos, +Attr, +Susp)
Adds the suspension Susp to the suspension list in the
argument position Pos of Attr. The suspension list can be pre-existing,
or the argument could be uninstantiated, in which case a new suspension
list will be created.
- schedule_suspensions(+Position, +Attribute)
Takes the suspension list on argument position Position within
Attribute, and schedule them for execution.
As a side effect, the suspension list within Attribute is updated,
ie. suspensions which are no longer useful are removed destructively.
See section 17.8.8 for more details on waking.
17.8.7 Attaching Suspensions to Global Triggers
A single suspension or a list of suspensions can be attached to a
symbolic trigger by using
A symbolic trigger can have an arbitrary name (an atom).
17.8.8 Scheduling Suspensions for Waking
Suspended goals are woken by submitting at least one of the suspension lists
in which they occur to the waking scheduler.
The waking scheduler which maintains a global priority queue inserts
them into this queue according to their priority (see figure 17.1).
A suspension list can be passed to the scheduler by either of the predicates
(for uder-defined suspension lists).
A suspension which has been scheduled in this way and awaits
its execution is called a scheduled suspension.
Note, however, that scheduling a suspension by means of
alone does not implicitly start the waking scheduler.
Instead, execution continues normally with the next goal in sequence after
The scheduler must be explicitly invoked by calling
Only then does it start to execute the woken suspensions.
The reason for having wake/0
is to be able to schedule several suspension lists before the
priority-driven execution begins5.