Author(s): Manuel Carro.
Version: 1.3#120 (1999/11/26, 12:5:17 MET)
Version of last change: 1.3#118 (1999/11/25, 19:28:6 MET)
This module provides basic mechanisms for using concurrency and implementing multi-goal applications. Goals can be specified to be run in a separate stack set; in that case, they are assigned a goal identifier with which further accesses (e.g., asking for more solutions) to the goal can be made. Additionally, in some architectures, these goals can be assigned an O.S. thread, separate from the one which made the initial call. On the architectures for which we were not able to implement it yet, the original thread switches to the new stack set, executes the goal there, and returns after it has completed.
As for now, the memory space of the threads (c.f., stack sets) is separate in the sense that goals are copied to the new stack set, and bindings of variables are not seen among stack sets which allows forward and backward execution to proceed independently in each stack set, at the cost of the initial goal copy. However, the program space (including, specially, the concurrent predicates) are shared and seen by all the goals and threads.
Additionally, a small set of lock-related predicates are provided: locks are associated with atom names. Whereas the concurrent database facilities are enough to implement locks, semaphores, messages, etc., the predicates implementing atom-based locks are faster than the ones accessing the concurrent database.
concurrency
)concurrency
)
Meta-predicate with arguments: eng_call(goal,?,?,?)
.
Usage: eng_call(+Goal,+EngineCreation,+ThreadCreation,-(GoalId))
Goal
in a new engine (stack set), possibly using a new thread, and returns a GoalId
to designate this new goal henceforth. EngineCreation
can be either wait
or create
; the distinction is not yet meaningful. ThreadCreation
can be one of self
, wait
, or create
. In the first case the creating thread is used to execute Goal
, and thus it has to wait until its first result or failure. In the other two cases, a new thread for the Goal
is created and assigned the new engine. Some details still have to be woked out.
eng_call/4
always succeeds and executes a copy (with new variables) of the original goal, so local and remote backtracking and execution do not affect each other.
+Goal
is a term which represents a goal, i.e., an atom or a structure.
(basic_props:callable/1
)
+EngineCreation
is an atom.
(basic_props:atm/1
)
+ThreadCreation
is an atom.
(basic_props:atm/1
)
-(GoalId)
is an integer.
(basic_props:int/1
)
Meta-predicate with arguments: eng_call(goal,?,?)
.
Usage: eng_call(+Goal,+EngineCreation,+ThreadCreation)
eng_call/4
, but the thread (if created) and stack areas are automatically released upon success or failure of the goal. No GoalId
is provided for further interaction with the goal.
+Goal
is a term which represents a goal, i.e., an atom or a structure.
(basic_props:callable/1
)
+EngineCreation
is an atom.
(basic_props:atm/1
)
+ThreadCreation
is an atom.
(basic_props:atm/1
)
The predicate is of type implicit.
Usage: eng_backtrack(+GoalId,+ThreadCreation)
GoalId
, maybe using a new thread, according to ThreadCreation
(same as in
eng_call/4
). Fails if the goal is backtracked over by the local thread, and there are no more solutions. Always succeeds if executed by a remote thread. The engine is automatically released and cleaned up upon failure.
+GoalId
is an integer.
(basic_props:int/1
)
+ThreadCreation
is an atom.
(basic_props:atm/1
)
The predicate is of type implicit.
Usage: eng_cut(+GoalId)
The predicate is of type implicit.
Usage: eng_release(+GoalId)
GoalId
. The engine must be in WAITING
state.
+GoalId
is an integer.
(basic_props:int/1
)
The predicate is of type implicit.
Usage: eng_wait(+GoalId)
GoalId
to be in waiting state (i.e., it has finished searching for a solution, either with success or failure).
+GoalId
is an integer.
(basic_props:int/1
)
The predicate is of type implicit.
Usage: eng_kill(+GoalId)
GoalId
(if any), and frees the memory used up by the stack set. Usually one should wait (
eng_wait/1
) for a goal, and then release it, but killing the thread explicitly allows recovering from error states, or imposing timeouts to a task. A goal can kill itself; use this feature with caution.
+GoalId
is an integer.
(basic_props:int/1
)
The predicate is of type implicit.
Usage:
The predicate is of type implicit.
Usage: eng_self(?(GoalId))
The predicate is of type implicit.
Usage:
The predicate is of type implicit.
Usage: lock_atom(+Atom)
Atom
is accessed; if its value is nonzero, it is atomically decremented and the execution of this thread proceeds. Otherwise, the goal waits until a nonzero value is reached. The semaphore is then atomically decremented and the execution of this thread proceeds.
+Atom
is an atom.
(basic_props:atm/1
)
The predicate is of type implicit.
Usage: unlock_atom(+Atom)
The predicate is of type implicit.
Usage 1: atom_lock_state(+Atom,+Value)
Atom
to Value
. This is usually done at the beginning of the execution, but can be executed at any time. If not called, semaphore associated to atoms are by default inited to 1. It should be used with caution: arbitrary use can transform programs using locks in a mess of internal relations. The change of a semaphore value in a place other than the initialization stage of a program is not among the allowed operations as defined by Dijkstra [Dij65,BA82].
+Atom
is an atom.
(basic_props:atm/1
)
+Value
is an integer.
(basic_props:int/1
)
Usage 2: atom_lock_state(+Atom,-(Value))
Value
of the semaphore associated to Atom
. Use sparingly and mainly as a medium to check state correctness. Not among the operations on semaphore by Djikstra.
+Atom
is an atom.
(basic_props:atm/1
)
-(Value)
is an integer.
(basic_props:int/1
)
concurrency
)Go to the first, previous, next, last section, table of contents.