5707 lines
141 KiB
C
5707 lines
141 KiB
C
/*************************************************************************
|
|
* *
|
|
* YAP Prolog *
|
|
* *
|
|
* Yap Prolog was developed at NCCUP - Universidade do Porto *
|
|
* *
|
|
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
|
|
* *
|
|
**************************************************************************
|
|
* *
|
|
* File: dbase.c *
|
|
* Last rev: 8/2/88 *
|
|
* mods: *
|
|
* comments: YAP's internal data base *
|
|
* *
|
|
*************************************************************************/
|
|
#ifdef SCCS
|
|
static char SccsId[] = "%W% %G%";
|
|
#endif
|
|
|
|
|
|
/** @defgroup Internal_Database Internal Data Base
|
|
@ingroup builtins
|
|
@{
|
|
|
|
Some programs need global information for, e.g. counting or collecting
|
|
data obtained by backtracking. As a rule, to keep this information, the
|
|
internal data base should be used instead of asserting and retracting
|
|
clauses (as most novice programmers do), .
|
|
In YAP (as in some other Prolog systems) the internal data base (i.d.b.
|
|
for short) is faster, needs less space and provides a better insulation of
|
|
program and data than using asserted/retracted clauses.
|
|
The i.d.b. is implemented as a set of terms, accessed by keys that
|
|
unlikely what happens in (non-Prolog) data bases are not part of the
|
|
term. Under each key a list of terms is kept. References are provided so that
|
|
terms can be identified: each term in the i.d.b. has a unique reference
|
|
(references are also available for clauses of dynamic predicates).
|
|
|
|
There is a strong analogy between the i.d.b. and the way dynamic
|
|
predicates are stored. In fact, the main i.d.b. predicates might be
|
|
implemented using dynamic predicates:
|
|
|
|
~~~~~
|
|
recorda(X,T,R) :- asserta(idb(X,T),R).
|
|
recordz(X,T,R) :- assertz(idb(X,T),R).
|
|
recorded(X,T,R) :- clause(idb(X,T),R).
|
|
~~~~~
|
|
We can take advantage of this, the other way around, as it is quite
|
|
easy to write a simple Prolog interpreter, using the i.d.b.:
|
|
|
|
~~~~~
|
|
asserta(G) :- recorda(interpreter,G,_).
|
|
assertz(G) :- recordz(interpreter,G,_).
|
|
retract(G) :- recorded(interpreter,G,R), !, erase(R).
|
|
call(V) :- var(V), !, fail.
|
|
call((H :- B)) :- !, recorded(interpreter,(H :- B),_), call(B).
|
|
call(G) :- recorded(interpreter,G,_).
|
|
~~~~~
|
|
In YAP, much attention has been given to the implementation of the
|
|
i.d.b., especially to the problem of accelerating the access to terms kept in
|
|
a large list under the same key. Besides using the key, YAP uses an internal
|
|
lookup function, transparent to the user, to find only the terms that might
|
|
unify. For instance, in a data base containing the terms
|
|
|
|
~~~~~
|
|
b
|
|
b(a)
|
|
c(d)
|
|
e(g)
|
|
b(X)
|
|
e(h)
|
|
~~~~~
|
|
|
|
stored under the key k/1, when executing the query
|
|
|
|
~~~~~
|
|
:- recorded(k(_),c(_),R).
|
|
~~~~~
|
|
|
|
`recorded` would proceed directly to the third term, spending almost the
|
|
time as if `a(X)` or `b(X)` was being searched.
|
|
The lookup function uses the functor of the term, and its first three
|
|
arguments (when they exist). So, `recorded(k(_),e(h),_)` would go
|
|
directly to the last term, while `recorded(k(_),e(_),_)` would find
|
|
first the fourth term, and then, after backtracking, the last one.
|
|
|
|
This mechanism may be useful to implement a sort of hierarchy, where
|
|
the functors of the terms (and eventually the first arguments) work as
|
|
secondary keys.
|
|
|
|
In the YAP's i.d.b. an optimized representation is used for
|
|
terms without free variables. This results in a faster retrieval of terms
|
|
and better space usage. Whenever possible, avoid variables in terms in terms stored in the i.d.b.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
#include "Yap.h"
|
|
#include "clause.h"
|
|
#include "yapio.h"
|
|
#include "attvar.h"
|
|
#include "heapgc.h"
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
|
|
/* There are two options to implement traditional immediate update semantics.
|
|
|
|
- In the first option, we only remove an element of the chain when
|
|
it is physically disposed of. This simplifies things, because
|
|
pointers are always valid, but it complicates some stuff a bit:
|
|
|
|
o You may have go through long lines of deleted db entries before you
|
|
actually reach the one you want.
|
|
|
|
o Deleted clauses are also not removed of the chain. The solution
|
|
was to place a fail in every clause, but you still have to
|
|
backtrack through failed clauses.
|
|
|
|
An alternative solution is to remove clauses from the chain, even
|
|
if they are still phisically present. Unfortunately this creates
|
|
problems because immediate update semantics means you have to
|
|
backtrack clauses or see the db entries stored later.
|
|
|
|
There are several solutions. One of the simplest is to use an age
|
|
counter. When you backtrack to a removed clause or to a deleted db
|
|
entry you use the age to find newly entered clauses in the DB.
|
|
|
|
This still causes a problem when you backtrack to a deleted
|
|
clause, because clauses are supposed to point to the next
|
|
alternative, and having been removed from the chain you cannot
|
|
point there directly. One solution is to have a predicate in C that
|
|
recovers the place where to go to and then gets rid of the clause.
|
|
|
|
*/
|
|
|
|
|
|
#define DISCONNECT_OLD_ENTRIES 1
|
|
|
|
#ifdef MACYAPBUG
|
|
#define Register
|
|
#else
|
|
#define Register register
|
|
#endif
|
|
|
|
/* Flags for recorda or recordz */
|
|
/* MkCode should be the same as CodeDBProperty */
|
|
#define MkFirst 1
|
|
#define MkCode CodeDBBit
|
|
#define MkLast 4
|
|
#define WithRef 8
|
|
#define MkIfNot 16
|
|
#define InQueue 32
|
|
|
|
#define FrstDBRef(V) ( (V) -> First )
|
|
#define NextDBRef(V) ( (V) -> Next )
|
|
|
|
#define DBLength(V) (sizeof(DBStruct) + (Int)(V) + CellSize)
|
|
#define AllocDBSpace(V) ((DBRef)Yap_AllocCodeSpace(V))
|
|
#define FreeDBSpace(V) Yap_FreeCodeSpace(V)
|
|
|
|
#if SIZEOF_INT_P==4
|
|
#define ToSmall(V) ((link_entry)(Unsigned(V)>>2))
|
|
#else
|
|
#define ToSmall(V) ((link_entry)(Unsigned(V)>>3))
|
|
#endif
|
|
|
|
#ifdef SFUNC
|
|
|
|
#define MaxSFs 256
|
|
|
|
typedef struct {
|
|
Term SName; /* The culprit */
|
|
CELL *SFather; /* and his father's position */
|
|
} SFKeep;
|
|
#endif
|
|
|
|
#define HashFieldMask ((CELL)0xffL)
|
|
#define DualHashFieldMask ((CELL)0xffffL)
|
|
#define TripleHashFieldMask ((CELL)0xffffffL)
|
|
#define FourHashFieldMask ((CELL)0xffffffffL)
|
|
|
|
#define ONE_FIELD_SHIFT 8
|
|
#define TWO_FIELDS_SHIFT 16
|
|
#define THREE_FIELDS_SHIFT 24
|
|
|
|
#define AtomHash(t) (Unsigned(t)>>4)
|
|
#define FunctorHash(t) (Unsigned(t)>>4)
|
|
#define NumberHash(t) (Unsigned(IntOfTerm(t)))
|
|
|
|
#define LARGE_IDB_LINK_TABLE 1
|
|
|
|
/* traditionally, YAP used a link table to recover IDB terms*/
|
|
#if LARGE_IDB_LINK_TABLE
|
|
typedef BITS32 link_entry;
|
|
#define SIZEOF_LINK_ENTRY 4
|
|
#else
|
|
typedef BITS16 link_entry;
|
|
#define SIZEOF_LINK_ENTRY 2
|
|
#endif
|
|
|
|
/* These global variables are necessary to build the data base
|
|
structure */
|
|
typedef struct db_globs {
|
|
link_entry *lr, *LinkAr;
|
|
/* we cannot call Error directly from within recorded(). These flags are used
|
|
to delay for a while
|
|
*/
|
|
DBRef *tofref; /* place the refs also up */
|
|
#ifdef SFUNC
|
|
CELL *FathersPlace; /* Where the father was going when the term
|
|
* was reached */
|
|
SFKeep *SFAr, *TopSF; /* Where are we putting our SFunctors */
|
|
#endif
|
|
DBRef found_one; /* Place where we started recording */
|
|
UInt sz; /* total size */
|
|
} dbglobs;
|
|
|
|
|
|
#ifdef SUPPORT_HASH_TABLES
|
|
typedef struct {
|
|
CELL key;
|
|
DBRef entry;
|
|
} hash_db_entry;
|
|
|
|
typedef table {
|
|
Int NOfEntries;
|
|
Int HashArg;
|
|
hash_db_entry *table;
|
|
} hash_db_table;
|
|
#endif
|
|
|
|
static CELL *cpcells(CELL *,CELL*,Int);
|
|
static void linkblk(link_entry *,CELL *,CELL);
|
|
static Int cmpclls(CELL *,CELL *,Int);
|
|
static Prop FindDBProp(AtomEntry *, int, unsigned int, Term);
|
|
static CELL CalcKey(Term);
|
|
#ifdef COROUTINING
|
|
static CELL *MkDBTerm(CELL *, CELL *, CELL *, CELL *, CELL *, CELL *,int *, struct db_globs *);
|
|
#else
|
|
static CELL *MkDBTerm(CELL *, CELL *, CELL *, CELL *, CELL *, int *, struct db_globs *);
|
|
#endif
|
|
static DBRef CreateDBStruct(Term, DBProp, int, int *, UInt, struct db_globs *);
|
|
static DBRef record(int, Term, Term, Term CACHE_TYPE);
|
|
static DBRef check_if_cons(DBRef, Term);
|
|
static DBRef check_if_var(DBRef);
|
|
static DBRef check_if_wvars(DBRef, unsigned int, CELL *);
|
|
static int scheckcells(int, CELL *, CELL *, link_entry *, CELL);
|
|
static DBRef check_if_nvars(DBRef, unsigned int, CELL *, struct db_globs *);
|
|
static Int p_rcda( USES_REGS1 );
|
|
static Int p_rcdap( USES_REGS1 );
|
|
static Int p_rcdz( USES_REGS1 );
|
|
static Int p_rcdzp( USES_REGS1 );
|
|
static Int p_drcdap( USES_REGS1 );
|
|
static Int p_drcdzp( USES_REGS1 );
|
|
static Term GetDBTerm(DBTerm *, int src CACHE_TYPE);
|
|
static DBProp FetchDBPropFromKey(Term, int, int, char *);
|
|
static Int i_recorded(DBProp,Term CACHE_TYPE);
|
|
static Int c_recorded(int CACHE_TYPE);
|
|
static Int co_rded( USES_REGS1 );
|
|
static Int in_rdedp( USES_REGS1 );
|
|
static Int co_rdedp( USES_REGS1 );
|
|
static Int p_first_instance( USES_REGS1 );
|
|
static void ErasePendingRefs(DBTerm * CACHE_TYPE);
|
|
static void RemoveDBEntry(DBRef CACHE_TYPE);
|
|
static void EraseLogUpdCl(LogUpdClause *);
|
|
static void MyEraseClause(DynamicClause * CACHE_TYPE);
|
|
static void PrepareToEraseClause(DynamicClause *, DBRef);
|
|
static void EraseEntry(DBRef);
|
|
static Int p_erase( USES_REGS1 );
|
|
static Int p_eraseall( USES_REGS1 );
|
|
static Int p_erased( USES_REGS1 );
|
|
static Int p_instance( USES_REGS1 );
|
|
static int NotActiveDB(DBRef);
|
|
static DBEntry *NextDBProp(PropEntry *);
|
|
static Int init_current_key( USES_REGS1 );
|
|
static Int cont_current_key( USES_REGS1 );
|
|
static Int cont_current_key_integer( USES_REGS1 );
|
|
static Int p_rcdstatp( USES_REGS1 );
|
|
static Int p_somercdedp( USES_REGS1 );
|
|
static yamop * find_next_clause(DBRef USES_REGS);
|
|
static Int p_jump_to_next_dynamic_clause( USES_REGS1 );
|
|
#ifdef SFUNC
|
|
static void SFVarIn(Term);
|
|
static void sf_include(SFKeep *);
|
|
#endif
|
|
static Int p_init_queue( USES_REGS1 );
|
|
static Int p_enqueue( USES_REGS1 );
|
|
static void keepdbrefs(DBTerm * CACHE_TYPE);
|
|
static Int p_dequeue( USES_REGS1 );
|
|
static void ErDBE(DBRef CACHE_TYPE);
|
|
static void ReleaseTermFromDB(DBTerm * CACHE_TYPE);
|
|
static PredEntry *new_lu_entry(Term);
|
|
static PredEntry *new_lu_int_key(Int);
|
|
static PredEntry *find_lu_entry(Term);
|
|
static DBProp find_int_key(Int);
|
|
|
|
#define db_check_trail(x) { \
|
|
if (Unsigned(dbg->tofref) == Unsigned(x)) { \
|
|
goto error_tr_overflow; \
|
|
} \
|
|
}
|
|
|
|
static UInt new_trail_size(void)
|
|
{
|
|
CACHE_REGS
|
|
UInt sz = (LOCAL_TrailTop-(ADDR)TR)/2;
|
|
if (sz < K64)
|
|
return K64;
|
|
if (sz > M1)
|
|
return M1;
|
|
return sz;
|
|
}
|
|
|
|
static int
|
|
recover_from_record_error(int nargs)
|
|
{
|
|
CACHE_REGS
|
|
switch(LOCAL_Error_TYPE) {
|
|
case OUT_OF_STACK_ERROR:
|
|
if (!Yap_gcl(LOCAL_Error_Size, nargs, ENV, gc_P(P,CP))) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
goto recover_record;
|
|
case OUT_OF_TRAIL_ERROR:
|
|
if (!Yap_growtrail(new_trail_size(), FALSE)) {
|
|
Yap_Error(OUT_OF_TRAIL_ERROR, TermNil, "YAP could not grow trail in recorda/3");
|
|
return FALSE;
|
|
}
|
|
goto recover_record;
|
|
case OUT_OF_HEAP_ERROR:
|
|
if (!Yap_growheap(FALSE, LOCAL_Error_Size, NULL)) {
|
|
Yap_Error(OUT_OF_HEAP_ERROR, LOCAL_Error_Term, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
goto recover_record;
|
|
case OUT_OF_AUXSPACE_ERROR:
|
|
if (!Yap_ExpandPreAllocCodeSpace(LOCAL_Error_Size, NULL, TRUE)) {
|
|
Yap_Error(OUT_OF_AUXSPACE_ERROR, LOCAL_Error_Term, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
goto recover_record;
|
|
default:
|
|
Yap_Error(LOCAL_Error_TYPE, LOCAL_Error_Term, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
recover_record:
|
|
LOCAL_Error_Size = 0;
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef SUPPORT_HASH_TABLES
|
|
/* related property and hint on number of entries */
|
|
static void create_hash_table(DBProp p, Int hint) {
|
|
int off = sizeof(CELL)*4, out;
|
|
Int size;
|
|
|
|
if (hint < p->NOfEntries)
|
|
hint = p->NOfEntries;
|
|
while (off) {
|
|
Int limit = ((CELL)1) << (off);
|
|
if (inp >= limit) {
|
|
out += off;
|
|
inp >>= off;
|
|
}
|
|
off >>= 1;
|
|
}
|
|
if ((size = ((CELL)1) << out) < hint)
|
|
hint <<= 1;
|
|
/* clean up the table */
|
|
pt = tbl = (hash_db_entry *)AllocDBSpace(hint*sizeof(hash_db_entry));
|
|
Yap_LUClauseSpace += hint*sizeof(hash_db_entry);
|
|
for (i=0; i< hint; i++) {
|
|
pt->key = NULL;
|
|
pt++;
|
|
}
|
|
/* next insert the entries */
|
|
}
|
|
|
|
static void insert_in_table() {
|
|
|
|
}
|
|
|
|
static void remove_from_table() {
|
|
|
|
}
|
|
#endif
|
|
|
|
inline static CELL *cpcells(CELL *to, CELL *from, Int n)
|
|
{
|
|
#if HAVE_MEMMOVE
|
|
memmove((void *)to, (void *)from, (size_t)(n*sizeof(CELL)));
|
|
return(to+n);
|
|
#else
|
|
while (n-- >= 0) {
|
|
*to++ = *from++;
|
|
}
|
|
return(to);
|
|
#endif
|
|
}
|
|
|
|
static void linkblk(link_entry *r, CELL *c, CELL offs)
|
|
{
|
|
CELL p;
|
|
while ((p = (CELL)*r) != 0) {
|
|
Term t = c[p];
|
|
r++;
|
|
c[p] = AdjustIDBPtr(t, offs);
|
|
}
|
|
}
|
|
|
|
static Int cmpclls(CELL *a,CELL *b,Int n)
|
|
{
|
|
while (n-- > 0) {
|
|
if(*a++ != *b++) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#if !THREADS
|
|
int Yap_DBTrailOverflow()
|
|
{
|
|
return((CELL *)LOCAL_s_dbg->lr > (CELL *)LOCAL_s_dbg->tofref - 2048);
|
|
}
|
|
#endif
|
|
|
|
/* get DB entry for ap/arity; */
|
|
static Prop
|
|
FindDBPropHavingLock(AtomEntry *ae, int CodeDB, unsigned int arity, Term dbmod)
|
|
{
|
|
Prop p0;
|
|
DBProp p;
|
|
|
|
p = RepDBProp(p0 = ae->PropsOfAE);
|
|
while (p0 && (((p->KindOfPE & ~0x1) != (CodeDB|DBProperty)) ||
|
|
(p->ArityOfDB != arity) ||
|
|
((CodeDB & MkCode) && p->ModuleOfDB && p->ModuleOfDB != dbmod))) {
|
|
p = RepDBProp(p0 = p->NextOfPE);
|
|
}
|
|
return p0;
|
|
}
|
|
|
|
|
|
/* get DB entry for ap/arity; */
|
|
static Prop
|
|
FindDBProp(AtomEntry *ae, int CodeDB, unsigned int arity, Term dbmod)
|
|
{
|
|
Prop out;
|
|
|
|
READ_LOCK(ae->ARWLock);
|
|
out = FindDBPropHavingLock(ae, CodeDB, arity, dbmod);
|
|
READ_UNLOCK(ae->ARWLock);
|
|
return(out);
|
|
}
|
|
|
|
|
|
|
|
/* These two functions allow us a fast lookup method in the data base */
|
|
/* PutMasks builds the mask and hash for a single argument */
|
|
inline static CELL
|
|
CalcKey(Term tw)
|
|
{
|
|
/* The first argument is known to be instantiated */
|
|
if (IsApplTerm(tw)) {
|
|
Functor f = FunctorOfTerm(tw);
|
|
if (IsExtensionFunctor(f)) {
|
|
if (f == FunctorDBRef) {
|
|
return(FunctorHash(tw)); /* Ref */
|
|
} /* if (f == FunctorLongInt || f == FunctorDouble) */
|
|
return(NumberHash(RepAppl(tw)[1]));
|
|
}
|
|
return(FunctorHash(f));
|
|
} else if (IsAtomOrIntTerm(tw)) {
|
|
if (IsAtomTerm(tw)) {
|
|
return(AtomHash(tw));
|
|
}
|
|
return(NumberHash(tw));
|
|
}
|
|
return(FunctorHash(FunctorList));
|
|
}
|
|
|
|
/* EvalMasks builds the mask and hash for up to three arguments of a term */
|
|
static CELL
|
|
EvalMasks(register Term tm, CELL *keyp)
|
|
{
|
|
|
|
if (IsVarTerm(tm)) {
|
|
*keyp = 0L;
|
|
return(0L);
|
|
} else if (IsApplTerm(tm)) {
|
|
Functor fun = FunctorOfTerm(tm);
|
|
|
|
if (IsExtensionFunctor(fun)) {
|
|
if (fun == FunctorDBRef) {
|
|
*keyp = FunctorHash(tm); /* Ref */
|
|
} else /* if (f == FunctorLongInt || f == FunctorDouble) */ {
|
|
*keyp = NumberHash(RepAppl(tm)[1]);
|
|
}
|
|
return(FourHashFieldMask);
|
|
} else {
|
|
unsigned int arity;
|
|
|
|
arity = ArityOfFunctor(fun);
|
|
#ifdef SFUNC
|
|
if (arity == SFArity) { /* do not even try to calculate masks */
|
|
*keyp = key;
|
|
return(FourHashFieldMask);
|
|
}
|
|
#endif
|
|
switch (arity) {
|
|
case 1:
|
|
{
|
|
Term tw = ArgOfTerm(1, tm);
|
|
|
|
if (IsNonVarTerm(tw)) {
|
|
*keyp = (FunctorHash(fun) & DualHashFieldMask) | (CalcKey(tw) << TWO_FIELDS_SHIFT);
|
|
return(FourHashFieldMask);
|
|
} else {
|
|
*keyp = (FunctorHash(fun) & DualHashFieldMask);
|
|
return(DualHashFieldMask);
|
|
}
|
|
}
|
|
case 2:
|
|
{
|
|
Term tw1, tw2;
|
|
CELL key, mask;
|
|
|
|
key = FunctorHash(fun) & DualHashFieldMask;
|
|
mask = DualHashFieldMask;
|
|
|
|
tw1 = ArgOfTerm(1, tm);
|
|
if (IsNonVarTerm(tw1)) {
|
|
key |= ((CalcKey(tw1) & HashFieldMask) << TWO_FIELDS_SHIFT);
|
|
mask |= (HashFieldMask << TWO_FIELDS_SHIFT);
|
|
}
|
|
tw2 = ArgOfTerm(2, tm);
|
|
if (IsNonVarTerm(tw2)) {
|
|
*keyp = key | (CalcKey(tw2) << THREE_FIELDS_SHIFT);
|
|
return(mask | (HashFieldMask << THREE_FIELDS_SHIFT));
|
|
} else {
|
|
*keyp = key;
|
|
return(mask);
|
|
}
|
|
}
|
|
default:
|
|
{
|
|
Term tw1, tw2, tw3;
|
|
CELL key, mask;
|
|
|
|
key = FunctorHash(fun) & HashFieldMask;
|
|
mask = HashFieldMask;
|
|
|
|
tw1 = ArgOfTerm(1, tm);
|
|
if (IsNonVarTerm(tw1)) {
|
|
key |= (CalcKey(tw1) & HashFieldMask) << ONE_FIELD_SHIFT;
|
|
mask |= HashFieldMask << ONE_FIELD_SHIFT;
|
|
}
|
|
tw2 = ArgOfTerm(2, tm);
|
|
if (IsNonVarTerm(tw2)) {
|
|
key |= (CalcKey(tw2) & HashFieldMask) << TWO_FIELDS_SHIFT;
|
|
mask |= HashFieldMask << TWO_FIELDS_SHIFT;
|
|
}
|
|
tw3 = ArgOfTerm(3, tm);
|
|
if (IsNonVarTerm(tw3)) {
|
|
*keyp = key | (CalcKey(tw3) << THREE_FIELDS_SHIFT);
|
|
return(mask | (HashFieldMask << THREE_FIELDS_SHIFT));
|
|
} else {
|
|
*keyp = key;
|
|
return(mask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
CELL key = (FunctorHash(FunctorList) & DualHashFieldMask);
|
|
CELL mask = DualHashFieldMask;
|
|
Term th = HeadOfTerm(tm), tt;
|
|
|
|
if (IsNonVarTerm(th)) {
|
|
mask |= (HashFieldMask << TWO_FIELDS_SHIFT);
|
|
key |= (CalcKey(th) << TWO_FIELDS_SHIFT);
|
|
}
|
|
tt = TailOfTerm(tm);
|
|
if (IsNonVarTerm(tt)) {
|
|
*keyp = key | (CalcKey(tt) << THREE_FIELDS_SHIFT);
|
|
return( mask|(HashFieldMask << THREE_FIELDS_SHIFT));
|
|
}
|
|
*keyp = key;
|
|
return(mask);
|
|
}
|
|
}
|
|
|
|
CELL
|
|
Yap_EvalMasks(register Term tm, CELL *keyp)
|
|
{
|
|
return EvalMasks(tm, keyp);
|
|
}
|
|
|
|
|
|
/* Called to inform that a new pointer to a data base entry has been added */
|
|
#define MarkThisRef(Ref) ((Ref)->NOfRefsTo ++ )
|
|
|
|
/* From a term, builds its representation in the data base */
|
|
|
|
/* otherwise, we just need to restore variables*/
|
|
typedef struct {
|
|
CELL *addr;
|
|
} visitel;
|
|
#define DB_UNWIND_CUNIF() \
|
|
while (visited < (visitel *)AuxSp) { \
|
|
RESET_VARIABLE(visited->addr); \
|
|
visited ++; \
|
|
}
|
|
|
|
/* no checking for overflow while building DB terms yet */
|
|
#define CheckDBOverflow(X) if (CodeMax+X >= (CELL *)visited-1024) { \
|
|
goto error; \
|
|
}
|
|
|
|
/* no checking for overflow while building DB terms yet */
|
|
#define CheckVisitOverflow() if ((CELL *)to_visit+1024 >= ASP) { \
|
|
goto error2; \
|
|
}
|
|
|
|
static CELL *
|
|
copy_long_int(CELL *st, CELL *pt)
|
|
{
|
|
/* first thing, store a link to the list before we move on */
|
|
st[0] = (CELL)FunctorLongInt;
|
|
st[1] = pt[1];
|
|
st[2] = EndSpecials;
|
|
/* now reserve space */
|
|
return st+3;
|
|
}
|
|
|
|
static CELL *
|
|
copy_double(CELL *st, CELL *pt)
|
|
{
|
|
/* first thing, store a link to the list before we move on */
|
|
st[0] = (CELL)FunctorDouble;
|
|
st[1] = pt[1];
|
|
#if SIZEOF_DOUBLE == 2*SIZEOF_INT_P
|
|
st[2] = pt[2];
|
|
st[3] = EndSpecials;
|
|
#else
|
|
st[2] = EndSpecials;
|
|
#endif
|
|
/* now reserve space */
|
|
return st+(2+SIZEOF_DOUBLE/SIZEOF_INT_P);
|
|
}
|
|
|
|
static CELL *
|
|
copy_string(CELL *st, CELL *pt)
|
|
{
|
|
UInt sz = pt[1]+3;
|
|
/* first thing, store a link to the list before we move on */
|
|
memcpy(st,pt,sizeof(CELL)*sz);
|
|
/* now reserve space */
|
|
return st+sz;
|
|
}
|
|
|
|
#ifdef USE_GMP
|
|
static CELL *
|
|
copy_big_int(CELL *st, CELL *pt)
|
|
{
|
|
Int sz =
|
|
sizeof(MP_INT)+
|
|
(((MP_INT *)(pt+2))->_mp_alloc*sizeof(mp_limb_t));
|
|
|
|
/* first functor */
|
|
st[0] = (CELL)FunctorBigInt;
|
|
st[1] = pt[1];
|
|
/* then the actual number */
|
|
memcpy((void *)(st+2), (void *)(pt+2), sz);
|
|
st = st+2+sz/CellSize;
|
|
/* then the tail for gc */
|
|
st[0] = EndSpecials;
|
|
return st+1;
|
|
}
|
|
#endif /* BIG_INT */
|
|
|
|
#define DB_MARKED(d0) ((CELL *)(d0) < CodeMax && (CELL *)(d0) >= tbase)
|
|
|
|
|
|
/* This routine creates a complex term in the heap. */
|
|
static CELL *MkDBTerm(register CELL *pt0, register CELL *pt0_end,
|
|
register CELL *StoPoint,
|
|
CELL *CodeMax, CELL *tbase,
|
|
#ifdef COROUTINING
|
|
CELL *attachmentsp,
|
|
#endif
|
|
int *vars_foundp,
|
|
struct db_globs *dbg)
|
|
{
|
|
CACHE_REGS
|
|
#if THREADS
|
|
#undef Yap_REGS
|
|
register REGSTORE *regp = Yap_regp;
|
|
#define Yap_REGS (*regp)
|
|
#endif
|
|
register visitel *visited = (visitel *)AuxSp;
|
|
/* store this in H */
|
|
register CELL **to_visit = (CELL **)HR;
|
|
CELL **to_visit_base = to_visit;
|
|
/* where we are going to add a new pair */
|
|
int vars_found = 0;
|
|
#ifdef COROUTINING
|
|
Term ConstraintsTerm = TermNil;
|
|
CELL *origH = HR;
|
|
#endif
|
|
CELL *CodeMaxBase = CodeMax;
|
|
|
|
loop:
|
|
while (pt0 <= pt0_end) {
|
|
|
|
CELL *ptd0 = pt0;
|
|
CELL d0 = *ptd0;
|
|
restart:
|
|
if (IsVarTerm(d0))
|
|
goto deref_var;
|
|
|
|
if (IsApplTerm(d0)) {
|
|
register Functor f;
|
|
register CELL *ap2;
|
|
|
|
/* we will need to link afterwards */
|
|
ap2 = RepAppl(d0);
|
|
#ifdef RATIONAL_TREES
|
|
if (ap2 >= tbase && ap2 <= StoPoint) {
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall((CELL)(StoPoint)-(CELL)(tbase));
|
|
*StoPoint++ = d0;
|
|
++pt0;
|
|
continue;
|
|
}
|
|
#endif
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall((CELL)(StoPoint)-(CELL)(tbase));
|
|
f = (Functor)(*ap2);
|
|
if (IsExtensionFunctor(f)) {
|
|
switch((CELL)f) {
|
|
case (CELL)FunctorDBRef:
|
|
{
|
|
DBRef dbentry;
|
|
/* store now the correct entry */
|
|
dbentry = DBRefOfTerm(d0);
|
|
*StoPoint++ = d0;
|
|
dbg->lr--;
|
|
if (dbentry->Flags & LogUpdMask) {
|
|
LogUpdClause *cl = (LogUpdClause *)dbentry;
|
|
|
|
cl->ClRefCount++;
|
|
} else {
|
|
dbentry->NOfRefsTo++;
|
|
}
|
|
*--dbg->tofref = dbentry;
|
|
db_check_trail(dbg->lr);
|
|
/* just continue the loop */
|
|
++ pt0;
|
|
continue;
|
|
}
|
|
case (CELL)FunctorLongInt:
|
|
CheckDBOverflow(3);
|
|
*StoPoint++ = AbsAppl(CodeMax);
|
|
CodeMax = copy_long_int(CodeMax, ap2);
|
|
++pt0;
|
|
continue;
|
|
#ifdef USE_GMP
|
|
case (CELL)FunctorBigInt:
|
|
CheckDBOverflow(3+Yap_SizeOfBigInt(d0));
|
|
/* first thing, store a link to the list before we move on */
|
|
*StoPoint++ = AbsAppl(CodeMax);
|
|
CodeMax = copy_big_int(CodeMax, ap2);
|
|
++pt0;
|
|
continue;
|
|
#endif
|
|
case (CELL)FunctorString:
|
|
{
|
|
CELL *st = CodeMax;
|
|
|
|
CheckDBOverflow(3+ap2[1]);
|
|
/* first thing, store a link to the list before we move on */
|
|
*StoPoint++ = AbsAppl(st);
|
|
CodeMax = copy_string(CodeMax, ap2);
|
|
++pt0;
|
|
continue;
|
|
}
|
|
case (CELL)FunctorDouble:
|
|
{
|
|
CELL *st = CodeMax;
|
|
|
|
CheckDBOverflow(4);
|
|
/* first thing, store a link to the list before we move on */
|
|
*StoPoint++ = AbsAppl(st);
|
|
CodeMax = copy_double(CodeMax, ap2);
|
|
++pt0;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
/* first thing, store a link to the list before we move on */
|
|
*StoPoint++ = AbsAppl(CodeMax);
|
|
/* next, postpone analysis to the rest of the current list */
|
|
CheckVisitOverflow();
|
|
#ifdef RATIONAL_TREES
|
|
to_visit[0] = pt0+1;
|
|
to_visit[1] = pt0_end;
|
|
to_visit[2] = StoPoint;
|
|
to_visit[3] = (CELL *)*pt0;
|
|
to_visit += 4;
|
|
*pt0 = StoPoint[-1];
|
|
#else
|
|
if (pt0 < pt0_end) {
|
|
to_visit[0] = pt0+1;
|
|
to_visit[1] = pt0_end;
|
|
to_visit[2] = StoPoint;
|
|
to_visit += 3;
|
|
}
|
|
#endif
|
|
d0 = ArityOfFunctor(f);
|
|
pt0 = ap2+1;
|
|
pt0_end = ap2 + d0;
|
|
CheckDBOverflow(d0+1);
|
|
/* prepare for our new compound term */
|
|
/* first the functor */
|
|
*CodeMax++ = (CELL)f;
|
|
/* we'll be working here */
|
|
StoPoint = CodeMax;
|
|
/* now reserve space */
|
|
CodeMax += d0;
|
|
continue;
|
|
}
|
|
else if (IsPairTerm(d0)) {
|
|
/* we will need to link afterwards */
|
|
CELL *ap2 = RepPair(d0);
|
|
if (ap2 >= tbase && ap2 <= StoPoint) {
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall((CELL)(StoPoint)-(CELL)(tbase));
|
|
*StoPoint++ = d0;
|
|
++pt0;
|
|
continue;
|
|
}
|
|
if (IsAtomOrIntTerm(Deref(ap2[0])) &&
|
|
IsPairTerm(Deref(ap2[1]))) {
|
|
/* shortcut for [1,2,3,4,5] */
|
|
Term tt = Deref(ap2[1]);
|
|
Term th = Deref(ap2[0]);
|
|
Int direction = RepPair(tt)-ap2;
|
|
CELL *OldStoPoint;
|
|
CELL *lp;
|
|
|
|
if (direction < 0)
|
|
direction = -1;
|
|
else
|
|
direction = 1;
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall((CELL)(StoPoint)-(CELL)(tbase));
|
|
*StoPoint++ = AbsPair(CodeMax);
|
|
OldStoPoint = StoPoint;
|
|
do {
|
|
lp = RepPair(tt);
|
|
|
|
if (lp >= tbase && lp <= StoPoint) {
|
|
break;
|
|
}
|
|
CheckDBOverflow(2);
|
|
CodeMax[0] = th;
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall((CELL)(CodeMax+1)-(CELL)(tbase));
|
|
CodeMax[1] = AbsPair(CodeMax+2);
|
|
CodeMax+=2;
|
|
th = Deref(lp[0]);
|
|
tt = Deref(lp[1]);
|
|
} while (IsAtomOrIntTerm(th) &&
|
|
IsPairTerm(tt) &&
|
|
/* have same direction to avoid infinite terms X = [a|X] */
|
|
(RepPair(tt)-lp)*direction > 0);
|
|
if (lp >= tbase && lp <= StoPoint) {
|
|
CodeMax[-1] = tt;
|
|
break;
|
|
}
|
|
if (IsAtomOrIntTerm(th) && IsAtomOrIntTerm(tt)) {
|
|
CheckDBOverflow(2);
|
|
CodeMax[0] = th;
|
|
CodeMax[1] = tt;
|
|
CodeMax+=2;
|
|
++pt0;
|
|
continue;
|
|
}
|
|
d0 = AbsPair(lp);
|
|
StoPoint = OldStoPoint;
|
|
} else {
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall((CELL)(StoPoint)-(CELL)(tbase));
|
|
*StoPoint++ = AbsPair(CodeMax);
|
|
}
|
|
/* next, postpone analysis to the rest of the current list */
|
|
#ifdef RATIONAL_TREES
|
|
to_visit[0] = pt0+1;
|
|
to_visit[1] = pt0_end;
|
|
to_visit[2] = StoPoint;
|
|
to_visit[3] = (CELL *)*pt0;
|
|
to_visit += 4;
|
|
*pt0 = StoPoint[-1];
|
|
#else
|
|
if (pt0 < pt0_end) {
|
|
to_visit[0] = pt0+1;
|
|
to_visit[1] = pt0_end;
|
|
to_visit[2] = StoPoint;
|
|
to_visit += 3;
|
|
}
|
|
#endif
|
|
CheckVisitOverflow();
|
|
/* new list */
|
|
/* we are working at CodeMax */
|
|
StoPoint = CodeMax;
|
|
/* set ptr to new term being analysed */
|
|
pt0 = RepPair(d0);
|
|
pt0_end = RepPair(d0) + 1;
|
|
/* reserve space for our new list */
|
|
CodeMax += 2;
|
|
CheckDBOverflow(2);
|
|
continue;
|
|
} else if (IsAtomOrIntTerm(d0)) {
|
|
*StoPoint++ = d0;
|
|
++pt0;
|
|
continue;
|
|
}
|
|
|
|
/* the code to dereference a variable */
|
|
deref_var:
|
|
if (!DB_MARKED(d0)) {
|
|
if (
|
|
#if YAPOR_SBA
|
|
d0 != 0
|
|
#else
|
|
d0 != (CELL)ptd0
|
|
#endif
|
|
) {
|
|
ptd0 = (Term *) d0;
|
|
d0 = *ptd0;
|
|
goto restart; /* continue dereferencing */
|
|
}
|
|
/* else just drop to found_var */
|
|
}
|
|
/* else just drop to found_var */
|
|
{
|
|
CELL displacement = (CELL)(StoPoint)-(CELL)(tbase);
|
|
|
|
pt0++;
|
|
/* first time we found this variable! */
|
|
if (!DB_MARKED(d0)) {
|
|
|
|
/* store previous value */
|
|
visited --;
|
|
visited->addr = ptd0;
|
|
CheckDBOverflow(1);
|
|
/* variables need to be offset at read time */
|
|
*ptd0 = (CELL)StoPoint;
|
|
#if YAPOR_SBA
|
|
/* the copy we keep will be an empty variable */
|
|
*StoPoint++ = 0;
|
|
#else
|
|
/* the copy we keep will be the current displacement */
|
|
*StoPoint = (CELL)StoPoint;
|
|
StoPoint++;
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall(displacement);
|
|
#endif
|
|
/* indicate we found variables */
|
|
vars_found++;
|
|
#ifdef COROUTINING
|
|
if (SafeIsAttachedTerm((CELL)ptd0)) {
|
|
Term t[4];
|
|
int sz = to_visit-to_visit_base;
|
|
|
|
HR = (CELL *)to_visit;
|
|
/* store the constraint away for: we need a back pointer to
|
|
the variable, the constraint in some cannonical form, what type
|
|
of constraint, and a list pointer */
|
|
t[0] = (CELL)ptd0;
|
|
t[1] = GLOBAL_attas[ExtFromCell(ptd0)].to_term_op(ptd0);
|
|
t[2] = MkIntegerTerm(ExtFromCell(ptd0));
|
|
t[3] = ConstraintsTerm;
|
|
ConstraintsTerm = Yap_MkApplTerm(FunctorClist, 4, t);
|
|
if (HR+sz >= ASP) {
|
|
goto error2;
|
|
}
|
|
memcpy((void *)HR, (void *)(to_visit_base), sz*sizeof(CELL *));
|
|
to_visit_base = (CELL **)HR;
|
|
to_visit = to_visit_base+sz;
|
|
}
|
|
#endif
|
|
continue;
|
|
} else {
|
|
/* references need to be offset at read time */
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall(displacement);
|
|
/* store the offset */
|
|
*StoPoint = d0;
|
|
StoPoint++;
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Do we still have compound terms to visit */
|
|
if (to_visit > to_visit_base) {
|
|
#ifdef RATIONAL_TREES
|
|
to_visit -= 4;
|
|
pt0 = to_visit[0];
|
|
pt0_end = to_visit[1];
|
|
StoPoint = to_visit[2];
|
|
pt0[-1] = (CELL)to_visit[3];
|
|
#else
|
|
to_visit -= 3;
|
|
pt0 = to_visit[0];
|
|
pt0_end = to_visit[1];
|
|
CheckDBOverflow(1);
|
|
StoPoint = to_visit[2];
|
|
#endif
|
|
goto loop;
|
|
}
|
|
|
|
#ifdef COROUTINING
|
|
/* we still may have constraints to do */
|
|
if (ConstraintsTerm != TermNil &&
|
|
!IN_BETWEEN(tbase,RepAppl(ConstraintsTerm),CodeMax)) {
|
|
*attachmentsp = (CELL)(CodeMax+1);
|
|
pt0 = RepAppl(ConstraintsTerm)+1;
|
|
pt0_end = RepAppl(ConstraintsTerm)+4;
|
|
StoPoint = CodeMax;
|
|
*StoPoint++ = RepAppl(ConstraintsTerm)[0];
|
|
ConstraintsTerm = AbsAppl(CodeMax);
|
|
CheckDBOverflow(1);
|
|
CodeMax += 5;
|
|
goto loop;
|
|
}
|
|
#endif
|
|
/* we're done */
|
|
*vars_foundp = vars_found;
|
|
DB_UNWIND_CUNIF();
|
|
#ifdef COROUTINING
|
|
HR = origH;
|
|
#endif
|
|
return CodeMax;
|
|
|
|
error:
|
|
LOCAL_Error_TYPE = OUT_OF_AUXSPACE_ERROR;
|
|
LOCAL_Error_Size = 1024+((char *)AuxSp-(char *)CodeMaxBase);
|
|
*vars_foundp = vars_found;
|
|
#ifdef RATIONAL_TREES
|
|
while (to_visit > to_visit_base) {
|
|
to_visit -= 4;
|
|
pt0 = to_visit[0];
|
|
pt0_end = to_visit[1];
|
|
StoPoint = to_visit[2];
|
|
pt0[-1] = (CELL)to_visit[3];
|
|
}
|
|
#endif
|
|
DB_UNWIND_CUNIF();
|
|
#ifdef COROUTINING
|
|
HR = origH;
|
|
#endif
|
|
return NULL;
|
|
|
|
error2:
|
|
LOCAL_Error_TYPE = OUT_OF_STACK_ERROR;
|
|
*vars_foundp = vars_found;
|
|
#ifdef RATIONAL_TREES
|
|
while (to_visit > to_visit_base) {
|
|
to_visit -= 4;
|
|
pt0 = to_visit[0];
|
|
pt0_end = to_visit[1];
|
|
StoPoint = to_visit[2];
|
|
pt0[-1] = (CELL)to_visit[3];
|
|
}
|
|
#endif
|
|
DB_UNWIND_CUNIF();
|
|
#ifdef COROUTINING
|
|
HR = origH;
|
|
#endif
|
|
return NULL;
|
|
|
|
error_tr_overflow:
|
|
LOCAL_Error_TYPE = OUT_OF_TRAIL_ERROR;
|
|
*vars_foundp = vars_found;
|
|
#ifdef RATIONAL_TREES
|
|
while (to_visit > to_visit_base) {
|
|
to_visit -= 4;
|
|
pt0 = to_visit[0];
|
|
pt0_end = to_visit[1];
|
|
StoPoint = to_visit[2];
|
|
pt0[-1] = (CELL)to_visit[3];
|
|
}
|
|
#endif
|
|
DB_UNWIND_CUNIF();
|
|
#ifdef COROUTINING
|
|
HR = origH;
|
|
#endif
|
|
return NULL;
|
|
#if THREADS
|
|
#undef Yap_REGS
|
|
#define Yap_REGS (*Yap_regp)
|
|
#endif /* THREADS */
|
|
}
|
|
|
|
|
|
#ifdef SFUNC
|
|
/*
|
|
* The sparse terms existing in the structure are to be included now. This
|
|
* means simple copy for constant terms but, some care about variables If
|
|
* they have appeared before, we will know by their position number
|
|
*/
|
|
static void
|
|
sf_include(SFKeep *sfp, struct db_globs *dbg)
|
|
SFKeep *sfp;
|
|
{
|
|
Term Tm = sfp->SName;
|
|
CELL *tp = ArgsOfSFTerm(Tm);
|
|
Register Term *StoPoint = ntp;
|
|
CELL *displacement = CodeAbs;
|
|
CELL arg_no;
|
|
Term tvalue;
|
|
int j = 3;
|
|
|
|
if (sfp->SFather != NIL)
|
|
*(sfp->SFather) = AbsAppl(displacement);
|
|
*StoPoint++ = FunctorOfTerm(Tm);
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall(displacement + 1);
|
|
*StoPoint++ = (Term) (displacement + 1);
|
|
while (*tp) {
|
|
arg_no = *tp++;
|
|
tvalue = Derefa(tp++);
|
|
if (IsVarTerm(tvalue)) {
|
|
if (((VarKeep *) tvalue)->NOfVars != 0) {
|
|
*StoPoint++ = arg_no;
|
|
db_check_trail(dbg->lr+1);
|
|
*dbg->lr++ = ToSmall(displacement + j);
|
|
if (((VarKeep *) tvalue)->New == 0)
|
|
*StoPoint++ = ((VarKeep *) tvalue)->New = Unsigned(displacement + j);
|
|
else
|
|
*StoPoint++ = ((VarKeep *) tvalue)->New;
|
|
j += 2;
|
|
}
|
|
} else if (IsAtomicTerm(tvalue)) {
|
|
*StoPoint++ = arg_no;
|
|
*StoPoint++ = tvalue;
|
|
j += 2;
|
|
} else {
|
|
LOCAL_Error_TYPE = TYPE_ERROR_DBTERM;
|
|
LOCAL_Error_Term = d0;
|
|
LOCAL_ErrorMessage = "wrong term in SF";
|
|
return(NULL);
|
|
}
|
|
}
|
|
*StoPoint++ = 0;
|
|
ntp = StoPoint;
|
|
CodeAbs = displacement + j;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* This function is used to check if one of the terms in the idb is the
|
|
* constant to_compare
|
|
*/
|
|
inline static DBRef
|
|
check_if_cons(DBRef p, Term to_compare)
|
|
{
|
|
while (p != NIL
|
|
&& (p->Flags & (DBCode | ErasedMask | DBVar | DBNoVars | DBComplex)
|
|
|| p->DBT.Entry != Unsigned(to_compare)))
|
|
p = NextDBRef(p);
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* This function is used to check if one of the terms in the idb is a prolog
|
|
* variable
|
|
*/
|
|
static DBRef
|
|
check_if_var(DBRef p)
|
|
{
|
|
while (p != NIL &&
|
|
p->Flags & (DBCode | ErasedMask | DBAtomic | DBNoVars | DBComplex ))
|
|
p = NextDBRef(p);
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* This function is used to check if a Prolog complex term with variables
|
|
* already exists in the idb for that key. The comparison is alike ==, but
|
|
* only the relative binding of variables, not their position is used. The
|
|
* comparison is done using the function cmpclls only. The function could
|
|
* only fail if a functor was matched to a Prolog term, but then, it should
|
|
* have failed before because the structure of term would have been very
|
|
* different
|
|
*/
|
|
static DBRef
|
|
check_if_wvars(DBRef p, unsigned int NOfCells, CELL *BTptr)
|
|
{
|
|
CELL *memptr;
|
|
|
|
do {
|
|
while (p != NIL &&
|
|
p->Flags & (DBCode | ErasedMask | DBAtomic | DBNoVars | DBVar))
|
|
p = NextDBRef(p);
|
|
if (p == NIL)
|
|
return p;
|
|
memptr = CellPtr(&(p->DBT.Contents));
|
|
if (NOfCells == p->DBT.NOfCells
|
|
&& cmpclls(memptr, BTptr, NOfCells))
|
|
return p;
|
|
else
|
|
p = NextDBRef(p);
|
|
} while (TRUE);
|
|
return NIL;
|
|
}
|
|
|
|
static int
|
|
scheckcells(int NOfCells, register CELL *m1, register CELL *m2, link_entry *lp, register CELL bp)
|
|
{
|
|
CELL base = Unsigned(m1);
|
|
link_entry *lp1;
|
|
|
|
while (NOfCells-- > 0) {
|
|
Register CELL r1, r2;
|
|
|
|
r1 = *m1++;
|
|
r2 = *m2++;
|
|
if (r1 == r2)
|
|
continue;
|
|
else if (r2 + bp == r1) {
|
|
/* link pointers may not have been generated in the */
|
|
/* same order */
|
|
/* make sure r1 is really an offset. */
|
|
lp1 = lp;
|
|
r1 = m1 - (CELL *)base;
|
|
while (*lp1 != r1 && *lp1)
|
|
lp1++;
|
|
if (!(*lp1))
|
|
return FALSE;
|
|
/* keep the old link pointer for future search. */
|
|
/* vsc: this looks like a bug!!!! */
|
|
/* *lp1 = *lp++; */
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* the cousin of the previous, but with things a bit more sophisticated.
|
|
* mtchcells, if an error was an found, needs to test ........
|
|
*/
|
|
static DBRef
|
|
check_if_nvars(DBRef p, unsigned int NOfCells, CELL *BTptr, struct db_globs *dbg)
|
|
{
|
|
CELL *memptr;
|
|
|
|
do {
|
|
while (p != NIL &&
|
|
p->Flags & (DBCode | ErasedMask | DBAtomic | DBComplex | DBVar))
|
|
p = NextDBRef(p);
|
|
if (p == NIL)
|
|
return p;
|
|
memptr = CellPtr(p->DBT.Contents);
|
|
if (scheckcells(NOfCells, memptr, BTptr, dbg->LinkAr, Unsigned(p->DBT.Contents-1)))
|
|
return p;
|
|
else
|
|
p = NextDBRef(p);
|
|
} while(TRUE);
|
|
return NIL;
|
|
}
|
|
|
|
static DBRef
|
|
generate_dberror_msg(int errnumb, UInt sz, char *msg)
|
|
{
|
|
CACHE_REGS
|
|
LOCAL_Error_Size = sz;
|
|
LOCAL_Error_TYPE = errnumb;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = msg;
|
|
return NULL;
|
|
}
|
|
|
|
static DBRef
|
|
CreateDBWithDBRef(Term Tm, DBProp p, struct db_globs *dbg)
|
|
{
|
|
DBRef pp, dbr = DBRefOfTerm(Tm);
|
|
DBTerm *ppt;
|
|
|
|
if (p == NULL) {
|
|
UInt sz = sizeof(DBTerm)+2*sizeof(CELL);
|
|
ppt = (DBTerm *)AllocDBSpace(sz);
|
|
if (ppt == NULL) {
|
|
return generate_dberror_msg(OUT_OF_HEAP_ERROR, TermNil, "could not allocate heap");
|
|
}
|
|
dbg->sz = sz;
|
|
Yap_LUClauseSpace += sz;
|
|
pp = (DBRef)ppt;
|
|
} else {
|
|
UInt sz = DBLength(2*sizeof(DBRef));
|
|
pp = AllocDBSpace(sz);
|
|
if (pp == NULL) {
|
|
return generate_dberror_msg(OUT_OF_HEAP_ERROR, 0, "could not allocate space");
|
|
}
|
|
Yap_LUClauseSpace += sz;
|
|
dbg->sz = sz;
|
|
pp->id = FunctorDBRef;
|
|
pp->Flags = DBNoVars|DBComplex|DBWithRefs;
|
|
INIT_LOCK(pp->lock);
|
|
INIT_DBREF_COUNT(pp);
|
|
ppt = &(pp->DBT);
|
|
}
|
|
if (dbr->Flags & LogUpdMask) {
|
|
LogUpdClause *cl = (LogUpdClause *)dbr;
|
|
cl->ClRefCount++;
|
|
} else {
|
|
dbr->NOfRefsTo++;
|
|
}
|
|
ppt->Entry = Tm;
|
|
ppt->NOfCells = 0;
|
|
ppt->Contents[0] = (CELL)NULL;
|
|
ppt->Contents[1] = (CELL)dbr;
|
|
ppt->DBRefs = (DBRef *)(ppt->Contents+2);
|
|
#ifdef COROUTINING
|
|
ppt->ag.attachments = 0L;
|
|
#endif
|
|
return pp;
|
|
}
|
|
|
|
static DBTerm *
|
|
CreateDBTermForAtom(Term Tm, UInt extra_size, struct db_globs *dbg) {
|
|
DBTerm *ppt;
|
|
ADDR ptr;
|
|
UInt sz = extra_size+sizeof(DBTerm);
|
|
|
|
ptr = (ADDR)AllocDBSpace(sz);
|
|
if (ptr == NULL) {
|
|
return (DBTerm *)generate_dberror_msg(OUT_OF_HEAP_ERROR, 0, "could not allocate space");
|
|
}
|
|
Yap_LUClauseSpace += sz;
|
|
dbg->sz = sz;
|
|
ppt = (DBTerm *)(ptr+extra_size);
|
|
ppt->NOfCells = 0;
|
|
ppt->DBRefs = NULL;
|
|
#ifdef COROUTINING
|
|
ppt->ag.attachments = 0;
|
|
#endif
|
|
ppt->DBRefs = NULL;
|
|
ppt->Entry = Tm;
|
|
return ppt;
|
|
}
|
|
|
|
static DBTerm *
|
|
CreateDBTermForVar(UInt extra_size, struct db_globs *dbg)
|
|
{
|
|
DBTerm *ppt;
|
|
ADDR ptr;
|
|
UInt sz = extra_size+sizeof(DBTerm);
|
|
|
|
ptr = (ADDR)AllocDBSpace(sz);
|
|
if (ptr == NULL) {
|
|
return (DBTerm *)generate_dberror_msg(OUT_OF_HEAP_ERROR, 0, "could not allocate space");
|
|
}
|
|
Yap_LUClauseSpace += sz;
|
|
dbg->sz = sz;
|
|
ppt = (DBTerm *)(ptr+extra_size);
|
|
ppt->NOfCells = 0;
|
|
ppt->DBRefs = NULL;
|
|
#ifdef COROUTINING
|
|
ppt->ag.attachments = 0;
|
|
#endif
|
|
ppt->DBRefs = NULL;
|
|
ppt->Entry = (CELL)(&(ppt->Entry));
|
|
return ppt;
|
|
}
|
|
|
|
static DBRef
|
|
CreateDBRefForAtom(Term Tm, DBProp p, int InFlag, struct db_globs *dbg) {
|
|
Register DBRef pp;
|
|
SMALLUNSGN flag;
|
|
UInt sz = DBLength(NIL);
|
|
|
|
flag = DBAtomic;
|
|
if (InFlag & MkIfNot && (dbg->found_one = check_if_cons(p->First, Tm)))
|
|
return dbg->found_one;
|
|
pp = AllocDBSpace(sz);
|
|
if (pp == NIL) {
|
|
return generate_dberror_msg(OUT_OF_HEAP_ERROR, 0, "could not allocate space");
|
|
}
|
|
Yap_LUClauseSpace += sz;
|
|
dbg->sz = sz;
|
|
pp->id = FunctorDBRef;
|
|
INIT_LOCK(pp->lock);
|
|
INIT_DBREF_COUNT(pp);
|
|
pp->Flags = flag;
|
|
pp->Code = NULL;
|
|
pp->DBT.Entry = Tm;
|
|
pp->DBT.DBRefs = NULL;
|
|
pp->DBT.NOfCells = 0;
|
|
#ifdef COROUTINING
|
|
pp->DBT.ag.attachments = 0;
|
|
#endif
|
|
return(pp);
|
|
}
|
|
|
|
static DBRef
|
|
CreateDBRefForVar(Term Tm, DBProp p, int InFlag, struct db_globs *dbg) {
|
|
Register DBRef pp;
|
|
UInt sz = DBLength(NULL);
|
|
|
|
if (InFlag & MkIfNot && (dbg->found_one = check_if_var(p->First)))
|
|
return dbg->found_one;
|
|
pp = AllocDBSpace(sz);
|
|
if (pp == NULL) {
|
|
return generate_dberror_msg(OUT_OF_HEAP_ERROR, 0, "could not allocate space");
|
|
}
|
|
Yap_LUClauseSpace += sz;
|
|
dbg->sz = sz;
|
|
pp->id = FunctorDBRef;
|
|
pp->Flags = DBVar;
|
|
pp->DBT.Entry = (CELL) Tm;
|
|
pp->Code = NULL;
|
|
pp->DBT.NOfCells = 0;
|
|
pp->DBT.DBRefs = NULL;
|
|
#ifdef COROUTINING
|
|
pp->DBT.ag.attachments = 0;
|
|
#endif
|
|
INIT_LOCK(pp->lock);
|
|
INIT_DBREF_COUNT(pp);
|
|
return pp;
|
|
}
|
|
|
|
static DBRef
|
|
CreateDBStruct(Term Tm, DBProp p, int InFlag, int *pstat, UInt extra_size, struct db_globs *dbg)
|
|
{
|
|
CACHE_REGS
|
|
Register Term tt, *nar = NIL;
|
|
SMALLUNSGN flag;
|
|
int NOfLinks = 0;
|
|
/* place DBRefs in ConsultStack */
|
|
DBRef *TmpRefBase = (DBRef *)LOCAL_TrailTop;
|
|
CELL *CodeAbs; /* how much code did we find */
|
|
int vars_found = FALSE;
|
|
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
|
|
if (p == NULL) {
|
|
if (IsVarTerm(Tm)) {
|
|
#ifdef COROUTINING
|
|
if (!SafeIsAttachedTerm(Tm)) {
|
|
#endif
|
|
DBRef out = (DBRef)CreateDBTermForVar(extra_size, dbg);
|
|
*pstat = TRUE;
|
|
return out;
|
|
#ifdef COROUTINING
|
|
}
|
|
#endif
|
|
} else if (IsAtomOrIntTerm(Tm)) {
|
|
DBRef out = (DBRef)CreateDBTermForAtom(Tm, extra_size, dbg);
|
|
*pstat = FALSE;
|
|
return out;
|
|
}
|
|
} else {
|
|
if (IsVarTerm(Tm)
|
|
#ifdef COROUTINING
|
|
&& !SafeIsAttachedTerm(Tm)
|
|
#endif
|
|
) {
|
|
*pstat = TRUE;
|
|
return CreateDBRefForVar(Tm, p, InFlag, dbg);
|
|
} else if (IsAtomOrIntTerm(Tm)) {
|
|
return CreateDBRefForAtom(Tm, p, InFlag, dbg);
|
|
}
|
|
}
|
|
/* next, let's process a compound term */
|
|
{
|
|
DBTerm *ppt, *ppt0;
|
|
DBRef pp, pp0;
|
|
Term *ntp0, *ntp;
|
|
unsigned int NOfCells = 0;
|
|
#ifdef COROUTINING
|
|
CELL attachments = 0;
|
|
#endif
|
|
|
|
dbg->tofref = TmpRefBase;
|
|
|
|
if (p == NULL) {
|
|
ADDR ptr = Yap_PreAllocCodeSpace();
|
|
ppt0 = (DBTerm *)(ptr+extra_size);
|
|
pp0 = (DBRef)ppt0;
|
|
} else {
|
|
pp0 = (DBRef)Yap_PreAllocCodeSpace();
|
|
ppt0 = &(pp0->DBT);
|
|
}
|
|
if ((ADDR)ppt0 >= (ADDR)AuxSp-1024) {
|
|
LOCAL_Error_Size = (UInt)(extra_size+sizeof(ppt0));
|
|
LOCAL_Error_TYPE = OUT_OF_AUXSPACE_ERROR;
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return NULL;
|
|
}
|
|
ntp0 = ppt0->Contents;
|
|
if ((ADDR)TR >= LOCAL_TrailTop-1024) {
|
|
LOCAL_Error_Size = 0;
|
|
LOCAL_Error_TYPE = OUT_OF_TRAIL_ERROR;
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return NULL;
|
|
}
|
|
dbg->lr = dbg->LinkAr = (link_entry *)TR;
|
|
#ifdef COROUTINING
|
|
/* attachment */
|
|
if (IsVarTerm(Tm)) {
|
|
tt = (CELL)(ppt0->Contents);
|
|
ntp = MkDBTerm(VarOfTerm(Tm), VarOfTerm(Tm), ntp0, ntp0+1, ntp0-1,
|
|
&attachments,
|
|
&vars_found,
|
|
dbg);
|
|
if (ntp == NULL) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return NULL;
|
|
}
|
|
} else
|
|
#endif
|
|
if (IsPairTerm(Tm)) {
|
|
/* avoid null pointers!! */
|
|
tt = AbsPair(ppt0->Contents);
|
|
ntp = MkDBTerm(RepPair(Tm), RepPair(Tm)+1, ntp0, ntp0+2, ntp0-1,
|
|
#ifdef COROUTINING
|
|
&attachments,
|
|
#endif
|
|
&vars_found, dbg);
|
|
if (ntp == NULL) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
unsigned int arity;
|
|
Functor fun;
|
|
|
|
tt = AbsAppl(ppt0->Contents);
|
|
/* we need to store the functor manually */
|
|
fun = FunctorOfTerm(Tm);
|
|
if (IsExtensionFunctor(fun)) {
|
|
switch((CELL)fun) {
|
|
case (CELL)FunctorDouble:
|
|
ntp = copy_double(ntp0, RepAppl(Tm));
|
|
break;
|
|
case (CELL)FunctorString:
|
|
ntp = copy_string(ntp0, RepAppl(Tm));
|
|
break;
|
|
case (CELL)FunctorDBRef:
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return CreateDBWithDBRef(Tm, p, dbg);
|
|
#ifdef USE_GMP
|
|
case (CELL)FunctorBigInt:
|
|
ntp = copy_big_int(ntp0, RepAppl(Tm));
|
|
break;
|
|
#endif
|
|
default: /* LongInt */
|
|
ntp = copy_long_int(ntp0, RepAppl(Tm));
|
|
break;
|
|
}
|
|
} else {
|
|
*ntp0 = (CELL)fun;
|
|
arity = ArityOfFunctor(fun);
|
|
ntp = MkDBTerm(RepAppl(Tm)+1,
|
|
RepAppl(Tm)+arity,
|
|
ntp0+1, ntp0+1+arity, ntp0-1,
|
|
#ifdef COROUTINING
|
|
&attachments,
|
|
#endif
|
|
&vars_found, dbg);
|
|
if (ntp == NULL) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
CodeAbs = (CELL *)((CELL)ntp-(CELL)ntp0);
|
|
if (LOCAL_Error_TYPE) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return NULL; /* Error Situation */
|
|
}
|
|
NOfCells = ntp - ntp0; /* End Of Code Info */
|
|
*dbg->lr++ = 0;
|
|
NOfLinks = (dbg->lr - dbg->LinkAr);
|
|
if (vars_found || InFlag & InQueue ) {
|
|
|
|
/*
|
|
* Take into account the fact that one needs an entry
|
|
* for the number of links
|
|
*/
|
|
flag = DBComplex;
|
|
CodeAbs += (NOfLinks+(sizeof(CELL)/sizeof(BITS32)-1))/(sizeof(CELL)/sizeof(BITS32));
|
|
if ((CELL *)((char *)ntp0+(CELL)CodeAbs) > AuxSp) {
|
|
LOCAL_Error_Size = (UInt)DBLength(CodeAbs);
|
|
LOCAL_Error_TYPE = OUT_OF_AUXSPACE_ERROR;
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return NULL;
|
|
}
|
|
if ((InFlag & MkIfNot) && (dbg->found_one = check_if_wvars(p->First, NOfCells, ntp0))) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return dbg->found_one;
|
|
}
|
|
} else {
|
|
flag = DBNoVars;
|
|
if ((InFlag & MkIfNot) && (dbg->found_one = check_if_nvars(p->First, NOfCells, ntp0, dbg))) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return dbg->found_one;
|
|
}
|
|
}
|
|
if (dbg->tofref != TmpRefBase) {
|
|
CodeAbs += (TmpRefBase - dbg->tofref) + 1;
|
|
if ((CELL *)((char *)ntp0+(CELL)CodeAbs) > AuxSp) {
|
|
LOCAL_Error_Size = (UInt)DBLength(CodeAbs);
|
|
LOCAL_Error_TYPE = OUT_OF_AUXSPACE_ERROR;
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return NULL;
|
|
}
|
|
flag |= DBWithRefs;
|
|
}
|
|
#if SIZEOF_LINK_ENTRY==2
|
|
if (Unsigned(CodeAbs) >= 0x40000) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return generate_dberror_msg(SYSTEM_ERROR, 0, "trying to store term larger than 256KB");
|
|
}
|
|
#endif
|
|
if (p == NULL) {
|
|
UInt sz = (CELL)CodeAbs+extra_size+sizeof(DBTerm);
|
|
ADDR ptr = Yap_AllocCodeSpace(sz);
|
|
ppt = (DBTerm *)(ptr+extra_size);
|
|
if (ptr == NULL) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return generate_dberror_msg(OUT_OF_HEAP_ERROR, sz, "heap crashed against stacks");
|
|
}
|
|
Yap_LUClauseSpace += sz;
|
|
dbg->sz = sz;
|
|
pp = (DBRef)ppt;
|
|
} else {
|
|
UInt sz = DBLength(CodeAbs);
|
|
pp = AllocDBSpace(sz);
|
|
if (pp == NULL) {
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return generate_dberror_msg(OUT_OF_HEAP_ERROR, sz, "heap crashed against stacks");
|
|
}
|
|
Yap_LUClauseSpace += sz;
|
|
dbg->sz = sz;
|
|
pp->id = FunctorDBRef;
|
|
pp->Flags = flag;
|
|
INIT_LOCK(pp->lock);
|
|
INIT_DBREF_COUNT(pp);
|
|
ppt = &(pp->DBT);
|
|
}
|
|
if (flag & DBComplex) {
|
|
link_entry *woar;
|
|
|
|
ppt->NOfCells = NOfCells;
|
|
#ifdef COROUTINING
|
|
ppt->ag.attachments = attachments;
|
|
#endif
|
|
if (pp0 != pp) {
|
|
nar = ppt->Contents;
|
|
nar = (Term *) cpcells(CellPtr(nar), ntp0, Unsigned(NOfCells));
|
|
} else {
|
|
nar = ppt->Contents + Unsigned(NOfCells);
|
|
}
|
|
woar = (link_entry *)nar;
|
|
memcpy((void *)woar,(const void *)dbg->LinkAr,(size_t)(NOfLinks*sizeof(link_entry)));
|
|
woar += NOfLinks;
|
|
#ifdef ALIGN_LONGS
|
|
#if SIZEOF_INT_P==8
|
|
while ((Unsigned(woar) & 7) != 0)
|
|
woar++;
|
|
#else
|
|
if ((Unsigned(woar) & 3) != 0)
|
|
woar++;
|
|
#endif
|
|
#endif
|
|
nar = (Term *) (woar);
|
|
*pstat = TRUE;
|
|
} else if (flag & DBNoVars) {
|
|
if (pp0 != pp) {
|
|
nar = (Term *) cpcells(CellPtr(ppt->Contents), ntp0, Unsigned(NOfCells));
|
|
} else {
|
|
nar = ppt->Contents + Unsigned(NOfCells);
|
|
}
|
|
ppt->NOfCells = NOfCells;
|
|
}
|
|
if (ppt != ppt0) {
|
|
linkblk(dbg->LinkAr, CellPtr(ppt->Contents-1), (CELL)ppt-(CELL)ppt0);
|
|
ppt->Entry = AdjustIDBPtr(tt,(CELL)ppt-(CELL)ppt0);
|
|
#ifdef COROUTINING
|
|
if (attachments)
|
|
ppt->ag.attachments = AdjustIDBPtr(attachments,(CELL)ppt-(CELL)ppt0);
|
|
else
|
|
ppt->ag.attachments = 0L;
|
|
#endif
|
|
} else {
|
|
ppt->Entry = tt;
|
|
#ifdef COROUTINING
|
|
ppt->ag.attachments = attachments;
|
|
#endif
|
|
}
|
|
if (flag & DBWithRefs) {
|
|
DBRef *ptr = TmpRefBase, *rfnar = (DBRef *)nar;
|
|
|
|
*rfnar++ = NULL;
|
|
while (ptr != dbg->tofref)
|
|
*rfnar++ = *--ptr;
|
|
ppt->DBRefs = rfnar;
|
|
} else {
|
|
ppt->DBRefs = NULL;
|
|
}
|
|
Yap_ReleasePreAllocCodeSpace((ADDR)pp0);
|
|
return pp;
|
|
}
|
|
}
|
|
|
|
static DBRef
|
|
record(int Flag, Term key, Term t_data, Term t_code USES_REGS)
|
|
{
|
|
Register Term twork = key;
|
|
Register DBProp p;
|
|
Register DBRef x;
|
|
int needs_vars;
|
|
struct db_globs dbg;
|
|
|
|
LOCAL_s_dbg = &dbg;
|
|
dbg.found_one = NULL;
|
|
#ifdef SFUNC
|
|
FathersPlace = NIL;
|
|
#endif
|
|
if (EndOfPAEntr(p = FetchDBPropFromKey(twork, Flag & MkCode, TRUE, "record/3"))) {
|
|
return NULL;
|
|
}
|
|
if ((x = CreateDBStruct(t_data, p, Flag, &needs_vars, 0, &dbg)) == NULL) {
|
|
return NULL;
|
|
}
|
|
if ((Flag & MkIfNot) && dbg.found_one)
|
|
return NULL;
|
|
TRAIL_REF(x);
|
|
if (x->Flags & (DBNoVars|DBComplex))
|
|
x->Mask = EvalMasks(t_data, &x->Key);
|
|
else
|
|
x->Mask = x->Key = 0;
|
|
if (Flag & MkCode)
|
|
x->Flags |= DBCode;
|
|
else
|
|
x->Flags |= DBNoCode;
|
|
x->Parent = p;
|
|
#if MULTIPLE_STACKS
|
|
x->Flags |= DBClMask;
|
|
x->ref_count = 1;
|
|
#else
|
|
x->Flags |= (InUseMask | DBClMask);
|
|
#endif
|
|
x->NOfRefsTo = 0;
|
|
WRITE_LOCK(p->DBRWLock);
|
|
if (p->F0 == NULL) {
|
|
p->F0 = p->L0 = x;
|
|
x->p = x->n = NULL;
|
|
} else {
|
|
if (Flag & MkFirst) {
|
|
x->n = p->F0;
|
|
p->F0->p = x;
|
|
p->F0 = x;
|
|
x->p = NULL;
|
|
} else {
|
|
x->p = p->L0;
|
|
p->L0->n = x;
|
|
p->L0 = x;
|
|
x->n = NULL;
|
|
}
|
|
}
|
|
if (p->First == NIL) {
|
|
p->First = p->Last = x;
|
|
x->Prev = x->Next = NIL;
|
|
} else if (Flag & MkFirst) {
|
|
x->Prev = NIL;
|
|
(p->First)->Prev = x;
|
|
x->Next = p->First;
|
|
p->First = x;
|
|
} else {
|
|
x->Next = NIL;
|
|
(p->Last)->Next = x;
|
|
x->Prev = p->Last;
|
|
p->Last = x;
|
|
}
|
|
if (Flag & MkCode) {
|
|
x->Code = (yamop *) IntegerOfTerm(t_code);
|
|
}
|
|
WRITE_UNLOCK(p->DBRWLock);
|
|
return x;
|
|
}
|
|
|
|
/* add a new entry next to an old one */
|
|
static DBRef
|
|
record_at(int Flag, DBRef r0, Term t_data, Term t_code USES_REGS)
|
|
{
|
|
Register DBProp p;
|
|
Register DBRef x;
|
|
int needs_vars;
|
|
struct db_globs dbg;
|
|
|
|
LOCAL_s_dbg = &dbg;
|
|
#ifdef SFUNC
|
|
FathersPlace = NIL;
|
|
#endif
|
|
p = r0->Parent;
|
|
if ((x = CreateDBStruct(t_data, p, Flag, &needs_vars, 0, &dbg)) == NULL) {
|
|
return NULL;
|
|
}
|
|
TRAIL_REF(x);
|
|
if (x->Flags & (DBNoVars|DBComplex))
|
|
x->Mask = EvalMasks(t_data, &x->Key);
|
|
else
|
|
x->Mask = x->Key = 0;
|
|
if (Flag & MkCode)
|
|
x->Flags |= DBCode;
|
|
else
|
|
x->Flags |= DBNoCode;
|
|
x->Parent = p;
|
|
#if MULTIPLE_STACKS
|
|
x->Flags |= DBClMask;
|
|
x->ref_count = 1;
|
|
#else
|
|
x->Flags |= (InUseMask | DBClMask);
|
|
#endif
|
|
x->NOfRefsTo = 0;
|
|
WRITE_LOCK(p->DBRWLock);
|
|
if (Flag & MkFirst) {
|
|
x->n = r0;
|
|
x->p = r0->p;
|
|
if (p->F0 == r0) {
|
|
p->F0 = x;
|
|
} else {
|
|
r0->p->n = x;
|
|
}
|
|
r0->p = x;
|
|
} else {
|
|
x->p = r0;
|
|
x->n = r0->n;
|
|
if (p->L0 == r0) {
|
|
p->L0 = x;
|
|
} else {
|
|
r0->n->p = x;
|
|
}
|
|
r0->n = x;
|
|
}
|
|
if (Flag & MkFirst) {
|
|
x->Prev = r0->Prev;
|
|
x->Next = r0;
|
|
if (p->First == r0) {
|
|
p->First = x;
|
|
} else {
|
|
r0->Prev->Next = x;
|
|
}
|
|
r0->Prev = x;
|
|
} else {
|
|
x->Next = r0->Next;
|
|
x->Prev = r0;
|
|
if (p->Last == r0) {
|
|
p->Last = x;
|
|
} else {
|
|
r0->Next->Prev = x;
|
|
}
|
|
r0->Next = x;
|
|
}
|
|
if (Flag & WithRef) {
|
|
x->Code = (yamop *) IntegerOfTerm(t_code);
|
|
}
|
|
WRITE_UNLOCK(p->DBRWLock);
|
|
return x;
|
|
}
|
|
|
|
|
|
static LogUpdClause *
|
|
new_lu_db_entry(Term t, PredEntry *pe)
|
|
{
|
|
CACHE_REGS
|
|
DBTerm *x;
|
|
LogUpdClause *cl;
|
|
yamop *ipc;
|
|
int needs_vars = FALSE;
|
|
struct db_globs dbg;
|
|
int d_flag = 0;
|
|
|
|
#if MULTIPLE_STACKS
|
|
/* we cannot allow sharing between threads (for now) */
|
|
if (!pe || !(pe->PredFlags & ThreadLocalPredFlag))
|
|
d_flag |= InQueue;
|
|
#endif
|
|
LOCAL_s_dbg = &dbg;
|
|
ipc = NEXTOP(((LogUpdClause *)NULL)->ClCode,e);
|
|
if ((x = (DBTerm *)CreateDBStruct(t, NULL, d_flag, &needs_vars, (UInt)ipc, &dbg)) == NULL) {
|
|
return NULL; /* crash */
|
|
}
|
|
cl = (LogUpdClause *)((ADDR)x-(UInt)ipc);
|
|
ipc = cl->ClCode;
|
|
cl->Id = FunctorDBRef;
|
|
cl->ClFlags = LogUpdMask;
|
|
cl->lusl.ClSource = x;
|
|
cl->ClRefCount = 0;
|
|
cl->ClPred = pe;
|
|
cl->ClExt = NULL;
|
|
cl->ClPrev = cl->ClNext = NULL;
|
|
cl->ClSize = dbg.sz;
|
|
/* Support for timestamps */
|
|
if (pe && pe->LastCallOfPred != LUCALL_ASSERT) {
|
|
if (pe->TimeStampOfPred >= TIMESTAMP_RESET)
|
|
Yap_UpdateTimestamps(pe);
|
|
++pe->TimeStampOfPred;
|
|
/* fprintf(stderr,"+ %x--%d--%ul\n",pe,pe->TimeStampOfPred,pe->ArityOfPE);*/
|
|
pe->LastCallOfPred = LUCALL_ASSERT;
|
|
cl->ClTimeStart = pe->TimeStampOfPred;
|
|
} else {
|
|
cl->ClTimeStart = 0L;
|
|
}
|
|
cl->ClTimeEnd = TIMESTAMP_EOT;
|
|
#if MULTIPLE_STACKS
|
|
// INIT_LOCK(cl->ClLock);
|
|
INIT_CLREF_COUNT(cl);
|
|
ipc->opc = Yap_opcode(_copy_idb_term);
|
|
#else
|
|
if (needs_vars)
|
|
ipc->opc = Yap_opcode(_copy_idb_term);
|
|
else
|
|
ipc->opc = Yap_opcode(_unify_idb_term);
|
|
#endif
|
|
|
|
return cl;
|
|
}
|
|
|
|
|
|
LogUpdClause *
|
|
Yap_new_ludbe(Term t, PredEntry *pe, UInt nargs)
|
|
{
|
|
CACHE_REGS
|
|
LogUpdClause *x;
|
|
|
|
LOCAL_Error_Size = 0;
|
|
while ((x = new_lu_db_entry(t, pe)) == NULL) {
|
|
if (LOCAL_Error_TYPE == YAP_NO_ERROR) {
|
|
break;
|
|
} else {
|
|
XREGS[nargs+1] = t;
|
|
if (recover_from_record_error(nargs+1)) {
|
|
t = Deref(XREGS[nargs+1]);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
|
|
static LogUpdClause *
|
|
record_lu(PredEntry *pe, Term t, int position)
|
|
{
|
|
LogUpdClause *cl;
|
|
|
|
if ((cl = new_lu_db_entry(t, pe)) == NULL) {
|
|
return NULL;
|
|
}
|
|
{
|
|
Yap_inform_profiler_of_clause(cl, (char *)cl+cl->ClSize, pe, GPROF_NEW_LU_CLAUSE);
|
|
}
|
|
Yap_add_logupd_clause(pe, cl, (position == MkFirst ? 2 : 0));
|
|
return cl;
|
|
}
|
|
|
|
static LogUpdClause *
|
|
record_lu_at(int position, LogUpdClause *ocl, Term t)
|
|
{
|
|
LogUpdClause *cl;
|
|
PredEntry *pe;
|
|
|
|
pe = ocl->ClPred;
|
|
PELOCK(62,pe);
|
|
if ((cl = new_lu_db_entry(t,pe)) == NULL) {
|
|
UNLOCK(pe->PELock);
|
|
return NULL;
|
|
}
|
|
if(pe->cs.p_code.NOfClauses > 1)
|
|
Yap_RemoveIndexation(pe);
|
|
if (position == MkFirst) {
|
|
/* add before current clause */
|
|
cl->ClNext = ocl;
|
|
if (ocl->ClCode == pe->cs.p_code.FirstClause) {
|
|
cl->ClPrev = NULL;
|
|
pe->cs.p_code.FirstClause = cl->ClCode;
|
|
} else {
|
|
cl->ClPrev = ocl->ClPrev;
|
|
ocl->ClPrev->ClNext = cl;
|
|
}
|
|
ocl->ClPrev = cl;
|
|
} else {
|
|
/* add after current clause */
|
|
cl->ClPrev = ocl;
|
|
if (ocl->ClCode == pe->cs.p_code.LastClause) {
|
|
cl->ClNext = NULL;
|
|
pe->cs.p_code.LastClause = cl->ClCode;
|
|
} else {
|
|
cl->ClNext = ocl->ClNext;
|
|
ocl->ClNext->ClPrev = cl;
|
|
}
|
|
ocl->ClNext = cl;
|
|
}
|
|
pe->cs.p_code.NOfClauses++;
|
|
if (pe->cs.p_code.NOfClauses > 1) {
|
|
pe->OpcodeOfPred = INDEX_OPCODE;
|
|
pe->CodeOfPred = (yamop *)(&(pe->OpcodeOfPred));
|
|
}
|
|
UNLOCK(pe->PELock);
|
|
return cl;
|
|
}
|
|
|
|
|
|
/* recorda(+Functor,+Term,-Ref) */
|
|
static Int
|
|
p_rcda( USES_REGS1 )
|
|
{
|
|
/* Idiotic xlc's cpp does not work with ARG1 within MkDBRefTerm */
|
|
Term TRef, t1 = Deref(ARG1);
|
|
PredEntry *pe = NULL;
|
|
|
|
if (!IsVarTerm(Deref(ARG3)))
|
|
return (FALSE);
|
|
pe = find_lu_entry(t1);
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
if (pe) {
|
|
LogUpdClause *cl;
|
|
|
|
PELOCK(61,pe);
|
|
cl = record_lu(pe, Deref(ARG2), MkFirst);
|
|
if (cl != NULL) {
|
|
TRAIL_CLREF(cl);
|
|
#if MULTIPLE_STACKS
|
|
INC_CLREF_COUNT(cl);
|
|
#else
|
|
cl->ClFlags |= InUseMask;
|
|
#endif
|
|
TRef = MkDBRefTerm((DBRef)cl);
|
|
} else {
|
|
TRef = TermNil;
|
|
}
|
|
UNLOCK(pe->PELock);
|
|
} else {
|
|
TRef = MkDBRefTerm(record(MkFirst, t1, Deref(ARG2), Unsigned(0) PASS_REGS));
|
|
}
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(3)) {
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (!pe)
|
|
return FALSE;
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
/* '$recordap'(+Functor,+Term,-Ref) */
|
|
static Int
|
|
p_rcdap( USES_REGS1 )
|
|
{
|
|
Term TRef, t1 = Deref(ARG1), t2 = Deref(ARG2);
|
|
|
|
if (!IsVarTerm(Deref(ARG3)))
|
|
return FALSE;
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
TRef = MkDBRefTerm(record(MkFirst | MkCode, t1, t2, Unsigned(0) PASS_REGS));
|
|
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(3)) {
|
|
t1 = Deref(ARG1);
|
|
t2 = Deref(ARG2);
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
/* recorda_at(+DBRef,+Term,-Ref) */
|
|
/** @pred recorda_at(+ _R0_, _T_,- _R_)
|
|
|
|
|
|
Makes term _T_ the record preceding record with reference
|
|
_R0_, and unifies _R_ with its reference.
|
|
|
|
|
|
*/
|
|
static Int
|
|
p_rcda_at( USES_REGS1 )
|
|
{
|
|
/* Idiotic xlc's cpp does not work with ARG1 within MkDBRefTerm */
|
|
Term TRef, t1 = Deref(ARG1), t2 = Deref(ARG2);
|
|
DBRef dbr;
|
|
|
|
if (!IsVarTerm(Deref(ARG3)))
|
|
return FALSE;
|
|
if (IsVarTerm(t1)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t1, "recorda_at/3");
|
|
return FALSE;
|
|
}
|
|
if (!IsDBRefTerm(t1)) {
|
|
Yap_Error(TYPE_ERROR_DBREF, t1, "recorda_at/3");
|
|
return FALSE;
|
|
}
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
dbr = DBRefOfTerm(t1);
|
|
if (dbr->Flags & ErasedMask) {
|
|
/* doesn't make sense */
|
|
return FALSE;
|
|
}
|
|
if (dbr->Flags & LogUpdMask) {
|
|
TRef = MkDBRefTerm((DBRef)record_lu_at(MkFirst, (LogUpdClause *)dbr, t2));
|
|
} else {
|
|
TRef = MkDBRefTerm(record_at(MkFirst, DBRefOfTerm(t1), t2, Unsigned(0) PASS_REGS));
|
|
}
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(3)) {
|
|
t1 = Deref(ARG1);
|
|
t2 = Deref(ARG2);
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
/* recordz(+Functor,+Term,-Ref) */
|
|
/** @pred recordz(+ _K_, _T_,- _R_)
|
|
|
|
Makes term _T_ the last record under key _K_ and unifies _R_
|
|
with its reference.
|
|
|
|
*/
|
|
static Int
|
|
p_rcdz( USES_REGS1 )
|
|
{
|
|
Term TRef, t1 = Deref(ARG1), t2 = Deref(ARG2);
|
|
PredEntry *pe;
|
|
|
|
if (!IsVarTerm(Deref(ARG3)))
|
|
return (FALSE);
|
|
pe = find_lu_entry(t1);
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
if (pe) {
|
|
LogUpdClause *cl;
|
|
|
|
PELOCK(62,pe);
|
|
cl = record_lu(pe, t2, MkLast);
|
|
if (cl != NULL) {
|
|
TRAIL_CLREF(cl);
|
|
#if MULTIPLE_STACKS
|
|
INC_CLREF_COUNT(cl);
|
|
#else
|
|
cl->ClFlags |= InUseMask;
|
|
#endif
|
|
TRef = MkDBRefTerm((DBRef)cl);
|
|
} else {
|
|
TRef = TermNil;
|
|
}
|
|
UNLOCK(pe->PELock);
|
|
} else {
|
|
TRef = MkDBRefTerm(record(MkLast, t1, t2, Unsigned(0) PASS_REGS));
|
|
}
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(3)) {
|
|
t1 = Deref(ARG1);
|
|
t2 = Deref(ARG2);
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (!pe)
|
|
return FALSE;
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
/* recordz(+Functor,+Term,-Ref) */
|
|
Int
|
|
Yap_Recordz(Atom at, Term t2)
|
|
{
|
|
CACHE_REGS
|
|
PredEntry *pe;
|
|
|
|
pe = find_lu_entry(MkAtomTerm(at));
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
if (pe) {
|
|
record_lu(pe, t2, MkLast);
|
|
} else {
|
|
record(MkLast, MkAtomTerm(at), t2, Unsigned(0) PASS_REGS);
|
|
}
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
ARG1 = t2;
|
|
if (recover_from_record_error(1)) {
|
|
t2 = ARG1;
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* '$recordzp'(+Functor,+Term,-Ref) */
|
|
static Int
|
|
p_rcdzp( USES_REGS1 )
|
|
{
|
|
Term TRef, t1 = Deref(ARG1), t2 = Deref(ARG2);
|
|
|
|
if (!IsVarTerm(Deref(ARG3)))
|
|
return (FALSE);
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
TRef = MkDBRefTerm(record(MkLast | MkCode, t1, t2, Unsigned(0) PASS_REGS));
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(3)) {
|
|
t1 = Deref(ARG1);
|
|
t2 = Deref(ARG2);
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
/* recordz_at(+Functor,+Term,-Ref) */
|
|
/** @pred recordz_at(+ _R0_, _T_,- _R_)
|
|
|
|
|
|
Makes term _T_ the record following record with reference
|
|
_R0_, and unifies _R_ with its reference.
|
|
|
|
|
|
*/
|
|
static Int
|
|
p_rcdz_at( USES_REGS1 )
|
|
{
|
|
/* Idiotic xlc's cpp does not work with ARG1 within MkDBRefTerm */
|
|
Term TRef, t1 = Deref(ARG1), t2 = Deref(ARG2);
|
|
DBRef dbr;
|
|
|
|
if (!IsVarTerm(Deref(ARG3)))
|
|
return (FALSE);
|
|
if (IsVarTerm(t1)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t1, "recordz_at/3");
|
|
return FALSE;
|
|
}
|
|
if (!IsDBRefTerm(t1)) {
|
|
Yap_Error(TYPE_ERROR_DBREF, t1, "recordz_at/3");
|
|
return FALSE;
|
|
}
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
dbr = DBRefOfTerm(t1);
|
|
if (dbr->Flags & ErasedMask) {
|
|
/* doesn't make sense */
|
|
return FALSE;
|
|
}
|
|
if (dbr->Flags & LogUpdMask) {
|
|
TRef = MkDBRefTerm((DBRef)record_lu_at(MkLast, (LogUpdClause *)dbr, t2));
|
|
} else {
|
|
TRef = MkDBRefTerm(record_at(MkLast, dbr, t2, Unsigned(0) PASS_REGS));
|
|
}
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(3)) {
|
|
t1 = Deref(ARG1);
|
|
t2 = Deref(ARG2);
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
/* '$record_stat_source'(+Functor,+Term) */
|
|
static Int
|
|
p_rcdstatp( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1), t2 = Deref(ARG2), t3 = Deref(ARG3);
|
|
int mk_first;
|
|
Term TRef;
|
|
|
|
if (IsVarTerm(t3) || !IsIntTerm(t3))
|
|
return (FALSE);
|
|
if (IsVarTerm(t3) || !IsIntTerm(t3))
|
|
return (FALSE);
|
|
mk_first = ((IntOfTerm(t3) % 4) == 2);
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
if (mk_first)
|
|
TRef = MkDBRefTerm(record(MkFirst | MkCode, t1, t2, MkIntTerm(0) PASS_REGS));
|
|
else
|
|
TRef = MkDBRefTerm(record(MkLast | MkCode, t1, t2, MkIntTerm(0) PASS_REGS));
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(4)) {
|
|
t1 = Deref(ARG1);
|
|
t2 = Deref(ARG2);
|
|
t3 = Deref(ARG3);
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return Yap_unify(ARG4, TRef);
|
|
}
|
|
|
|
/* '$recordap'(+Functor,+Term,-Ref,+CRef) */
|
|
static Int
|
|
p_drcdap( USES_REGS1 )
|
|
{
|
|
Term TRef, t1 = Deref(ARG1), t2 = Deref(ARG2), t4 = Deref(ARG4);
|
|
|
|
if (!IsVarTerm(Deref(ARG3)))
|
|
return (FALSE);
|
|
if (IsVarTerm(t4) || !IsIntegerTerm(t4))
|
|
return (FALSE);
|
|
LOCAL_Error_Size = 0;
|
|
restart_record:
|
|
TRef = MkDBRefTerm(record(MkFirst | MkCode | WithRef,
|
|
t1, t2, t4 PASS_REGS));
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(4)) {
|
|
t1 = Deref(ARG1);
|
|
t2 = Deref(ARG2);
|
|
t4 = Deref(ARG4);
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
/* '$recordzp'(+Functor,+Term,-Ref,+CRef) */
|
|
static Int
|
|
p_drcdzp( USES_REGS1 )
|
|
{
|
|
Term TRef, t1 = Deref(ARG1), t2 = Deref(ARG2), t4 = Deref(ARG4);
|
|
|
|
if (!IsVarTerm(Deref(ARG3)))
|
|
return (FALSE);
|
|
if (IsVarTerm(t4) || !IsIntegerTerm(t4))
|
|
return (FALSE);
|
|
restart_record:
|
|
LOCAL_Error_Size = 0;
|
|
TRef = MkDBRefTerm(record(MkLast | MkCode | WithRef,
|
|
t1, t2, t4 PASS_REGS));
|
|
if (LOCAL_Error_TYPE != YAP_NO_ERROR) {
|
|
if (recover_from_record_error(4)) {
|
|
t1 = Deref(ARG1);
|
|
t2 = Deref(ARG2);
|
|
t4 = Deref(ARG4);
|
|
goto restart_record;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
static Int
|
|
p_still_variant( USES_REGS1 )
|
|
{
|
|
CELL *old_h = B->cp_h;
|
|
tr_fr_ptr old_tr = B->cp_tr;
|
|
Term t1 = Deref(ARG1), t2 = Deref(ARG2);
|
|
DBTerm *dbt;
|
|
DBRef dbr;
|
|
|
|
if (IsVarTerm(t1) || !IsDBRefTerm(t1)) {
|
|
return (FALSE);
|
|
/* limited sanity checking */
|
|
if (dbr->id != FunctorDBRef) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
dbr = DBRefOfTerm(t1);
|
|
}
|
|
/* ok, we assume there was a choicepoint before we copied the term */
|
|
|
|
/* skip binding for argument variable */
|
|
old_tr++;
|
|
if (dbr->Flags & LogUpdMask) {
|
|
LogUpdClause *cl = (LogUpdClause *)dbr;
|
|
|
|
if (old_tr == TR-1) {
|
|
if (TrailTerm(old_tr) != CLREF_TO_TRENTRY(cl))
|
|
return FALSE;
|
|
} else if (old_tr != TR)
|
|
return FALSE;
|
|
if (Yap_op_from_opcode(cl->ClCode->opc) == _unify_idb_term) {
|
|
return TRUE;
|
|
} else {
|
|
dbt = cl->lusl.ClSource;
|
|
}
|
|
} else {
|
|
if (old_tr == TR-1) {
|
|
if (TrailTerm(old_tr) != REF_TO_TRENTRY(dbr))
|
|
return FALSE;
|
|
} else if (old_tr != TR)
|
|
return FALSE;
|
|
if (dbr->Flags & (DBNoVars|DBAtomic))
|
|
return TRUE;
|
|
if (dbr->Flags & DBVar)
|
|
return IsVarTerm(t2);
|
|
dbt = &(dbr->DBT);
|
|
}
|
|
/*
|
|
we checked the trail, so we are sure only variables in the new term
|
|
were bound
|
|
*/
|
|
{
|
|
link_entry *lp = (link_entry *)(dbt->Contents+dbt->NOfCells);
|
|
link_entry link;
|
|
|
|
if (!dbt->NOfCells) {
|
|
return IsVarTerm(t2);
|
|
}
|
|
while ((link = *lp++)) {
|
|
Term t2 = Deref(old_h[link-1]);
|
|
if (IsUnboundVar(dbt->Contents+(link-1))) {
|
|
if (IsVarTerm(t2)) {
|
|
Yap_unify(t2,MkAtomTerm(AtomFoundVar));
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef COROUTINING
|
|
static int
|
|
copy_attachments(CELL *ts USES_REGS)
|
|
{
|
|
/* we will change delayed vars, and that also means the trail */
|
|
tr_fr_ptr tr0 = TR;
|
|
|
|
while (TRUE) {
|
|
/* store away in case there is an overflow */
|
|
|
|
if (GLOBAL_attas[IntegerOfTerm(ts[2])].term_to_op(ts[1], ts[0] PASS_REGS) == FALSE) {
|
|
/* oops, we did not have enough space to copy the elements */
|
|
/* reset queue of woken up goals */
|
|
TR = tr0;
|
|
return FALSE;
|
|
}
|
|
if (ts[3] == TermNil) return TRUE;
|
|
ts = RepAppl(ts[3])+1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static Term
|
|
GetDBLUKey(PredEntry *ap)
|
|
{
|
|
PELOCK(63,ap);
|
|
if (ap->PredFlags & NumberDBPredFlag) {
|
|
CACHE_REGS
|
|
Int id = ap->src.IndxId;
|
|
UNLOCK(ap->PELock);
|
|
return MkIntegerTerm(id);
|
|
} else if (ap->PredFlags & AtomDBPredFlag ||
|
|
(ap->ModuleOfPred != IDB_MODULE && ap->ArityOfPE == 0)) {
|
|
Atom at = (Atom)ap->FunctorOfPred;
|
|
UNLOCK(ap->PELock);
|
|
return MkAtomTerm(at);
|
|
} else {
|
|
Functor f = ap->FunctorOfPred;
|
|
UNLOCK(ap->PELock);
|
|
return Yap_MkNewApplTerm(f,ArityOfFunctor(f));
|
|
}
|
|
}
|
|
|
|
static int
|
|
UnifyDBKey(DBRef DBSP, PropFlags flags, Term t)
|
|
{
|
|
DBProp p = DBSP->Parent;
|
|
Term t1, tf;
|
|
|
|
READ_LOCK(p->DBRWLock);
|
|
/* get the key */
|
|
if (p->ArityOfDB == 0) {
|
|
t1 = MkAtomTerm((Atom)(p->FunctorOfDB));
|
|
} else {
|
|
t1 = Yap_MkNewApplTerm(p->FunctorOfDB,p->ArityOfDB);
|
|
}
|
|
if ((p->KindOfPE & CodeDBBit) && (flags & CodeDBBit)) {
|
|
Term t[2];
|
|
if (p->ModuleOfDB)
|
|
t[0] = p->ModuleOfDB;
|
|
else
|
|
t[0] = TermProlog;
|
|
t[1] = t1;
|
|
tf = Yap_MkApplTerm(FunctorModule, 2, t);
|
|
} else if (!(flags & CodeDBBit)) {
|
|
tf = t1;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
READ_UNLOCK(p->DBRWLock);
|
|
return Yap_unify(tf,t);
|
|
}
|
|
|
|
|
|
static int
|
|
UnifyDBNumber(DBRef DBSP, Term t)
|
|
{
|
|
CACHE_REGS
|
|
DBProp p = DBSP->Parent;
|
|
DBRef ref;
|
|
Int i = 1;
|
|
|
|
READ_LOCK(p->DBRWLock);
|
|
ref = p->First;
|
|
while (ref != NIL) {
|
|
if (ref == DBSP) break;
|
|
if (!DEAD_REF(ref)) i++;
|
|
ref = ref->Next;
|
|
}
|
|
if (ref == NIL)
|
|
return FALSE;
|
|
READ_UNLOCK(p->DBRWLock);
|
|
return Yap_unify(MkIntegerTerm(i),t);
|
|
}
|
|
|
|
Int
|
|
Yap_unify_immediate_ref(DBRef ref USES_REGS)
|
|
{
|
|
// old immediate semantics style
|
|
LOCK(ref->lock);
|
|
if (ref == NULL
|
|
|| DEAD_REF(ref)
|
|
|| !UnifyDBKey(ref,0,ARG1)
|
|
|| !UnifyDBNumber(ref,ARG2)) {
|
|
UNLOCK(ref->lock);
|
|
return FALSE;
|
|
} else {
|
|
UNLOCK(ref->lock);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static Term
|
|
GetDBTerm(DBTerm *DBSP, int src USES_REGS)
|
|
{
|
|
Term t = DBSP->Entry;
|
|
|
|
if (IsVarTerm(t)
|
|
#if COROUTINING
|
|
&& !DBSP->ag.attachments
|
|
#endif
|
|
) {
|
|
return MkVarTerm();
|
|
} else if (IsAtomOrIntTerm(t)) {
|
|
return t;
|
|
} else {
|
|
CELL *HOld = HR;
|
|
CELL *HeapPtr;
|
|
CELL *pt;
|
|
CELL NOf;
|
|
|
|
if (!(NOf = DBSP->NOfCells)) {
|
|
return t;
|
|
}
|
|
pt = CellPtr(DBSP->Contents);
|
|
CalculateStackGap( PASS_REGS1 );
|
|
if (HR+NOf > ASP-EventFlag/sizeof(CELL)) {
|
|
if (LOCAL_PrologMode & InErrorMode) {
|
|
if (HR+NOf > ASP)
|
|
fprintf(stderr, "\n\n [ FATAL ERROR: No Stack for Error Handling ]\n");
|
|
Yap_exit( 1);
|
|
} else {
|
|
LOCAL_Error_TYPE = OUT_OF_STACK_ERROR;
|
|
LOCAL_Error_Size = NOf*sizeof(CELL);
|
|
return (Term)0;
|
|
}
|
|
}
|
|
HeapPtr = cpcells(HOld, pt, NOf);
|
|
pt += HeapPtr - HOld;
|
|
HR = HeapPtr;
|
|
{
|
|
link_entry *lp = (link_entry *)pt;
|
|
linkblk(lp, HOld-1, (CELL)HOld-(CELL)(DBSP->Contents));
|
|
}
|
|
#ifdef COROUTINING
|
|
if (DBSP->ag.attachments != 0L && !src) {
|
|
if (!copy_attachments((CELL *)AdjustIDBPtr(DBSP->ag.attachments,(CELL)HOld-(CELL)(DBSP->Contents)) PASS_REGS)) {
|
|
HR = HOld;
|
|
LOCAL_Error_TYPE = OUT_OF_ATTVARS_ERROR;
|
|
LOCAL_Error_Size = 0;
|
|
return (Term)0;
|
|
}
|
|
}
|
|
#endif
|
|
return AdjustIDBPtr(t,Unsigned(HOld)-(CELL)(DBSP->Contents));
|
|
}
|
|
}
|
|
|
|
static Term
|
|
GetDBTermFromDBEntry(DBRef DBSP USES_REGS)
|
|
{
|
|
if (DBSP->Flags & (DBNoVars | DBAtomic))
|
|
return DBSP->DBT.Entry;
|
|
return GetDBTerm(&(DBSP->DBT), FALSE PASS_REGS);
|
|
}
|
|
|
|
static void
|
|
init_int_keys( void ) {
|
|
INT_KEYS = (Prop *)Yap_AllocCodeSpace(sizeof(Prop)*INT_KEYS_SIZE);
|
|
if (INT_KEYS != NULL) {
|
|
UInt i = 0;
|
|
Prop *p = INT_KEYS;
|
|
for (i = 0; i < INT_KEYS_SIZE; i++) {
|
|
p[0] = NIL;
|
|
p++;
|
|
}
|
|
Yap_LUClauseSpace += sizeof(Prop)*INT_KEYS_SIZE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
init_int_lu_keys( void ) {
|
|
INT_LU_KEYS = (Prop *)Yap_AllocCodeSpace(sizeof(Prop)*INT_KEYS_SIZE);
|
|
if (INT_LU_KEYS != NULL) {
|
|
UInt i = 0;
|
|
Prop *p = INT_LU_KEYS;
|
|
for (i = 0; i < INT_KEYS_SIZE; i++) {
|
|
p[0] = NULL;
|
|
p++;
|
|
}
|
|
Yap_LUClauseSpace += sizeof(Prop)*INT_KEYS_SIZE;
|
|
}
|
|
}
|
|
|
|
static int
|
|
resize_int_keys(UInt new_size) {
|
|
CACHE_REGS
|
|
Prop *new;
|
|
UInt i;
|
|
UInt old_size = INT_KEYS_SIZE;
|
|
|
|
YAPEnterCriticalSection();
|
|
if (INT_KEYS == NULL) {
|
|
INT_KEYS_SIZE = new_size;
|
|
YAPLeaveCriticalSection();
|
|
return TRUE;
|
|
}
|
|
new = (Prop *)Yap_AllocCodeSpace(sizeof(Prop)*new_size);
|
|
if (new == NULL) {
|
|
YAPLeaveCriticalSection();
|
|
LOCAL_Error_TYPE = OUT_OF_HEAP_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "could not allocate space";
|
|
return FALSE;
|
|
}
|
|
Yap_LUClauseSpace += sizeof(Prop)*new_size;
|
|
for (i = 0; i < new_size; i++) {
|
|
new[i] = NIL;
|
|
}
|
|
for (i = 0; i < INT_KEYS_SIZE; i++) {
|
|
if (INT_KEYS[i] != NIL) {
|
|
Prop p0 = INT_KEYS[i];
|
|
while (p0 != NIL) {
|
|
DBProp p = RepDBProp(p0);
|
|
CELL key = (CELL)(p->FunctorOfDB);
|
|
UInt hash_key = (CELL)key % new_size;
|
|
p0 = p->NextOfPE;
|
|
p->NextOfPE = new[hash_key];
|
|
new[hash_key] = AbsDBProp(p);
|
|
}
|
|
}
|
|
}
|
|
Yap_LUClauseSpace -= sizeof(Prop)*old_size;
|
|
Yap_FreeCodeSpace((char *)INT_KEYS);
|
|
INT_KEYS = new;
|
|
INT_KEYS_SIZE = new_size;
|
|
INT_KEYS_TIMESTAMP++;
|
|
if (INT_KEYS_TIMESTAMP == MAX_ABS_INT)
|
|
INT_KEYS_TIMESTAMP = 0;
|
|
YAPLeaveCriticalSection();
|
|
return TRUE;
|
|
}
|
|
|
|
static PredEntry *
|
|
find_lu_int_key(Int key)
|
|
{
|
|
UInt hash_key = (CELL)key % INT_KEYS_SIZE;
|
|
Prop p0;
|
|
|
|
if (INT_LU_KEYS != NULL) {
|
|
p0 = INT_LU_KEYS[hash_key];
|
|
while (p0) {
|
|
PredEntry *pe = RepPredProp(p0);
|
|
if (pe->src.IndxId == key) {
|
|
return pe;
|
|
}
|
|
p0 = pe->NextOfPE;
|
|
}
|
|
}
|
|
if (UPDATE_MODE == UPDATE_MODE_LOGICAL &&
|
|
find_int_key(key) == NULL) {
|
|
return new_lu_int_key(key);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PredEntry *
|
|
Yap_FindLUIntKey(Int key)
|
|
{
|
|
return find_lu_int_key(key);
|
|
}
|
|
|
|
static DBProp
|
|
find_int_key(Int key)
|
|
{
|
|
UInt hash_key = (CELL)key % INT_KEYS_SIZE;
|
|
Prop p0;
|
|
|
|
if (INT_KEYS == NULL) {
|
|
return NULL;
|
|
}
|
|
p0 = INT_KEYS[hash_key];
|
|
while (p0) {
|
|
DBProp p = RepDBProp(p0);
|
|
if (p->FunctorOfDB == (Functor)key)
|
|
return p;
|
|
p0 = p->NextOfPE;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static PredEntry *
|
|
new_lu_int_key(Int key)
|
|
{
|
|
UInt hash_key = (CELL)key % INT_KEYS_SIZE;
|
|
PredEntry *p;
|
|
Prop p0;
|
|
Atom ae;
|
|
|
|
if (INT_LU_KEYS == NULL) {
|
|
init_int_lu_keys();
|
|
if (INT_LU_KEYS == NULL) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = OUT_OF_HEAP_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "could not allocate space";
|
|
return NULL;
|
|
}
|
|
}
|
|
ae = AtomDInteger;
|
|
WRITE_LOCK(ae->ARWLock);
|
|
p0 = Yap_NewPredPropByAtom(ae,IDB_MODULE);
|
|
p = RepPredProp(p0);
|
|
p->NextOfPE = INT_LU_KEYS[hash_key];
|
|
p->src.IndxId = key;
|
|
p->PredFlags |= LogUpdatePredFlag|NumberDBPredFlag;
|
|
p->ArityOfPE = 3;
|
|
p->OpcodeOfPred = Yap_opcode(_op_fail);
|
|
p->cs.p_code.TrueCodeOfPred = p->CodeOfPred = FAILCODE;
|
|
INT_LU_KEYS[hash_key] = p0;
|
|
return p;
|
|
}
|
|
|
|
static PredEntry *
|
|
new_lu_entry(Term t)
|
|
{
|
|
Prop p0;
|
|
PredEntry *pe;
|
|
|
|
if (IsApplTerm(t)) {
|
|
Functor f = FunctorOfTerm(t);
|
|
|
|
FUNC_WRITE_LOCK(f);
|
|
p0 = Yap_NewPredPropByFunctor(f,IDB_MODULE);
|
|
} else if (IsAtomTerm(t)) {
|
|
Atom at = AtomOfTerm(t);
|
|
|
|
WRITE_LOCK(RepAtom(at)->ARWLock);
|
|
p0 = Yap_NewPredPropByAtom(at,IDB_MODULE);
|
|
} else {
|
|
FUNC_WRITE_LOCK(FunctorList);
|
|
p0 = Yap_NewPredPropByFunctor(FunctorList,IDB_MODULE);
|
|
}
|
|
pe = RepPredProp(p0);
|
|
pe->PredFlags |= LogUpdatePredFlag;
|
|
if (IsAtomTerm(t)) {
|
|
pe->PredFlags |= AtomDBPredFlag;
|
|
}
|
|
pe->ArityOfPE = 3;
|
|
pe->OpcodeOfPred = Yap_opcode(_op_fail);
|
|
pe->cs.p_code.TrueCodeOfPred = pe->CodeOfPred = FAILCODE;
|
|
return pe;
|
|
}
|
|
|
|
static DBProp
|
|
find_entry(Term t)
|
|
{
|
|
Atom at;
|
|
UInt arity;
|
|
|
|
if (IsVarTerm(t)) {
|
|
return RepDBProp(NIL);
|
|
} else if (IsAtomTerm(t)) {
|
|
at = AtomOfTerm(t);
|
|
arity = 0;
|
|
|
|
} else if (IsIntegerTerm(t)) {
|
|
return find_int_key(IntegerOfTerm(t));
|
|
} else if (IsApplTerm(t)) {
|
|
Functor f = FunctorOfTerm(t);
|
|
|
|
at = NameOfFunctor(f);
|
|
arity = ArityOfFunctor(f);
|
|
} else {
|
|
at = AtomDot;
|
|
arity = 2;
|
|
}
|
|
return RepDBProp(FindDBProp(RepAtom(at), 0, arity, 0));
|
|
}
|
|
|
|
static PredEntry *
|
|
find_lu_entry(Term t)
|
|
{
|
|
Prop p;
|
|
|
|
if (IsVarTerm(t)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t, "while accessing database key");
|
|
return NULL;
|
|
}
|
|
if (IsIntegerTerm(t)) {
|
|
return find_lu_int_key(IntegerOfTerm(t));
|
|
} else if (IsApplTerm(t)) {
|
|
Functor f = FunctorOfTerm(t);
|
|
|
|
if (IsExtensionFunctor(f)) {
|
|
Yap_Error(TYPE_ERROR_KEY, t, "while accessing database key");
|
|
return NULL;
|
|
}
|
|
p = Yap_GetPredPropByFuncInThisModule(FunctorOfTerm(t),IDB_MODULE);
|
|
} else if (IsAtomTerm(t)) {
|
|
p = Yap_GetPredPropByAtomInThisModule(AtomOfTerm(t),IDB_MODULE);
|
|
} else {
|
|
p = Yap_GetPredPropByFuncInThisModule(FunctorList,IDB_MODULE);
|
|
}
|
|
if (p == NIL) {
|
|
if (UPDATE_MODE == UPDATE_MODE_LOGICAL && !find_entry(t)) {
|
|
return new_lu_entry(t);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
return RepPredProp(p);
|
|
}
|
|
|
|
|
|
static DBProp
|
|
FetchIntDBPropFromKey(Int key, int flag, int new, char *error_mssg)
|
|
{
|
|
Functor fun = (Functor)key;
|
|
UInt hash_key = (CELL)key % INT_KEYS_SIZE;
|
|
Prop p0;
|
|
|
|
if (INT_KEYS == NULL) {
|
|
init_int_keys();
|
|
if (INT_KEYS == NULL) {
|
|
CACHE_REGS
|
|
LOCAL_Error_TYPE = OUT_OF_HEAP_ERROR;
|
|
LOCAL_Error_Term = TermNil;
|
|
LOCAL_ErrorMessage = "could not allocate space";
|
|
return NULL;
|
|
}
|
|
}
|
|
p0 = INT_KEYS[hash_key];
|
|
while (p0 != NIL) {
|
|
DBProp p = RepDBProp(p0);
|
|
if (p->FunctorOfDB == fun)
|
|
return p;
|
|
p0 = p->NextOfPE;
|
|
}
|
|
/* p is NULL, meaning we did not find the functor */
|
|
if (new) {
|
|
DBProp p;
|
|
/* create a new DBProp */
|
|
p = (DBProp) Yap_AllocAtomSpace(sizeof(*p));
|
|
p->KindOfPE = DBProperty|flag;
|
|
p->F0 = p->L0 = NULL;
|
|
p->ArityOfDB = 0;
|
|
p->First = p->Last = NULL;
|
|
p->ModuleOfDB = 0;
|
|
p->FunctorOfDB = fun;
|
|
p->NextOfPE = INT_KEYS[hash_key];
|
|
INIT_RWLOCK(p->DBRWLock);
|
|
INT_KEYS[hash_key] = AbsDBProp(p);
|
|
return p;
|
|
} else {
|
|
return
|
|
RepDBProp(NULL);
|
|
}
|
|
}
|
|
|
|
static DBProp
|
|
FetchDBPropFromKey(Term twork, int flag, int new, char *error_mssg)
|
|
{
|
|
Atom At;
|
|
Int arity;
|
|
Term dbmod;
|
|
|
|
if (flag & MkCode) {
|
|
if (IsVarTerm(twork)) {
|
|
Yap_Error(INSTANTIATION_ERROR, twork, error_mssg);
|
|
return RepDBProp(NULL);
|
|
}
|
|
if (!IsApplTerm(twork)) {
|
|
Yap_Error(SYSTEM_ERROR, twork, "missing module");
|
|
return RepDBProp(NULL);
|
|
} else {
|
|
Functor f = FunctorOfTerm(twork);
|
|
if (f != FunctorModule) {
|
|
Yap_Error(SYSTEM_ERROR, twork, "missing module");
|
|
return RepDBProp(NULL);
|
|
}
|
|
dbmod = ArgOfTerm(1, twork);
|
|
if (IsVarTerm(dbmod)) {
|
|
Yap_Error(INSTANTIATION_ERROR, twork, "var in module");
|
|
return
|
|
RepDBProp(NIL);
|
|
}
|
|
if (!IsAtomTerm(dbmod)) {
|
|
Yap_Error(TYPE_ERROR_ATOM, twork, "not atom in module");
|
|
return
|
|
RepDBProp(NIL);
|
|
}
|
|
twork = ArgOfTerm(2, twork);
|
|
}
|
|
} else {
|
|
dbmod = 0;
|
|
|
|
}
|
|
if (IsVarTerm(twork)) {
|
|
Yap_Error(INSTANTIATION_ERROR, twork, error_mssg);
|
|
return
|
|
RepDBProp(NIL);
|
|
} else if (IsAtomTerm(twork)) {
|
|
arity = 0, At = AtomOfTerm(twork);
|
|
} else if (IsIntegerTerm(twork)) {
|
|
return
|
|
FetchIntDBPropFromKey(IntegerOfTerm(twork), flag, new, error_mssg);
|
|
} else if (IsApplTerm(twork)) {
|
|
Register Functor f = FunctorOfTerm(twork);
|
|
if (IsExtensionFunctor(f)) {
|
|
Yap_Error(TYPE_ERROR_KEY, twork, error_mssg);
|
|
return
|
|
RepDBProp(NIL);
|
|
}
|
|
At = NameOfFunctor(f);
|
|
arity = ArityOfFunctor(f);
|
|
} else if (IsPairTerm(twork)) {
|
|
At = AtomDot;
|
|
arity = 2;
|
|
} else {
|
|
Yap_Error(TYPE_ERROR_KEY, twork,error_mssg);
|
|
return
|
|
RepDBProp(NIL);
|
|
}
|
|
if (new) {
|
|
DBProp p;
|
|
AtomEntry *ae = RepAtom(At);
|
|
|
|
WRITE_LOCK(ae->ARWLock);
|
|
if (EndOfPAEntr(p = RepDBProp(FindDBPropHavingLock(ae, flag, arity, dbmod)))) {
|
|
/* create a new DBProp */
|
|
int OLD_UPDATE_MODE = UPDATE_MODE;
|
|
if (flag & MkCode) {
|
|
PredEntry *pp;
|
|
pp = RepPredProp(Yap_GetPredPropHavingLock(At, arity, dbmod));
|
|
|
|
if (!EndOfPAEntr(pp)) {
|
|
PELOCK(64,pp);
|
|
if(pp->PredFlags & LogUpdatePredFlag)
|
|
UPDATE_MODE = UPDATE_MODE_LOGICAL;
|
|
UNLOCK(pp->PELock);
|
|
}
|
|
|
|
}
|
|
p = (DBProp) Yap_AllocAtomSpace(sizeof(*p));
|
|
p->KindOfPE = DBProperty|flag;
|
|
p->F0 = p->L0 = NULL;
|
|
UPDATE_MODE = OLD_UPDATE_MODE;
|
|
p->ArityOfDB = arity;
|
|
p->First = p->Last = NIL;
|
|
p->ModuleOfDB = dbmod;
|
|
/* This is NOT standard but is QUITE convenient */
|
|
INIT_RWLOCK(p->DBRWLock);
|
|
if (arity == 0)
|
|
p->FunctorOfDB = (Functor) At;
|
|
else
|
|
p->FunctorOfDB = Yap_UnlockedMkFunctor(ae,arity);
|
|
AddPropToAtom(ae, (PropEntry *)p);
|
|
}
|
|
WRITE_UNLOCK(ae->ARWLock);
|
|
return
|
|
p;
|
|
} else
|
|
return
|
|
RepDBProp(FindDBProp(RepAtom(At), flag, arity, dbmod));
|
|
}
|
|
|
|
|
|
static Int
|
|
lu_nth_recorded(PredEntry *pe, Int Count USES_REGS)
|
|
{
|
|
LogUpdClause *cl;
|
|
|
|
XREGS[2] = MkVarTerm();
|
|
cl = Yap_NthClause(pe, Count);
|
|
if (cl == NULL)
|
|
return FALSE;
|
|
#if MULTIPLE_STACKS
|
|
TRAIL_CLREF(cl); /* So that fail will erase it */
|
|
INC_CLREF_COUNT(cl);
|
|
#else
|
|
if (!(cl->ClFlags & InUseMask)) {
|
|
cl->ClFlags |= InUseMask;
|
|
TRAIL_CLREF(cl); /* So that fail will erase it */
|
|
}
|
|
#endif
|
|
UNLOCK(pe->PELock);
|
|
return Yap_unify(MkDBRefTerm((DBRef)cl),ARG4);
|
|
}
|
|
|
|
|
|
/* Finds a term recorded under the key ARG1 */
|
|
static Int
|
|
nth_recorded(DBProp AtProp, Int Count USES_REGS)
|
|
{
|
|
Register DBRef ref;
|
|
|
|
READ_LOCK(AtProp->DBRWLock);
|
|
ref = AtProp->First;
|
|
Count--;
|
|
while (ref != NULL
|
|
&& DEAD_REF(ref))
|
|
ref = NextDBRef(ref);
|
|
if (ref == NULL) {
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
return FALSE;
|
|
}
|
|
while (Count) {
|
|
Count--;
|
|
ref = NextDBRef(ref);
|
|
while (ref != NULL
|
|
&& DEAD_REF(ref))
|
|
ref = NextDBRef(ref);
|
|
if (ref == NULL) {
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
return FALSE;
|
|
}
|
|
}
|
|
#if MULTIPLE_STACKS
|
|
LOCK(ref->lock);
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
INC_DBREF_COUNT(ref);
|
|
UNLOCK(ref->lock);
|
|
#else
|
|
if (!(ref->Flags & InUseMask)) {
|
|
ref->Flags |= InUseMask;
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
}
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
#endif
|
|
return Yap_unify(MkDBRefTerm(ref),ARG4);
|
|
}
|
|
|
|
Int
|
|
Yap_db_nth_recorded( PredEntry *pe, Int Count USES_REGS )
|
|
{
|
|
DBProp AtProp;
|
|
|
|
if (pe == NULL) {
|
|
return lu_nth_recorded(pe,Count PASS_REGS);
|
|
}
|
|
if (EndOfPAEntr(AtProp = FetchDBPropFromKey(Deref(ARG1), 0, FALSE, "nth_instance/3"))) {
|
|
UNLOCK(pe->PELock);
|
|
return FALSE;
|
|
}
|
|
return nth_recorded(AtProp, Count PASS_REGS);
|
|
}
|
|
|
|
static Int
|
|
p_db_key( USES_REGS1 )
|
|
{
|
|
Register Term twork = Deref(ARG1); /* fetch the key */
|
|
DBProp AtProp;
|
|
|
|
if (EndOfPAEntr(AtProp = FetchDBPropFromKey(twork, 0, TRUE, "db_key/3"))) {
|
|
/* should never happen */
|
|
return FALSE;
|
|
}
|
|
return Yap_unify(ARG2,MkIntegerTerm((Int)AtProp));
|
|
}
|
|
|
|
/* Finds a term recorded under the key ARG1 */
|
|
static Int
|
|
i_recorded(DBProp AtProp, Term t3 USES_REGS)
|
|
{
|
|
Term TermDB, TRef;
|
|
Register DBRef ref;
|
|
Term twork;
|
|
|
|
READ_LOCK(AtProp->DBRWLock);
|
|
ref = AtProp->First;
|
|
while (ref != NULL
|
|
&& DEAD_REF(ref))
|
|
ref = NextDBRef(ref);
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
if (ref == NULL) {
|
|
cut_fail();
|
|
}
|
|
twork = Deref(ARG2); /* now working with ARG2 */
|
|
if (IsVarTerm(twork)) {
|
|
EXTRA_CBACK_ARG(3,2) = MkIntegerTerm(0);
|
|
EXTRA_CBACK_ARG(3,3) = MkIntegerTerm(0);
|
|
B->cp_h = HR;
|
|
while ((TermDB = GetDBTermFromDBEntry(ref PASS_REGS)) == (CELL)0) {
|
|
/* make sure the garbage collector sees what we want it to see! */
|
|
EXTRA_CBACK_ARG(3,1) = (CELL)ref;
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 3, ENV, CP)) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
}
|
|
LOCAL_Error_Size = 0;
|
|
twork = Deref(ARG2);
|
|
t3 = Deref(ARG3);
|
|
}
|
|
if (!Yap_unify(twork, TermDB)) {
|
|
cut_fail();
|
|
}
|
|
} else if (IsAtomOrIntTerm(twork)) {
|
|
EXTRA_CBACK_ARG(3,2) = MkIntegerTerm(0);
|
|
EXTRA_CBACK_ARG(3,3) = MkIntegerTerm((Int)twork);
|
|
B->cp_h = HR;
|
|
READ_LOCK(AtProp->DBRWLock);
|
|
do {
|
|
if (((twork == ref->DBT.Entry) || IsVarTerm(ref->DBT.Entry)) &&
|
|
!DEAD_REF(ref))
|
|
break;
|
|
ref = NextDBRef(ref);
|
|
if (ref == NIL) {
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
} while (TRUE);
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
} else {
|
|
CELL key;
|
|
CELL mask = EvalMasks(twork, &key);
|
|
|
|
B->cp_h = HR;
|
|
READ_LOCK(AtProp->DBRWLock);
|
|
do {
|
|
while ((mask & ref->Key) != (key & ref->Mask) && !DEAD_REF(ref)) {
|
|
ref = NextDBRef(ref);
|
|
if (ref == NULL) {
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
}
|
|
if ((TermDB = GetDBTermFromDBEntry(ref PASS_REGS)) != (CELL)0) {
|
|
if (Yap_unify(TermDB, ARG2)) {
|
|
/* success */
|
|
EXTRA_CBACK_ARG(3,2) = MkIntegerTerm(((Int)mask));
|
|
EXTRA_CBACK_ARG(3,3) = MkIntegerTerm(((Int)key));
|
|
B->cp_h = HR;
|
|
break;
|
|
} else {
|
|
while ((ref = NextDBRef(ref)) != NULL
|
|
&& DEAD_REF(ref));
|
|
if (ref == NULL) {
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
}
|
|
} else {
|
|
/* make sure the garbage collector sees what we want it to see! */
|
|
EXTRA_CBACK_ARG(3,1) = (CELL)ref;
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
EXTRA_CBACK_ARG(3,2) = MkIntegerTerm(((Int)mask));
|
|
EXTRA_CBACK_ARG(3,3) = MkIntegerTerm(((Int)key));
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 3, ENV, CP)) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
}
|
|
READ_LOCK(AtProp->DBRWLock);
|
|
}
|
|
} while (TRUE);
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
}
|
|
EXTRA_CBACK_ARG(3,1) = (CELL)ref;
|
|
/* This should be after any non-tagged terms, because the routines in grow.c
|
|
go from upper to lower addresses */
|
|
TRef = MkDBRefTerm(ref);
|
|
#if MULTIPLE_STACKS
|
|
LOCK(ref->lock);
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
INC_DBREF_COUNT(ref);
|
|
UNLOCK(ref->lock);
|
|
#else
|
|
if (!(ref->Flags & InUseMask)) {
|
|
ref->Flags |= InUseMask;
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
}
|
|
#endif
|
|
return (Yap_unify(ARG3, TRef));
|
|
}
|
|
|
|
static Int
|
|
c_recorded(int flags USES_REGS)
|
|
{
|
|
Term TermDB, TRef;
|
|
Register DBRef ref, ref0;
|
|
CELL *PreviousHeap = HR;
|
|
CELL mask, key;
|
|
Term t1;
|
|
|
|
t1 = EXTRA_CBACK_ARG(3,1);
|
|
ref0 = (DBRef)t1;
|
|
READ_LOCK(ref0->Parent->DBRWLock);
|
|
ref = NextDBRef(ref0);
|
|
if (ref == NIL) {
|
|
if (ref0->Flags & ErasedMask) {
|
|
ref = ref0;
|
|
while ((ref = ref->n) != NULL) {
|
|
if (!(ref->Flags & ErasedMask))
|
|
break;
|
|
}
|
|
/* we have used the DB entry, so we can remove it now, although
|
|
first we have to make sure noone is pointing to it */
|
|
if (ref == NULL) {
|
|
READ_UNLOCK(ref0->Parent->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
READ_UNLOCK(ref0->Parent->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
}
|
|
|
|
{
|
|
Term ttmp = EXTRA_CBACK_ARG(3,2);
|
|
if (IsLongIntTerm(ttmp))
|
|
mask = (CELL)LongIntOfTerm(ttmp);
|
|
else
|
|
mask = (CELL)IntOfTerm(ttmp);
|
|
}
|
|
{
|
|
Term ttmp = EXTRA_CBACK_ARG(3,3);
|
|
if (IsLongIntTerm(ttmp))
|
|
key = (CELL)LongIntOfTerm(ttmp);
|
|
else
|
|
key = (CELL)IntOfTerm(ttmp);
|
|
}
|
|
while (ref != NIL
|
|
&& DEAD_REF(ref))
|
|
ref = NextDBRef(ref);
|
|
if (ref == NIL) {
|
|
READ_UNLOCK(ref0->Parent->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
if (mask == 0 && key == 0) { /* ARG2 is a variable */
|
|
while ((TermDB = GetDBTermFromDBEntry(ref PASS_REGS)) == (CELL)0) {
|
|
/* make sure the garbage collector sees what we want it to see! */
|
|
EXTRA_CBACK_ARG(3,1) = (CELL)ref;
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 3, ENV, CP)) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
}
|
|
LOCAL_Error_Size = 0;
|
|
PreviousHeap = HR;
|
|
}
|
|
Yap_unify(ARG2, TermDB);
|
|
} else if (mask == 0) { /* ARG2 is a constant */
|
|
do {
|
|
if (((key == Unsigned(ref->DBT.Entry)) || (ref->Flags & DBVar)) &&
|
|
!DEAD_REF(ref))
|
|
break;
|
|
ref = NextDBRef(ref);
|
|
} while (ref != NIL);
|
|
if (ref == NIL) {
|
|
READ_UNLOCK(ref0->Parent->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
} else
|
|
do { /* ARG2 is a structure */
|
|
HR = PreviousHeap;
|
|
while ((mask & ref->Key) != (key & ref->Mask)) {
|
|
while ((ref = NextDBRef(ref)) != NIL
|
|
&& DEAD_REF(ref));
|
|
if (ref == NIL) {
|
|
READ_UNLOCK(ref0->Parent->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
}
|
|
while ((TermDB = GetDBTermFromDBEntry(ref PASS_REGS)) == (CELL)0) {
|
|
/* make sure the garbage collector sees what we want it to see! */
|
|
EXTRA_CBACK_ARG(3,1) = (CELL)ref;
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 3, ENV, CP)) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
}
|
|
LOCAL_Error_Size = 0;
|
|
PreviousHeap = HR;
|
|
}
|
|
if (Yap_unify(ARG2, TermDB))
|
|
break;
|
|
while ((ref = NextDBRef(ref)) != NIL
|
|
&& DEAD_REF(ref));
|
|
if (ref == NIL) {
|
|
READ_UNLOCK(ref0->Parent->DBRWLock);
|
|
cut_fail();
|
|
}
|
|
} while (1);
|
|
READ_UNLOCK(ref0->Parent->DBRWLock);
|
|
TRef = MkDBRefTerm(ref);
|
|
EXTRA_CBACK_ARG(3,1) = (CELL)ref;
|
|
#if MULTIPLE_STACKS
|
|
LOCK(ref->lock);
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
INC_DBREF_COUNT(ref);
|
|
UNLOCK(ref->lock);
|
|
#else
|
|
if (!(ref->Flags & InUseMask)) {
|
|
ref->Flags |= InUseMask;
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
}
|
|
#endif
|
|
return (Yap_unify(ARG3, TRef));
|
|
}
|
|
|
|
/*
|
|
* The arguments for this 4 functions are the flags for terms which should be
|
|
* skipped
|
|
*/
|
|
|
|
static Int
|
|
lu_recorded(PredEntry *pe USES_REGS) {
|
|
op_numbers opc = Yap_op_from_opcode(P->opc);
|
|
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
PELOCK(66,pe);
|
|
PP = pe;
|
|
#endif
|
|
if (opc == _procceed) {
|
|
P = pe->CodeOfPred;
|
|
} else {
|
|
if (P->opc != Yap_opcode(_execute_cpred)) {
|
|
CP = P;
|
|
ENV = YENV;
|
|
YENV = ASP;
|
|
YENV[E_CB] = (CELL) B;
|
|
}
|
|
P = pe->CodeOfPred;
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
/* avoid holding a lock if we don't have anything in the database */
|
|
if (P == FAILCODE) {
|
|
UNLOCK(pe->PELock);
|
|
PP = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
if (pe->PredFlags & ProfiledPredFlag) {
|
|
LOCK(pe->StatisticsForPred.lock);
|
|
pe->StatisticsForPred.NOfEntries++;
|
|
UNLOCK(pe->StatisticsForPred.lock);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* recorded(+Functor,+Term,-Ref) */
|
|
static Int
|
|
in_rded_with_key( USES_REGS1 )
|
|
{
|
|
DBProp AtProp = (DBProp)IntegerOfTerm(Deref(ARG1));
|
|
|
|
return (i_recorded(AtProp,Deref(ARG3) PASS_REGS));
|
|
}
|
|
|
|
/* recorded(+Functor,+Term,-Ref) */
|
|
static Int
|
|
p_recorded( USES_REGS1 )
|
|
{
|
|
DBProp AtProp;
|
|
Register Term twork = Deref(ARG1); /* initially working with
|
|
* ARG1 */
|
|
Term t3 = Deref(ARG3);
|
|
PredEntry *pe;
|
|
|
|
if (!IsVarTerm(t3)) {
|
|
DBRef ref = DBRefOfTerm(t3);
|
|
if (!IsDBRefTerm(t3)) {
|
|
return FALSE;
|
|
} else {
|
|
ref = DBRefOfTerm(t3);
|
|
}
|
|
ref = DBRefOfTerm(t3);
|
|
if (ref == NULL) return FALSE;
|
|
if (DEAD_REF(ref)) {
|
|
return FALSE;
|
|
}
|
|
if (ref->Flags & LogUpdMask) {
|
|
LogUpdClause *cl = (LogUpdClause *)ref;
|
|
PredEntry *ap = cl->ClPred;
|
|
op_numbers opc = Yap_op_from_opcode(P->opc);
|
|
|
|
if (!Yap_unify(GetDBLUKey(ap), ARG1))
|
|
return FALSE;
|
|
|
|
if (opc == _procceed) {
|
|
P = cl->ClCode;
|
|
} else {
|
|
CP = P;
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
PP = cl->ClPred;
|
|
#endif
|
|
P = cl->ClCode;
|
|
ENV = YENV;
|
|
YENV = ASP;
|
|
YENV[E_CB] = (CELL) B;
|
|
}
|
|
return TRUE;
|
|
} else {
|
|
Term TermDB;
|
|
while ((TermDB = GetDBTermFromDBEntry(ref PASS_REGS)) == (CELL)0) {
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 3, ENV, gc_P(P,CP))) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
if (!Yap_unify(ARG2,TermDB)
|
|
|| !UnifyDBKey(ref,0,ARG1)) {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
if ((pe = find_lu_entry(twork)) != NULL) {
|
|
return lu_recorded(pe PASS_REGS);
|
|
}
|
|
if (EndOfPAEntr(AtProp = FetchDBPropFromKey(twork, 0, FALSE, "recorded/3"))) {
|
|
return FALSE;
|
|
}
|
|
ARG1 = MkIntegerTerm((Int)AtProp);
|
|
P = PredRecordedWithKey->CodeOfPred;
|
|
return (i_recorded(AtProp, t3 PASS_REGS));
|
|
}
|
|
|
|
static Int
|
|
co_rded( USES_REGS1 )
|
|
{
|
|
return (c_recorded(0 PASS_REGS));
|
|
}
|
|
|
|
/* '$recordedp'(+Functor,+Term,-Ref) */
|
|
static Int
|
|
in_rdedp( USES_REGS1 )
|
|
{
|
|
DBProp AtProp;
|
|
register choiceptr b0=B;
|
|
Register Term twork = Deref(ARG1); /* initially working with
|
|
* ARG1 */
|
|
|
|
Term t3 = Deref(ARG3);
|
|
if (!IsVarTerm(t3)) {
|
|
if (!IsDBRefTerm(t3)) {
|
|
cut_fail();
|
|
} else {
|
|
DBRef ref = DBRefOfTerm(t3);
|
|
LOCK(ref->lock);
|
|
if (ref == NULL
|
|
|| DEAD_REF(ref)
|
|
|| !Yap_unify(ARG2,GetDBTermFromDBEntry(ref PASS_REGS))
|
|
|| !UnifyDBKey(ref,CodeDBBit,ARG1)) {
|
|
UNLOCK(ref->lock);
|
|
cut_fail();
|
|
} else {
|
|
UNLOCK(ref->lock);
|
|
cut_succeed();
|
|
}
|
|
}
|
|
}
|
|
if (EndOfPAEntr(AtProp = FetchDBPropFromKey(twork, MkCode, FALSE, "recorded/3"))) {
|
|
if (b0 == B)
|
|
cut_fail();
|
|
else
|
|
return FALSE;
|
|
}
|
|
return (i_recorded(AtProp,t3 PASS_REGS));
|
|
}
|
|
|
|
|
|
static Int
|
|
co_rdedp( USES_REGS1 )
|
|
{
|
|
return (c_recorded(MkCode PASS_REGS));
|
|
}
|
|
|
|
/* '$some_recordedp'(Functor) */
|
|
static Int
|
|
p_somercdedp( USES_REGS1 )
|
|
{
|
|
Register DBRef ref;
|
|
DBProp AtProp;
|
|
Register Term twork = Deref(ARG1); /* initially working with
|
|
* ARG1 */
|
|
if (EndOfPAEntr(AtProp = FetchDBPropFromKey(twork, MkCode, FALSE, "some_recorded/3"))) {
|
|
return FALSE;
|
|
}
|
|
READ_LOCK(AtProp->DBRWLock);
|
|
ref = FrstDBRef(AtProp);
|
|
while (ref != NIL && (ref->Flags & (DBNoCode | ErasedMask)))
|
|
ref = NextDBRef(ref);
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
if (ref == NIL)
|
|
return (FALSE);
|
|
else
|
|
return (TRUE);
|
|
}
|
|
|
|
/* Finds the first instance recorded under key ARG1 */
|
|
static Int
|
|
p_first_instance( USES_REGS1 )
|
|
{
|
|
Term TRef;
|
|
Register DBRef ref;
|
|
DBProp AtProp;
|
|
Register Term twork = Deref(ARG1); /* initially working with
|
|
* ARG1 */
|
|
Term TermDB;
|
|
|
|
ARG3 = Deref(ARG3);
|
|
if (!IsVarTerm(ARG3)) {
|
|
cut_fail();
|
|
}
|
|
if (EndOfPAEntr(AtProp = FetchDBPropFromKey(twork, 0, FALSE, "first_instance/3"))) {
|
|
return FALSE;
|
|
}
|
|
READ_LOCK(AtProp->DBRWLock);
|
|
ref = AtProp->First;
|
|
while (ref != NIL
|
|
&& (ref->Flags & (DBCode | ErasedMask)))
|
|
ref = NextDBRef(ref);
|
|
READ_UNLOCK(AtProp->DBRWLock);
|
|
if (ref == NIL) {
|
|
cut_fail();
|
|
}
|
|
TRef = MkDBRefTerm(ref);
|
|
/* we have a pointer to the term available */
|
|
LOCK(ref->lock);
|
|
#if MULTIPLE_STACKS
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
INC_DBREF_COUNT(ref);
|
|
#else
|
|
if (!(ref->Flags & InUseMask)) {
|
|
ref->Flags |= InUseMask;
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
}
|
|
#endif
|
|
UNLOCK(ref->lock);
|
|
while ((TermDB = GetDBTermFromDBEntry(ref PASS_REGS)) == (CELL)0) {
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 3, ENV, gc_P(P,CP))) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
if (IsVarTerm(TermDB)) {
|
|
Yap_unify(TermDB, ARG2);
|
|
} else {
|
|
return Yap_unify(ARG2, TermDB);
|
|
}
|
|
return Yap_unify(ARG3, TRef);
|
|
}
|
|
|
|
static UInt
|
|
index_sz(LogUpdIndex *x)
|
|
{
|
|
UInt sz = x->ClSize;
|
|
yamop *start = x->ClCode;
|
|
op_numbers op = Yap_op_from_opcode(start->opc);
|
|
|
|
/* add try-retry-trust children */
|
|
while (op == _jump_if_nonvar) {
|
|
start = NEXTOP(start, xll);
|
|
op = Yap_op_from_opcode(start->opc);
|
|
}
|
|
if (op == _enter_lu_pred) {
|
|
PredEntry *ap = x->ClPred;
|
|
OPCODE endop, op1;
|
|
UInt count = 0, dead=0;
|
|
|
|
if (ap->PredFlags & CountPredFlag)
|
|
endop = Yap_opcode(_count_trust_logical);
|
|
else if (ap->PredFlags & ProfiledPredFlag)
|
|
endop = Yap_opcode(_profiled_trust_logical);
|
|
else
|
|
endop = Yap_opcode(_trust_logical);
|
|
start = start->y_u.Illss.l1;
|
|
if (start->y_u.Illss.s) do {
|
|
sz += (UInt)NEXTOP((yamop*)NULL,OtaLl);
|
|
op1 = start->opc;
|
|
count++;
|
|
if (start->y_u.OtaLl.d->ClFlags & ErasedMask)
|
|
dead++;
|
|
start = start->y_u.OtaLl.n;
|
|
} while (op1 != endop);
|
|
}
|
|
x = x->ChildIndex;
|
|
while (x != NULL) {
|
|
sz += index_sz(x);
|
|
x = x->SiblingIndex;
|
|
}
|
|
return sz;
|
|
}
|
|
|
|
static Int
|
|
lu_statistics(PredEntry *pe USES_REGS)
|
|
{
|
|
UInt sz = sizeof(PredEntry), cls = 0, isz = 0;
|
|
|
|
/* count number of clauses and size */
|
|
LogUpdClause *x;
|
|
|
|
if (pe->cs.p_code.FirstClause == NULL) {
|
|
cls = 0;
|
|
sz = 0;
|
|
} else {
|
|
x = ClauseCodeToLogUpdClause(pe->cs.p_code.FirstClause);
|
|
while (x != NULL) {
|
|
cls++;
|
|
sz += x->ClSize;
|
|
x = x->ClNext;
|
|
}
|
|
}
|
|
isz = 0;
|
|
if (pe->PredFlags & IndexedPredFlag) {
|
|
/* expand clause blocks */
|
|
yamop *ep = ExpandClausesFirst;
|
|
while (ep) {
|
|
if (ep->y_u.sssllp.p == pe)
|
|
isz += (UInt)NEXTOP((yamop *)NULL,sssllp)+ep->y_u.sssllp.s1*sizeof(yamop *);
|
|
ep = ep->y_u.sssllp.snext;
|
|
}
|
|
isz += index_sz(ClauseCodeToLogUpdIndex(pe->cs.p_code.TrueCodeOfPred));
|
|
}
|
|
return
|
|
Yap_unify(ARG2,MkIntegerTerm(cls)) &&
|
|
Yap_unify(ARG3,MkIntegerTerm(sz)) &&
|
|
Yap_unify(ARG4,MkIntegerTerm(isz));
|
|
}
|
|
|
|
/** @pred key_statistics(+ _K_,- _Entries_,- _Size_,- _IndexSize_)
|
|
|
|
|
|
Returns several statistics for a key _K_. Currently, it says how
|
|
many entries we have for that key, _Entries_, what is the
|
|
total size spent on entries, _Size_, and what is the amount of
|
|
space spent in indices.
|
|
|
|
|
|
*/
|
|
static Int
|
|
p_key_statistics( USES_REGS1 )
|
|
{
|
|
Register DBProp p;
|
|
Register DBRef x;
|
|
UInt sz = 0, cls = 0;
|
|
Term twork = Deref(ARG1);
|
|
PredEntry *pe;
|
|
|
|
if ((pe = find_lu_entry(twork)) != NULL) {
|
|
return lu_statistics(pe PASS_REGS);
|
|
}
|
|
if (EndOfPAEntr(p = FetchDBPropFromKey(twork, 0, TRUE, "key_statistics/4"))) {
|
|
/* This is not a key property */
|
|
return FALSE;
|
|
}
|
|
/* count number of clauses and size */
|
|
x = p->First;
|
|
while (x != NULL) {
|
|
cls++;
|
|
sz += sizeof(DBStruct)+sizeof(CELL)*x->DBT.NOfCells;
|
|
if (x->Code) {
|
|
DynamicClause *cl = ClauseCodeToDynamicClause(x->Code);
|
|
sz += cl->ClSize;
|
|
}
|
|
x = NextDBRef(x);
|
|
}
|
|
return
|
|
Yap_unify(ARG2,MkIntegerTerm(cls)) &&
|
|
Yap_unify(ARG3,MkIntegerTerm(sz)) &&
|
|
Yap_unify(ARG4,MkIntTerm(0));
|
|
}
|
|
|
|
static Int
|
|
p_lu_statistics( USES_REGS1 )
|
|
{
|
|
Term t = Deref(ARG1);
|
|
Term mod = Deref(ARG5);
|
|
PredEntry *pe;
|
|
if (IsVarTerm(t)) {
|
|
return (FALSE);
|
|
} else if (IsAtomTerm(t)) {
|
|
Atom at = AtomOfTerm(t);
|
|
pe = RepPredProp(Yap_GetPredPropByAtom(at, mod));
|
|
} else if (IsIntegerTerm(t) && mod == IDB_MODULE) {
|
|
pe = find_lu_int_key(IntegerOfTerm(t));
|
|
} else if (IsApplTerm(t)) {
|
|
Functor fun = FunctorOfTerm(t);
|
|
pe = RepPredProp(Yap_GetPredPropByFunc(fun, mod));
|
|
} else
|
|
return FALSE;
|
|
if (pe == NIL)
|
|
return FALSE;
|
|
if (!(pe->PredFlags & LogUpdatePredFlag)) {
|
|
/* should use '$recordedp' in this case */
|
|
return FALSE;
|
|
}
|
|
return lu_statistics(pe PASS_REGS);
|
|
}
|
|
|
|
|
|
static Int
|
|
p_total_erased( USES_REGS1 )
|
|
{
|
|
UInt sz = 0, cls = 0;
|
|
UInt isz = 0, icls = 0;
|
|
LogUpdClause *cl = DBErasedList;
|
|
LogUpdIndex *icl = DBErasedIList;
|
|
|
|
/* only for log upds */
|
|
while (cl) {
|
|
cls++;
|
|
sz += cl->ClSize;
|
|
cl = cl->ClNext;
|
|
}
|
|
while (icl) {
|
|
icls++;
|
|
isz += icl->ClSize;
|
|
icl = icl->SiblingIndex;
|
|
}
|
|
return
|
|
Yap_unify(ARG1,MkIntegerTerm(cls)) &&
|
|
Yap_unify(ARG2,MkIntegerTerm(sz)) &&
|
|
Yap_unify(ARG3,MkIntegerTerm(icls)) &&
|
|
Yap_unify(ARG4,MkIntegerTerm(isz));
|
|
}
|
|
|
|
static Int
|
|
lu_erased_statistics(PredEntry *pe USES_REGS)
|
|
{
|
|
UInt sz = 0, cls = 0;
|
|
UInt isz = 0, icls = 0;
|
|
LogUpdClause *cl = DBErasedList;
|
|
LogUpdIndex *icl = DBErasedIList;
|
|
|
|
while (cl) {
|
|
if (cl->ClPred == pe) {
|
|
cls++;
|
|
sz += cl->ClSize;
|
|
}
|
|
cl = cl->ClNext;
|
|
}
|
|
while (icl) {
|
|
if (pe == icl->ClPred) {
|
|
icls++;
|
|
isz += icl->ClSize;
|
|
}
|
|
icl = icl->SiblingIndex;
|
|
}
|
|
return
|
|
Yap_unify(ARG2,MkIntegerTerm(cls)) &&
|
|
Yap_unify(ARG3,MkIntegerTerm(sz)) &&
|
|
Yap_unify(ARG4,MkIntegerTerm(icls)) &&
|
|
Yap_unify(ARG5,MkIntegerTerm(isz));
|
|
}
|
|
|
|
static Int
|
|
p_key_erased_statistics( USES_REGS1 )
|
|
{
|
|
Term twork = Deref(ARG1);
|
|
PredEntry *pe;
|
|
|
|
/* only for log upds */
|
|
if ((pe = find_lu_entry(twork)) == NULL)
|
|
return FALSE;
|
|
return lu_erased_statistics(pe PASS_REGS);
|
|
}
|
|
|
|
static Int
|
|
p_heap_space_info( USES_REGS1 )
|
|
{
|
|
return
|
|
Yap_unify(ARG1,MkIntegerTerm(HeapUsed)) &&
|
|
Yap_unify(ARG2,MkIntegerTerm(HeapMax-HeapUsed)) &&
|
|
Yap_unify(ARG3,MkIntegerTerm(Yap_expand_clauses_sz));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* This is called when we are erasing a data base clause, because we may have
|
|
* pending references
|
|
*/
|
|
static void
|
|
ErasePendingRefs(DBTerm *entryref USES_REGS)
|
|
{
|
|
DBRef *cp;
|
|
DBRef ref;
|
|
|
|
cp = entryref->DBRefs;
|
|
if (entryref->DBRefs == NULL)
|
|
return;
|
|
while ((ref = *--cp) != NULL) {
|
|
if ((ref->Flags & DBClMask) && (--(ref->NOfRefsTo) == 0)
|
|
&& (ref->Flags & ErasedMask))
|
|
ErDBE(ref PASS_REGS);
|
|
}
|
|
}
|
|
|
|
|
|
inline static void
|
|
RemoveDBEntry(DBRef entryref USES_REGS)
|
|
{
|
|
|
|
ErasePendingRefs(&(entryref->DBT) PASS_REGS);
|
|
/* We may be backtracking back to a deleted entry. If we just remove
|
|
the space then the info on the entry may be corrupt. */
|
|
if ((B->cp_ap == RETRY_C_RECORDED_K_CODE
|
|
|| B->cp_ap == RETRY_C_RECORDEDP_CODE) &&
|
|
EXTRA_CBACK_ARG(3,1) == (CELL)entryref) {
|
|
/* make it clear the entry has been released */
|
|
#if MULTIPLE_STACKS
|
|
DEC_DBREF_COUNT(entryref);
|
|
#else
|
|
entryref->Flags &= ~InUseMask;
|
|
#endif
|
|
DBErasedMarker->Next = NULL;
|
|
DBErasedMarker->Parent = entryref->Parent;
|
|
DBErasedMarker->n = entryref->n;
|
|
EXTRA_CBACK_ARG(3,1) = (CELL)DBErasedMarker;
|
|
}
|
|
if (entryref->p != NULL)
|
|
entryref->p->n = entryref->n;
|
|
else
|
|
entryref->Parent->F0 = entryref->n;
|
|
if (entryref->n != NULL)
|
|
entryref->n->p = entryref->p;
|
|
else
|
|
entryref->Parent->L0 = entryref->p;
|
|
/* Yap_LUClauseSpace -= entryref->Size; */
|
|
FreeDBSpace((char *) entryref);
|
|
}
|
|
|
|
static yamop *
|
|
find_next_clause(DBRef ref0 USES_REGS)
|
|
{
|
|
Register DBRef ref;
|
|
yamop *newp;
|
|
|
|
/* fetch ref0 from the instruction we just started executing */
|
|
#ifdef DEBUG
|
|
if (!(ref0->Flags & ErasedMask)) {
|
|
Yap_Error(SYSTEM_ERROR, TermNil, "find_next_clause (dead clause %x)", ref0);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
/* search for an newer entry that is to the left and points to code */
|
|
ref = ref0;
|
|
while ((ref = ref->n) != NULL) {
|
|
if (!(ref->Flags & ErasedMask))
|
|
break;
|
|
}
|
|
/* no extra alternatives to try, let us leave gracefully */
|
|
if (ref == NULL) {
|
|
return NULL;
|
|
} else {
|
|
/* OK, we found a clause we can jump to, do a bit of hanky pancking with
|
|
the choice-point, so that it believes we are actually working from that
|
|
clause */
|
|
newp = ref->Code;
|
|
/* and next let's tell the world this clause is being used, just
|
|
like if we were executing a standard retry_and_mark */
|
|
#if MULTIPLE_STACKS
|
|
{
|
|
DynamicClause *cl = ClauseCodeToDynamicClause(newp);
|
|
|
|
LOCK(cl->ClLock);
|
|
TRAIL_CLREF(cl);
|
|
INC_CLREF_COUNT(cl);
|
|
UNLOCK(cl->ClLock);
|
|
}
|
|
#else
|
|
if (!(DynamicFlags(newp) & InUseMask)) {
|
|
DynamicFlags(newp) |= InUseMask;
|
|
TRAIL_CLREF(ClauseCodeToDynamicClause(newp));
|
|
}
|
|
#endif
|
|
return newp;
|
|
}
|
|
}
|
|
|
|
/* This procedure is called when a clause is officialy deleted. Its job
|
|
is to find out where the code can go next, if it can go anywhere */
|
|
static Int
|
|
p_jump_to_next_dynamic_clause( USES_REGS1 )
|
|
{
|
|
DBRef ref = (DBRef)(((yamop *)((CODEADDR)P-(CELL)NEXTOP((yamop *)NULL,Osbpp)))->y_u.Osbpp.bmap);
|
|
yamop *newp = find_next_clause(ref PASS_REGS);
|
|
|
|
if (newp == NULL) {
|
|
cut_fail();
|
|
}
|
|
/* the next alternative to try must be obtained from this clause */
|
|
B->cp_ap = newp;
|
|
/* and next, enter the clause */
|
|
P = NEXTOP(newp,Otapl);
|
|
/* and return like if nothing had happened. */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
complete_lu_erase(LogUpdClause *clau)
|
|
{
|
|
DBRef *cp;
|
|
|
|
if (clau->ClFlags & FactMask)
|
|
cp = NULL;
|
|
else
|
|
cp = clau->lusl.ClSource->DBRefs;
|
|
if (CL_IN_USE(clau)) {
|
|
return;
|
|
}
|
|
#ifndef THREADS
|
|
if (clau->ClNext)
|
|
clau->ClNext->ClPrev = clau->ClPrev;
|
|
if (clau->ClPrev) {
|
|
clau->ClPrev->ClNext = clau->ClNext;
|
|
} else {
|
|
DBErasedList = clau->ClNext;
|
|
}
|
|
#endif
|
|
if (cp != NULL) {
|
|
DBRef ref;
|
|
while ((ref = *--cp) != NIL) {
|
|
if (ref->Flags & LogUpdMask) {
|
|
LogUpdClause *cl = (LogUpdClause *)ref;
|
|
cl->ClRefCount--;
|
|
if (cl->ClFlags & ErasedMask &&
|
|
!(cl->ClFlags & InUseMask) &&
|
|
!(cl->ClRefCount)) {
|
|
EraseLogUpdCl(cl);
|
|
}
|
|
} else {
|
|
LOCK(ref->lock);
|
|
ref->NOfRefsTo--;
|
|
if (ref->Flags & ErasedMask &&
|
|
!(ref->Flags & InUseMask) &&
|
|
ref->NOfRefsTo) {
|
|
CACHE_REGS
|
|
UNLOCK(ref->lock);
|
|
ErDBE(ref PASS_REGS);
|
|
} else {
|
|
UNLOCK(ref->lock);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Yap_InformOfRemoval(clau);
|
|
Yap_LUClauseSpace -= clau->ClSize;
|
|
Yap_FreeCodeSpace((char *)clau);
|
|
}
|
|
|
|
static void
|
|
EraseLogUpdCl(LogUpdClause *clau)
|
|
{
|
|
PredEntry *ap;
|
|
|
|
ap = clau->ClPred;
|
|
/* no need to erase what has been erased */
|
|
if (!(clau->ClFlags & ErasedMask)) {
|
|
/* get ourselves out of the list */
|
|
if (clau->ClNext != NULL) {
|
|
clau->ClNext->ClPrev = clau->ClPrev;
|
|
}
|
|
if (clau->ClPrev != NULL) {
|
|
clau->ClPrev->ClNext = clau->ClNext;
|
|
}
|
|
if (ap) {
|
|
if (clau->ClCode == ap->cs.p_code.FirstClause) {
|
|
if (clau->ClNext == NULL) {
|
|
ap->cs.p_code.FirstClause = NULL;
|
|
} else {
|
|
ap->cs.p_code.FirstClause = clau->ClNext->ClCode;
|
|
}
|
|
}
|
|
if (clau->ClCode == ap->cs.p_code.LastClause) {
|
|
if (clau->ClPrev == NULL) {
|
|
ap->cs.p_code.LastClause = NULL;
|
|
} else {
|
|
ap->cs.p_code.LastClause = clau->ClPrev->ClCode;
|
|
}
|
|
}
|
|
ap->cs.p_code.NOfClauses--;
|
|
}
|
|
clau->ClFlags |= ErasedMask;
|
|
#ifndef THREADS
|
|
{
|
|
LogUpdClause *er_head = DBErasedList;
|
|
if (er_head == NULL) {
|
|
clau->ClPrev = clau->ClNext = NULL;
|
|
} else {
|
|
clau->ClNext = er_head;
|
|
er_head->ClPrev = clau;
|
|
clau->ClPrev = NULL;
|
|
}
|
|
DBErasedList = clau;
|
|
}
|
|
#endif
|
|
/* we are holding a reference to the clause */
|
|
clau->ClRefCount++;
|
|
if (ap) {
|
|
/* mark it as erased */
|
|
if (ap->LastCallOfPred != LUCALL_RETRACT) {
|
|
if (ap->cs.p_code.NOfClauses > 1) {
|
|
if (ap->TimeStampOfPred >= TIMESTAMP_RESET)
|
|
Yap_UpdateTimestamps(ap);
|
|
++ap->TimeStampOfPred;
|
|
/* fprintf(stderr,"- %x--%d--%ul\n",ap,ap->TimeStampOfPred,ap->ArityOfPE);*/
|
|
ap->LastCallOfPred = LUCALL_RETRACT;
|
|
} else {
|
|
/* OK, there's noone left */
|
|
#ifndef THREADS
|
|
if (ap->cs.p_code.NOfClauses == 0) {
|
|
/* Other threads may hold refs to clauses */
|
|
ap->TimeStampOfPred = 0L;
|
|
}
|
|
#endif
|
|
/* fprintf(stderr,"- %x--%d--%ul\n",ap,ap->TimeStampOfPred,ap->ArityOfPE);*/
|
|
ap->LastCallOfPred = LUCALL_ASSERT;
|
|
}
|
|
}
|
|
clau->ClTimeEnd = ap->TimeStampOfPred;
|
|
Yap_RemoveClauseFromIndex(ap, clau->ClCode);
|
|
/* release the extra reference */
|
|
}
|
|
clau->ClRefCount--;
|
|
}
|
|
complete_lu_erase(clau);
|
|
}
|
|
|
|
static void
|
|
MyEraseClause(DynamicClause *clau USES_REGS)
|
|
{
|
|
DBRef ref;
|
|
|
|
if (CL_IN_USE(clau))
|
|
return;
|
|
/*
|
|
I don't need to lock the clause at this point because
|
|
I am the last one using it anyway.
|
|
*/
|
|
ref = (DBRef) NEXTOP(clau->ClCode,Otapl)->y_u.Osbpp.bmap;
|
|
/* don't do nothing if the reference is still in use */
|
|
if (DBREF_IN_USE(ref))
|
|
return;
|
|
if ( P == clau->ClCode ) {
|
|
yamop *np = RTRYCODE;
|
|
/* make it the next alternative */
|
|
np->y_u.Otapl.d = find_next_clause((DBRef)(NEXTOP(P,Otapl)->y_u.Osbpp.bmap) PASS_REGS);
|
|
if (np->y_u.Otapl.d == NULL)
|
|
P = (yamop *)FAILCODE;
|
|
else {
|
|
/* with same arity as before */
|
|
np->y_u.Otapl.s = P->y_u.Otapl.s;
|
|
np->y_u.Otapl.p = P->y_u.Otapl.p;
|
|
/* go ahead and try this code */
|
|
P = np;
|
|
}
|
|
} else {
|
|
Yap_InformOfRemoval(clau);
|
|
Yap_LUClauseSpace -= clau->ClSize;
|
|
Yap_FreeCodeSpace((char *)clau);
|
|
#ifdef DEBUG
|
|
if (ref->NOfRefsTo)
|
|
fprintf(stderr, "Error: references to dynamic clause\n");
|
|
#endif
|
|
RemoveDBEntry(ref PASS_REGS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
This predicate is supposed to be called with a
|
|
lock on the current predicate
|
|
*/
|
|
void
|
|
Yap_ErLogUpdCl(LogUpdClause *clau)
|
|
{
|
|
EraseLogUpdCl(clau);
|
|
}
|
|
|
|
/*
|
|
This predicate is supposed to be called with a
|
|
lock on the current predicate
|
|
*/
|
|
void
|
|
Yap_ErCl(DynamicClause *clau)
|
|
{
|
|
CACHE_REGS
|
|
MyEraseClause(clau PASS_REGS);
|
|
}
|
|
|
|
static void
|
|
PrepareToEraseLogUpdClause(LogUpdClause *clau, DBRef dbr)
|
|
{
|
|
yamop *code_p = clau->ClCode;
|
|
PredEntry *p = clau->ClPred;
|
|
yamop *cl = code_p;
|
|
|
|
if (clau->ClFlags & ErasedMask) {
|
|
return;
|
|
}
|
|
clau->ClFlags |= ErasedMask;
|
|
if (p->cs.p_code.FirstClause != cl) {
|
|
/* we are not the first clause... */
|
|
yamop *prev_code_p = (yamop *)(dbr->Prev->Code);
|
|
prev_code_p->y_u.Otapl.d = code_p->y_u.Otapl.d;
|
|
/* are we the last? */
|
|
if (p->cs.p_code.LastClause == cl)
|
|
p->cs.p_code.LastClause = prev_code_p;
|
|
} else {
|
|
/* we are the first clause, what about the last ? */
|
|
if (p->cs.p_code.LastClause == p->cs.p_code.FirstClause) {
|
|
p->cs.p_code.LastClause = p->cs.p_code.FirstClause = NULL;
|
|
} else {
|
|
p->cs.p_code.FirstClause = code_p->y_u.Otapl.d;
|
|
p->cs.p_code.FirstClause->opc =
|
|
Yap_opcode(_try_me);
|
|
}
|
|
}
|
|
dbr->Code = NULL; /* unlink the two now */
|
|
if (p->PredFlags & IndexedPredFlag) {
|
|
p->cs.p_code.NOfClauses--;
|
|
Yap_RemoveIndexation(p);
|
|
} else {
|
|
EraseLogUpdCl(clau);
|
|
}
|
|
if (p->cs.p_code.FirstClause == p->cs.p_code.LastClause) {
|
|
if (p->cs.p_code.FirstClause != NULL) {
|
|
code_p = p->cs.p_code.FirstClause;
|
|
code_p->y_u.Otapl.d = p->cs.p_code.FirstClause;
|
|
p->cs.p_code.TrueCodeOfPred = NEXTOP(code_p, Otapl);
|
|
if (p->PredFlags & (SpiedPredFlag|CountPredFlag|ProfiledPredFlag)) {
|
|
p->OpcodeOfPred = Yap_opcode(_spy_pred);
|
|
p->CodeOfPred = (yamop *)(&(p->OpcodeOfPred));
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
} else if (p->ModuleOfPred != IDB_MODULE &&
|
|
!(p->PredFlags & ThreadLocalPredFlag)) {
|
|
p->OpcodeOfPred = LOCKPRED_OPCODE;
|
|
p->CodeOfPred = (yamop *)(&(p->OpcodeOfPred));
|
|
#endif
|
|
} else {
|
|
p->CodeOfPred = p->cs.p_code.TrueCodeOfPred;
|
|
p->OpcodeOfPred = p->cs.p_code.TrueCodeOfPred->opc;
|
|
}
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
} else if (p->ModuleOfPred != IDB_MODULE &&
|
|
!(p->PredFlags & ThreadLocalPredFlag)) {
|
|
p->OpcodeOfPred = LOCKPRED_OPCODE;
|
|
p->CodeOfPred = (yamop *)(&(p->OpcodeOfPred));
|
|
#endif
|
|
} else {
|
|
p->OpcodeOfPred = FAIL_OPCODE;
|
|
p->cs.p_code.TrueCodeOfPred = p->CodeOfPred = (yamop *)(&(p->OpcodeOfPred));
|
|
}
|
|
} else {
|
|
if (p->PredFlags & (SpiedPredFlag|CountPredFlag|ProfiledPredFlag)) {
|
|
p->OpcodeOfPred = Yap_opcode(_spy_pred);
|
|
p->CodeOfPred = (yamop *)(&(p->OpcodeOfPred));
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
} else if (p->ModuleOfPred != IDB_MODULE &&
|
|
!(p->PredFlags & ThreadLocalPredFlag)) {
|
|
p->OpcodeOfPred = LOCKPRED_OPCODE;
|
|
p->CodeOfPred = (yamop *)(&(p->OpcodeOfPred));
|
|
#endif
|
|
} else {
|
|
p->OpcodeOfPred = INDEX_OPCODE;
|
|
p->CodeOfPred = (yamop *)(&(p->OpcodeOfPred));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
PrepareToEraseClause(DynamicClause *clau, DBRef dbr)
|
|
{
|
|
}
|
|
|
|
static void
|
|
ErDBE(DBRef entryref USES_REGS)
|
|
{
|
|
|
|
if ((entryref->Flags & DBCode) && entryref->Code) {
|
|
if (entryref->Flags & LogUpdMask) {
|
|
LogUpdClause *clau = ClauseCodeToLogUpdClause(entryref->Code);
|
|
if (CL_IN_USE(clau) || entryref->NOfRefsTo != 0) {
|
|
PrepareToEraseLogUpdClause(clau, entryref);
|
|
} else {
|
|
if (!(clau->ClFlags & ErasedMask))
|
|
PrepareToEraseLogUpdClause(clau, entryref);
|
|
/* the clause must have left the chain */
|
|
EraseLogUpdCl(clau);
|
|
}
|
|
} else {
|
|
DynamicClause *clau = ClauseCodeToDynamicClause(entryref->Code);
|
|
if (CL_IN_USE(clau) || entryref->NOfRefsTo != 0) {
|
|
PrepareToEraseClause(clau, entryref);
|
|
} else {
|
|
if (!(clau->ClFlags & ErasedMask))
|
|
PrepareToEraseClause(clau, entryref);
|
|
/* the clause must have left the chain */
|
|
MyEraseClause(clau PASS_REGS);
|
|
}
|
|
}
|
|
} else if (!(DBREF_IN_USE(entryref))) {
|
|
if (entryref->NOfRefsTo == 0)
|
|
RemoveDBEntry(entryref PASS_REGS);
|
|
else if (!(entryref->Flags & ErasedMask)) {
|
|
/* oops, I cannot remove it, but I at least have to tell
|
|
the world what's going on */
|
|
entryref->Flags |= ErasedMask;
|
|
entryref->Next = entryref->Prev = NIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Yap_ErDBE(DBRef entryref)
|
|
{
|
|
CACHE_REGS
|
|
ErDBE(entryref PASS_REGS);
|
|
}
|
|
|
|
static void
|
|
EraseEntry(DBRef entryref)
|
|
{
|
|
DBProp p;
|
|
|
|
if (entryref->Flags & ErasedMask)
|
|
return;
|
|
if (entryref->Flags & LogUpdMask &&
|
|
!(entryref->Flags & DBClMask)) {
|
|
LogUpdClause *luclause = (LogUpdClause *)entryref;
|
|
PELOCK(67,luclause->ClPred);
|
|
EraseLogUpdCl(luclause);
|
|
UNLOCK(luclause->ClPred->PELock);
|
|
return;
|
|
}
|
|
entryref->Flags |= ErasedMask;
|
|
/* update FirstNEr */
|
|
p = entryref->Parent;
|
|
/* exit the db chain */
|
|
if (entryref->Next != NIL) {
|
|
entryref->Next->Prev = entryref->Prev;
|
|
} else {
|
|
p->Last = entryref->Prev;
|
|
}
|
|
if (entryref->Prev != NIL)
|
|
entryref->Prev->Next = entryref->Next;
|
|
else
|
|
p->First = entryref->Next;
|
|
/* make sure we know the entry has been removed from the list */
|
|
entryref->Next = NIL;
|
|
if (!DBREF_IN_USE(entryref)) {
|
|
CACHE_REGS
|
|
ErDBE(entryref PASS_REGS);
|
|
} else if ((entryref->Flags & DBCode) && entryref->Code) {
|
|
PrepareToEraseClause(ClauseCodeToDynamicClause(entryref->Code), entryref);
|
|
}
|
|
}
|
|
|
|
/* erase(+Ref) */
|
|
static Int
|
|
p_erase( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1);
|
|
|
|
if (IsVarTerm(t1)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t1, "erase");
|
|
return FALSE;
|
|
}
|
|
if (!IsDBRefTerm(t1)) {
|
|
Yap_Error(TYPE_ERROR_DBREF, t1, "erase");
|
|
return FALSE;
|
|
}
|
|
EraseEntry(DBRefOfTerm(t1));
|
|
return TRUE;
|
|
}
|
|
|
|
/* increase_reference_counter(+Ref) */
|
|
static Int
|
|
p_increase_reference_counter( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1);
|
|
LogUpdClause *cl;
|
|
|
|
if (IsVarTerm(t1)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t1, "increase_reference_counter/1");
|
|
return FALSE;
|
|
}
|
|
if (!IsDBRefTerm(t1)) {
|
|
Yap_Error(TYPE_ERROR_DBREF, t1, "increase_reference_counter");
|
|
return FALSE;
|
|
}
|
|
cl = (LogUpdClause *)DBRefOfTerm(t1);
|
|
PELOCK(67,cl->ClPred);
|
|
cl->ClRefCount++;
|
|
UNLOCK(cl->ClPred->PELock);
|
|
return TRUE;
|
|
}
|
|
|
|
/* increase_reference_counter(+Ref) */
|
|
static Int
|
|
p_decrease_reference_counter( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1);
|
|
LogUpdClause *cl;
|
|
|
|
if (IsVarTerm(t1)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t1, "increase_reference_counter/1");
|
|
return FALSE;
|
|
}
|
|
if (!IsDBRefTerm(t1)) {
|
|
Yap_Error(TYPE_ERROR_DBREF, t1, "increase_reference_counter");
|
|
return FALSE;
|
|
}
|
|
cl = (LogUpdClause *)DBRefOfTerm(t1);
|
|
PELOCK(67,cl->ClPred);
|
|
if (cl->ClRefCount) {
|
|
cl->ClRefCount--;
|
|
UNLOCK(cl->ClPred->PELock);
|
|
return TRUE;
|
|
}
|
|
UNLOCK(cl->ClPred->PELock);
|
|
return FALSE;
|
|
}
|
|
|
|
/* erase(+Ref) */
|
|
/** @pred erase(+ _R_)
|
|
|
|
|
|
The term referred to by _R_ is erased from the internal database. If
|
|
reference _R_ does not exist in the database, `erase` just fails.
|
|
|
|
|
|
*/
|
|
static Int
|
|
p_current_reference_counter( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1);
|
|
LogUpdClause *cl;
|
|
|
|
if (IsVarTerm(t1)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t1, "increase_reference_counter/1");
|
|
return FALSE;
|
|
}
|
|
if (!IsDBRefTerm(t1)) {
|
|
Yap_Error(TYPE_ERROR_DBREF, t1, "increase_reference_counter");
|
|
return FALSE;
|
|
}
|
|
cl = (LogUpdClause *)DBRefOfTerm(t1);
|
|
return Yap_unify(ARG2, MkIntegerTerm(cl->ClRefCount));
|
|
}
|
|
|
|
static Int
|
|
p_erase_clause( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1);
|
|
DBRef entryref;
|
|
|
|
if (IsVarTerm(t1)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t1, "erase");
|
|
return FALSE;
|
|
}
|
|
if (!IsDBRefTerm(t1)) {
|
|
if (IsApplTerm(t1)) {
|
|
if (FunctorOfTerm(t1) == FunctorStaticClause) {
|
|
Yap_EraseStaticClause(Yap_ClauseFromTerm(t1), (PredEntry *)IntegerOfTerm(ArgOfTerm(2,t1)), Deref(ARG2));
|
|
return TRUE;
|
|
}
|
|
if (FunctorOfTerm(t1) == FunctorMegaClause) {
|
|
Yap_EraseMegaClause(Yap_MegaClauseFromTerm(t1), Yap_MegaClausePredicateFromTerm(t1));
|
|
return TRUE;
|
|
}
|
|
if (FunctorOfTerm(t1) == FunctorExoClause) {
|
|
Yap_Error(TYPE_ERROR_DBREF, t1, "erase exo clause");
|
|
return FALSE;
|
|
}
|
|
}
|
|
Yap_Error(TYPE_ERROR_DBREF, t1, "erase");
|
|
return FALSE;
|
|
} else {
|
|
entryref = DBRefOfTerm(t1);
|
|
}
|
|
EraseEntry(entryref);
|
|
return TRUE;
|
|
}
|
|
|
|
/* eraseall(+Key) */
|
|
/** @pred eraseall(+ _K_)
|
|
|
|
All terms belonging to the key `K` are erased from the internal
|
|
database. The predicate always succeeds.
|
|
|
|
*/
|
|
static Int
|
|
p_eraseall( USES_REGS1 )
|
|
{
|
|
Register Term twork = Deref(ARG1);
|
|
Register DBRef entryref;
|
|
DBProp p;
|
|
PredEntry *pe;
|
|
|
|
if ((pe = find_lu_entry(twork)) != NULL) {
|
|
LogUpdClause *cl;
|
|
|
|
if (!pe->cs.p_code.NOfClauses)
|
|
return TRUE;
|
|
if (pe->PredFlags & IndexedPredFlag)
|
|
Yap_RemoveIndexation(pe);
|
|
cl = ClauseCodeToLogUpdClause(pe->cs.p_code.FirstClause);
|
|
do {
|
|
LogUpdClause *ncl = cl->ClNext;
|
|
Yap_ErLogUpdCl(cl);
|
|
cl = ncl;
|
|
} while (cl != NULL);
|
|
return TRUE;
|
|
}
|
|
if (EndOfPAEntr(p = FetchDBPropFromKey(twork, 0, FALSE, "eraseall/3"))) {
|
|
return TRUE;
|
|
}
|
|
WRITE_LOCK(p->DBRWLock);
|
|
entryref = FrstDBRef(p);
|
|
do {
|
|
DBRef next_entryref;
|
|
|
|
while (entryref != NIL &&
|
|
(entryref->Flags & (DBCode | ErasedMask)))
|
|
entryref = NextDBRef(entryref);
|
|
if (entryref == NIL)
|
|
break;
|
|
next_entryref = NextDBRef(entryref);
|
|
/* exit the db chain */
|
|
if (entryref->Next != NIL) {
|
|
entryref->Next->Prev = entryref->Prev;
|
|
} else {
|
|
p->Last = entryref->Prev;
|
|
}
|
|
if (entryref->Prev != NIL)
|
|
entryref->Prev->Next = entryref->Next;
|
|
else
|
|
p->First = entryref->Next;
|
|
/* make sure we know the entry has been removed from the list */
|
|
entryref->Next = entryref->Prev = NIL;
|
|
if (!DBREF_IN_USE(entryref))
|
|
ErDBE(entryref PASS_REGS);
|
|
else {
|
|
entryref->Flags |= ErasedMask;
|
|
}
|
|
entryref = next_entryref;
|
|
} while (entryref != NIL);
|
|
WRITE_UNLOCK(p->DBRWLock);
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/* erased(+Ref) */
|
|
/** @pred erased(+ _R_)
|
|
|
|
|
|
Succeeds if the object whose database reference is _R_ has been
|
|
erased.
|
|
|
|
|
|
*/
|
|
static Int
|
|
p_erased( USES_REGS1 )
|
|
{
|
|
Term t = Deref(ARG1);
|
|
|
|
if (IsVarTerm(t)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t, "erased");
|
|
return (FALSE);
|
|
}
|
|
if (!IsDBRefTerm(t)) {
|
|
Yap_Error(TYPE_ERROR_DBREF, t, "erased");
|
|
return (FALSE);
|
|
}
|
|
return (DBRefOfTerm(t)->Flags & ErasedMask);
|
|
}
|
|
|
|
static Int
|
|
static_instance(StaticClause *cl, PredEntry *ap USES_REGS)
|
|
{
|
|
if (cl->ClFlags & ErasedMask) {
|
|
return FALSE;
|
|
}
|
|
if (cl->ClFlags & FactMask) {
|
|
if (ap->ArityOfPE == 0) {
|
|
return Yap_unify(ARG2,MkAtomTerm((Atom)ap->FunctorOfPred));
|
|
} else {
|
|
Functor f = ap->FunctorOfPred;
|
|
UInt arity = ArityOfFunctor(ap->FunctorOfPred), i;
|
|
Term t2 = Deref(ARG2);
|
|
CELL *ptr;
|
|
|
|
if (IsVarTerm(t2)) {
|
|
Yap_unify(ARG2, (t2 = Yap_MkNewApplTerm(f,arity)));
|
|
} else if (!IsApplTerm(t2) || FunctorOfTerm(t2) != f) {
|
|
return FALSE;
|
|
}
|
|
ptr = RepAppl(t2)+1;
|
|
for (i=0; i<arity; i++) {
|
|
XREGS[i+1] = ptr[i];
|
|
}
|
|
CP = P;
|
|
YENV = ASP;
|
|
YENV[E_CB] = (CELL) B;
|
|
P = cl->ClCode;
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
Term TermDB;
|
|
|
|
while ((TermDB = GetDBTerm(cl->usc.ClSource, TRUE PASS_REGS)) == 0L) {
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 2, ENV, gc_P(P,CP))) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
return Yap_unify(ARG2, TermDB);
|
|
}
|
|
}
|
|
|
|
static Int
|
|
exo_instance(Int i, PredEntry *ap USES_REGS)
|
|
{
|
|
if (ap->ArityOfPE == 0) {
|
|
return Yap_unify(ARG2,MkAtomTerm((Atom)ap->FunctorOfPred));
|
|
} else {
|
|
MegaClause *mcl = ClauseCodeToMegaClause(ap->cs.p_code.FirstClause);
|
|
Functor f = ap->FunctorOfPred;
|
|
UInt arity = ArityOfFunctor(ap->FunctorOfPred);
|
|
Term t2 = Deref(ARG2);
|
|
CELL *ptr = (CELL *)((ADDR)mcl->ClCode+2*sizeof(struct index_t *)+i*(mcl->ClItemSize));
|
|
if (IsVarTerm(t2)) {
|
|
// fresh slate
|
|
t2 = Yap_MkApplTerm(f,arity,ptr);
|
|
Yap_unify(ARG2, t2);
|
|
} else if (!IsApplTerm(t2) || FunctorOfTerm(t2) != f) {
|
|
return FALSE;
|
|
}
|
|
for (i=0; i<arity; i++) {
|
|
XREGS[i+1] = ptr[i];
|
|
}
|
|
S = ptr;
|
|
CP = P;
|
|
YENV = ASP;
|
|
YENV[E_CB] = (CELL) B;
|
|
P = mcl->ClCode;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static Int
|
|
mega_instance(yamop *code, PredEntry *ap USES_REGS)
|
|
{
|
|
if (ap->ArityOfPE == 0) {
|
|
return Yap_unify(ARG2,MkAtomTerm((Atom)ap->FunctorOfPred));
|
|
} else {
|
|
Functor f = ap->FunctorOfPred;
|
|
UInt arity = ArityOfFunctor(ap->FunctorOfPred), i;
|
|
Term t2 = Deref(ARG2);
|
|
CELL *ptr;
|
|
|
|
if (IsVarTerm(t2)) {
|
|
t2 = Yap_MkNewApplTerm(f,arity);
|
|
Yap_unify(ARG2, t2);
|
|
} else if (!IsApplTerm(t2) || FunctorOfTerm(t2) != f) {
|
|
return FALSE;
|
|
}
|
|
ptr = RepAppl(t2)+1;
|
|
for (i=0; i<arity; i++) {
|
|
XREGS[i+1] = ptr[i];
|
|
}
|
|
CP = P;
|
|
YENV = ASP;
|
|
YENV[E_CB] = (CELL) B;
|
|
P = code;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* instance(+Ref,?Term) */
|
|
/** @pred instance(+ _R_,- _T_)
|
|
|
|
|
|
If _R_ refers to a clause or a recorded term, _T_ is unified
|
|
with its most general instance. If _R_ refers to an unit clause
|
|
_C_, then _T_ is unified with ` _C_ :- true`. When
|
|
_R_ is not a reference to an existing clause or to a recorded term,
|
|
this goal fails.
|
|
|
|
|
|
*/
|
|
static Int
|
|
p_instance( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1);
|
|
DBRef dbr;
|
|
|
|
if (IsVarTerm(t1) || !IsDBRefTerm(t1)) {
|
|
if (IsApplTerm(t1)) {
|
|
if (FunctorOfTerm(t1) == FunctorStaticClause) {
|
|
return static_instance(Yap_ClauseFromTerm(t1), (PredEntry *)IntegerOfTerm(ArgOfTerm(2,t1)) PASS_REGS);
|
|
}
|
|
if (FunctorOfTerm(t1) == FunctorMegaClause) {
|
|
return mega_instance(Yap_MegaClauseFromTerm(t1), Yap_MegaClausePredicateFromTerm(t1) PASS_REGS);
|
|
}
|
|
if (FunctorOfTerm(t1) == FunctorExoClause) {
|
|
return exo_instance(Yap_ExoClauseFromTerm(t1), Yap_ExoClausePredicateFromTerm(t1) PASS_REGS);
|
|
}
|
|
}
|
|
return FALSE;
|
|
} else {
|
|
dbr = DBRefOfTerm(t1);
|
|
}
|
|
if (dbr->Flags & LogUpdMask) {
|
|
op_numbers opc;
|
|
LogUpdClause *cl = (LogUpdClause *)dbr;
|
|
PredEntry *ap = cl->ClPred;
|
|
|
|
PELOCK(68,ap);
|
|
if (cl->ClFlags & ErasedMask) {
|
|
UNLOCK(ap->PELock);
|
|
return FALSE;
|
|
}
|
|
if (cl->ClFlags & FactMask) {
|
|
if (ap->ArityOfPE == 0) {
|
|
UNLOCK(ap->PELock);
|
|
return Yap_unify(ARG2,MkAtomTerm((Atom)ap->FunctorOfPred));
|
|
} else {
|
|
Functor f = ap->FunctorOfPred;
|
|
UInt arity = ArityOfFunctor(ap->FunctorOfPred), i;
|
|
Term t2 = Deref(ARG2);
|
|
CELL *ptr;
|
|
|
|
if (IsVarTerm(t2)) {
|
|
Yap_unify(ARG2, (t2 = Yap_MkNewApplTerm(f,arity)));
|
|
} else if (!IsApplTerm(t2) || FunctorOfTerm(t2) != f) {
|
|
UNLOCK(ap->PELock);
|
|
return FALSE;
|
|
}
|
|
ptr = RepAppl(t2)+1;
|
|
for (i=0; i<arity; i++) {
|
|
XREGS[i+1] = ptr[i];
|
|
}
|
|
CP = P;
|
|
YENV = ASP;
|
|
YENV[E_CB] = (CELL) B;
|
|
P = cl->ClCode;
|
|
#if defined(YAPOR) || defined(THREADS)
|
|
if (ap->PredFlags & ThreadLocalPredFlag) {
|
|
UNLOCK(ap->PELock);
|
|
} else {
|
|
PP = ap;
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
}
|
|
opc = Yap_op_from_opcode(cl->ClCode->opc);
|
|
if (opc == _unify_idb_term) {
|
|
UNLOCK(ap->PELock);
|
|
return Yap_unify(ARG2, cl->lusl.ClSource->Entry);
|
|
} else {
|
|
Term TermDB;
|
|
int in_cl = (opc != _copy_idb_term);
|
|
|
|
while ((TermDB = GetDBTerm(cl->lusl.ClSource, in_cl PASS_REGS)) == 0L) {
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
UNLOCK(ap->PELock);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 2, ENV, gc_P(P,CP))) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
UNLOCK(ap->PELock);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
UNLOCK(ap->PELock);
|
|
return Yap_unify(ARG2, TermDB);
|
|
}
|
|
} else {
|
|
Term TermDB;
|
|
while ((TermDB = GetDBTermFromDBEntry(dbr PASS_REGS)) == 0L) {
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 2, ENV, gc_P(P,CP))) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return FALSE;
|
|
}
|
|
}
|
|
t1 = Deref(ARG1);
|
|
}
|
|
return Yap_unify(ARG2, TermDB);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Term
|
|
Yap_LUInstance(LogUpdClause *cl, UInt arity)
|
|
{
|
|
CACHE_REGS
|
|
Term TermDB;
|
|
op_numbers opc = Yap_op_from_opcode(cl->ClCode->opc);
|
|
|
|
if (opc == _unify_idb_term) {
|
|
TermDB = cl->lusl.ClSource->Entry;
|
|
} else {
|
|
CACHE_REGS
|
|
int in_src;
|
|
|
|
in_src = (opc != _copy_idb_term);
|
|
while ((TermDB = GetDBTerm(cl->lusl.ClSource, in_src PASS_REGS)) == 0L) {
|
|
/* oops, we are in trouble, not enough stack space */
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return 0L;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, arity, ENV, gc_P(P,CP))) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return 0L;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if MULTIPLE_STACKS
|
|
cl->ClRefCount++;
|
|
TRAIL_CLREF(cl); /* So that fail will erase it */
|
|
#else
|
|
if (!(cl->ClFlags & InUseMask)) {
|
|
cl->ClFlags |= InUseMask;
|
|
TRAIL_CLREF(cl);
|
|
}
|
|
#endif
|
|
return TermDB;
|
|
}
|
|
|
|
|
|
/* instance(+Ref,?Term) */
|
|
static Int
|
|
p_instance_module( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1);
|
|
DBRef dbr;
|
|
|
|
if (IsVarTerm(t1)) {
|
|
return FALSE;
|
|
}
|
|
if (IsDBRefTerm(t1)) {
|
|
dbr = DBRefOfTerm(t1);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
if (dbr->Flags & LogUpdMask) {
|
|
LogUpdClause *cl = (LogUpdClause *)dbr;
|
|
|
|
if (cl->ClFlags & ErasedMask) {
|
|
return FALSE;
|
|
}
|
|
if (cl->ClPred->ModuleOfPred)
|
|
return Yap_unify(ARG2, cl->ClPred->ModuleOfPred);
|
|
else
|
|
return Yap_unify(ARG2, TermProlog);
|
|
} else {
|
|
return Yap_unify(ARG2, dbr->Parent->ModuleOfDB);
|
|
}
|
|
}
|
|
|
|
inline static int
|
|
NotActiveDB(DBRef my_dbref)
|
|
{
|
|
while (my_dbref && (my_dbref->Flags & (DBCode | ErasedMask)))
|
|
my_dbref = my_dbref->Next;
|
|
return (my_dbref == NIL);
|
|
}
|
|
|
|
inline static DBEntry *
|
|
NextDBProp(PropEntry *pp)
|
|
{
|
|
while (!EndOfPAEntr(pp) && (((pp->KindOfPE & ~ 0x1) != DBProperty) ||
|
|
NotActiveDB(((DBProp) pp)->First)))
|
|
pp = RepProp(pp->NextOfPE);
|
|
return ((DBEntry *)pp);
|
|
}
|
|
|
|
static Int
|
|
init_current_key( USES_REGS1 )
|
|
{ /* current_key(+Atom,?key) */
|
|
Int i = 0;
|
|
DBEntry *pp;
|
|
Atom a;
|
|
Term t1 = ARG1;
|
|
|
|
t1 = Deref(ARG1);
|
|
if (!IsVarTerm(t1)) {
|
|
if (IsAtomTerm(t1))
|
|
a = AtomOfTerm(t1);
|
|
else {
|
|
cut_fail();
|
|
}
|
|
} else {
|
|
/* ask for the first hash line */
|
|
while (TRUE) {
|
|
READ_LOCK(HashChain[i].AERWLock);
|
|
a = HashChain[i].Entry;
|
|
if (a != NIL) {
|
|
break;
|
|
}
|
|
READ_UNLOCK(HashChain[i].AERWLock);
|
|
i++;
|
|
}
|
|
READ_UNLOCK(HashChain[i].AERWLock);
|
|
}
|
|
READ_LOCK(RepAtom(a)->ARWLock);
|
|
pp = NextDBProp(RepProp(RepAtom(a)->PropsOfAE));
|
|
READ_UNLOCK(RepAtom(a)->ARWLock);
|
|
EXTRA_CBACK_ARG(2,3) = MkAtomTerm(a);
|
|
EXTRA_CBACK_ARG(2,2) = MkIntTerm(i);
|
|
EXTRA_CBACK_ARG(2,1) = MkIntegerTerm((Int)pp);
|
|
return cont_current_key( PASS_REGS1 );
|
|
}
|
|
|
|
static Int
|
|
cont_current_key( USES_REGS1 )
|
|
{
|
|
unsigned int arity;
|
|
Functor functor;
|
|
Term term, AtT;
|
|
Atom a;
|
|
Int i = IntegerOfTerm(EXTRA_CBACK_ARG(2,2));
|
|
Term first = Deref(ARG1);
|
|
DBEntry *pp = (DBEntry *) IntegerOfTerm(EXTRA_CBACK_ARG(2,1));
|
|
|
|
if (IsIntTerm(term = EXTRA_CBACK_ARG(2,3)))
|
|
return cont_current_key_integer( PASS_REGS1 );
|
|
a = AtomOfTerm(term);
|
|
if (EndOfPAEntr(pp) && IsAtomTerm(first)) {
|
|
cut_fail();
|
|
}
|
|
while (EndOfPAEntr(pp)) {
|
|
UInt j;
|
|
|
|
if ((a = RepAtom(a)->NextOfAE) == NIL) {
|
|
i++;
|
|
while (i < AtomHashTableSize) {
|
|
/* protect current hash table line, notice that the current
|
|
LOCK/UNLOCK algorithm assumes new entries are added to
|
|
the *front* of the list, otherwise I should have locked
|
|
earlier.
|
|
*/
|
|
READ_LOCK(HashChain[i].AERWLock);
|
|
a = HashChain[i].Entry;
|
|
if (a != NIL) {
|
|
break;
|
|
}
|
|
/* move to next entry */
|
|
READ_UNLOCK(HashChain[i].AERWLock);
|
|
i++;
|
|
}
|
|
if (i == AtomHashTableSize) {
|
|
/* we have left the atom hash table */
|
|
/* we don't have a lock over the hash table any longer */
|
|
if (IsAtomTerm(first)) {
|
|
cut_fail();
|
|
}
|
|
j = 0;
|
|
if (INT_KEYS == NULL) {
|
|
cut_fail();
|
|
}
|
|
for(j = 0; j < INT_KEYS_SIZE; j++) {
|
|
if (INT_KEYS[j] != NIL) {
|
|
DBProp pptr = RepDBProp(INT_KEYS[j]);
|
|
EXTRA_CBACK_ARG(2,1) = MkIntegerTerm((Int)(pptr->NextOfPE));
|
|
EXTRA_CBACK_ARG(2,2) = MkIntegerTerm(j+1);
|
|
EXTRA_CBACK_ARG(2,3) = MkIntTerm(INT_KEYS_TIMESTAMP);
|
|
term = MkIntegerTerm((Int)(pptr->FunctorOfDB));
|
|
return Yap_unify(term,ARG1) && Yap_unify(term,ARG2);
|
|
}
|
|
}
|
|
if (j == INT_KEYS_SIZE) {
|
|
cut_fail();
|
|
}
|
|
return cont_current_key_integer( PASS_REGS1 );
|
|
} else {
|
|
/* release our lock over the hash table */
|
|
READ_UNLOCK(HashChain[i].AERWLock);
|
|
EXTRA_CBACK_ARG(2,2) = MkIntTerm(i);
|
|
}
|
|
}
|
|
READ_LOCK(RepAtom(a)->ARWLock);
|
|
if (!EndOfPAEntr(pp = NextDBProp(RepProp(RepAtom(a)->PropsOfAE))))
|
|
EXTRA_CBACK_ARG(2,3) = (CELL) MkAtomTerm(a);
|
|
READ_UNLOCK(RepAtom(a)->ARWLock);
|
|
}
|
|
READ_LOCK(RepAtom(a)->ARWLock);
|
|
EXTRA_CBACK_ARG(2,1) = MkIntegerTerm((Int)NextDBProp(RepProp(pp->NextOfPE)));
|
|
READ_UNLOCK(RepAtom(a)->ARWLock);
|
|
arity = (unsigned int)(pp->ArityOfDB);
|
|
if (arity == 0) {
|
|
term = AtT = MkAtomTerm(a);
|
|
} else {
|
|
unsigned int j;
|
|
CELL *p = HR;
|
|
|
|
for (j = 0; j < arity; j++) {
|
|
p[j] = MkVarTerm();
|
|
}
|
|
functor = Yap_MkFunctor(a, arity);
|
|
term = Yap_MkApplTerm(functor, arity, p);
|
|
AtT = MkAtomTerm(a);
|
|
}
|
|
return (Yap_unify_constant(ARG1, AtT) && Yap_unify(ARG2, term));
|
|
}
|
|
|
|
static Int
|
|
cont_current_key_integer( USES_REGS1 )
|
|
{
|
|
Term term;
|
|
UInt i = IntOfTerm(EXTRA_CBACK_ARG(2,2));
|
|
Prop pp = (Prop)IntegerOfTerm(EXTRA_CBACK_ARG(2,1));
|
|
UInt tstamp = (UInt)IntOfTerm(EXTRA_CBACK_ARG(2,3));
|
|
DBProp pptr;
|
|
|
|
if (tstamp != INT_KEYS_TIMESTAMP) {
|
|
cut_fail();
|
|
}
|
|
while (pp == NIL) {
|
|
for(;i < INT_KEYS_SIZE; i++) {
|
|
if (INT_KEYS[i] != NIL) {
|
|
EXTRA_CBACK_ARG(2,2) = MkIntTerm(i+1);
|
|
pp = INT_KEYS[i];
|
|
break;
|
|
}
|
|
}
|
|
if (i == INT_KEYS_SIZE) {
|
|
cut_fail();
|
|
}
|
|
}
|
|
pptr = RepDBProp(pp);
|
|
EXTRA_CBACK_ARG(2,1) = MkIntegerTerm((Int)(pptr->NextOfPE));
|
|
term = MkIntegerTerm((Int)(pptr->FunctorOfDB));
|
|
return Yap_unify(term,ARG1) && Yap_unify(term,ARG2);
|
|
}
|
|
|
|
Term
|
|
Yap_FetchTermFromDB(DBTerm *ref)
|
|
{
|
|
CACHE_REGS
|
|
return GetDBTerm(ref, FALSE PASS_REGS);
|
|
}
|
|
|
|
Term
|
|
Yap_FetchClauseTermFromDB(DBTerm *ref)
|
|
{
|
|
CACHE_REGS
|
|
return GetDBTerm(ref, TRUE PASS_REGS);
|
|
}
|
|
|
|
Term
|
|
Yap_PopTermFromDB(DBTerm *ref)
|
|
{
|
|
CACHE_REGS
|
|
|
|
Term t = GetDBTerm(ref, FALSE PASS_REGS);
|
|
if (t != 0L)
|
|
ReleaseTermFromDB(ref PASS_REGS);
|
|
return t;
|
|
}
|
|
|
|
static DBTerm *
|
|
StoreTermInDB(Term t, int nargs USES_REGS)
|
|
{
|
|
DBTerm *x;
|
|
int needs_vars;
|
|
struct db_globs dbg;
|
|
|
|
LOCAL_s_dbg = &dbg;
|
|
LOCAL_Error_Size = 0;
|
|
while ((x = (DBTerm *)CreateDBStruct(t, (DBProp)NULL,
|
|
InQueue, &needs_vars, 0, &dbg)) == NULL) {
|
|
if (LOCAL_Error_TYPE == YAP_NO_ERROR) {
|
|
break;
|
|
} else if (nargs == -1) {
|
|
return NULL;
|
|
} else {
|
|
XREGS[nargs+1] = t;
|
|
if (recover_from_record_error(nargs+1)) {
|
|
t = Deref(XREGS[nargs+1]);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
|
|
DBTerm *
|
|
Yap_StoreTermInDB(Term t, int nargs) {
|
|
CACHE_REGS
|
|
return StoreTermInDB(t, nargs PASS_REGS);
|
|
}
|
|
|
|
DBTerm *
|
|
Yap_StoreTermInDBPlusExtraSpace(Term t, UInt extra_size, UInt *sz) {
|
|
CACHE_REGS
|
|
int needs_vars;
|
|
struct db_globs dbg;
|
|
DBTerm *o;
|
|
|
|
LOCAL_s_dbg = &dbg;
|
|
o = (DBTerm *)CreateDBStruct(t, (DBProp)NULL,
|
|
InQueue, &needs_vars, extra_size, &dbg);
|
|
*sz = dbg.sz;
|
|
return o;
|
|
}
|
|
|
|
void
|
|
Yap_init_tqueue( db_queue *dbq )
|
|
{
|
|
dbq->id = FunctorDBRef;
|
|
dbq->Flags = DBClMask;
|
|
dbq->FirstInQueue = dbq->LastInQueue = NULL;
|
|
INIT_RWLOCK(dbq->QRWLock);
|
|
}
|
|
|
|
void
|
|
Yap_destroy_tqueue( db_queue *dbq USES_REGS)
|
|
{
|
|
QueueEntry * cur_instance = dbq->FirstInQueue;
|
|
while (cur_instance) {
|
|
/* release space for cur_instance */
|
|
keepdbrefs(cur_instance->DBT PASS_REGS);
|
|
ErasePendingRefs(cur_instance->DBT PASS_REGS);
|
|
FreeDBSpace((char *) cur_instance->DBT);
|
|
FreeDBSpace((char *) cur_instance);
|
|
}
|
|
dbq->FirstInQueue =
|
|
dbq->LastInQueue = NULL;
|
|
|
|
}
|
|
|
|
bool
|
|
Yap_enqueue_tqueue(db_queue *father_key, Term t USES_REGS)
|
|
{
|
|
QueueEntry *x;
|
|
while ((x = (QueueEntry *)AllocDBSpace(sizeof(QueueEntry))) == NULL) {
|
|
if (!Yap_growheap(FALSE, sizeof(QueueEntry), NULL)) {
|
|
Yap_Error(OUT_OF_HEAP_ERROR, TermNil, "in findall");
|
|
return false;
|
|
}
|
|
}
|
|
/* Yap_LUClauseSpace += sizeof(QueueEntry); */
|
|
x->DBT = StoreTermInDB(Deref(t), 2 PASS_REGS);
|
|
if (x->DBT == NULL) {
|
|
return false;
|
|
}
|
|
x->next = NULL;
|
|
if (father_key->LastInQueue != NULL)
|
|
father_key->LastInQueue->next = x;
|
|
father_key->LastInQueue = x;
|
|
if (father_key->FirstInQueue == NULL) {
|
|
father_key->FirstInQueue = x;
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
bool
|
|
Yap_dequeue_tqueue(db_queue *father_key, Term t, bool first, bool release USES_REGS)
|
|
{
|
|
Term TDB;
|
|
CELL *oldH = HR ;
|
|
tr_fr_ptr oldTR = TR;
|
|
QueueEntry * cur_instance = father_key->FirstInQueue, *prev = NULL;
|
|
while (cur_instance) {
|
|
HR = oldH;
|
|
HB = LCL0;
|
|
while ((TDB = GetDBTerm(cur_instance->DBT, false PASS_REGS)) == 0L) {
|
|
if (LOCAL_Error_TYPE == OUT_OF_ATTVARS_ERROR) {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_growglobal(NULL)) {
|
|
Yap_Error(OUT_OF_ATTVARS_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return false;
|
|
}
|
|
} else {
|
|
LOCAL_Error_TYPE = YAP_NO_ERROR;
|
|
if (!Yap_gcl(LOCAL_Error_Size, 2, ENV, gc_P(P,CP))) {
|
|
Yap_Error(OUT_OF_STACK_ERROR, TermNil, LOCAL_ErrorMessage);
|
|
return false;
|
|
}
|
|
}
|
|
oldTR = TR;
|
|
oldH = HR;
|
|
}
|
|
if (Yap_unify(t, TDB)) {
|
|
if (release) {
|
|
if (cur_instance == father_key->FirstInQueue) {
|
|
father_key->FirstInQueue = cur_instance->next;
|
|
}
|
|
if (cur_instance == father_key->LastInQueue) {
|
|
father_key->LastInQueue = prev;
|
|
}
|
|
if (prev) {
|
|
prev->next = cur_instance->next;
|
|
}
|
|
/* release space for cur_instance */
|
|
keepdbrefs(cur_instance->DBT PASS_REGS);
|
|
ErasePendingRefs(cur_instance->DBT PASS_REGS);
|
|
FreeDBSpace((char *) cur_instance->DBT);
|
|
FreeDBSpace((char *) cur_instance);
|
|
} else {
|
|
// undo if you'rejust peeking
|
|
while (oldTR < TR) {
|
|
CELL d1 = TrailTerm(TR-1);
|
|
TR--;
|
|
/* normal variable */
|
|
RESET_VARIABLE(d1);
|
|
}
|
|
|
|
}
|
|
return true;
|
|
} else {
|
|
// just getting the first
|
|
if (first)
|
|
return false;
|
|
// but keep on going, if we want to check everything.
|
|
prev = cur_instance;
|
|
cur_instance = cur_instance->next;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
static Int
|
|
p_init_queue( USES_REGS1 )
|
|
{
|
|
db_queue *dbq;
|
|
Term t;
|
|
|
|
while ((dbq = (db_queue *)AllocDBSpace(sizeof(db_queue))) == NULL) {
|
|
if (!Yap_growheap(FALSE, sizeof(db_queue), NULL)) {
|
|
Yap_Error(OUT_OF_HEAP_ERROR, TermNil, "in findall");
|
|
return FALSE;
|
|
}
|
|
}
|
|
/* Yap_LUClauseSpace += sizeof(db_queue); */
|
|
Yap_init_tqueue( dbq );
|
|
t = MkIntegerTerm((Int)dbq);
|
|
return Yap_unify(ARG1, t);
|
|
}
|
|
|
|
|
|
static Int
|
|
p_enqueue( USES_REGS1 )
|
|
{
|
|
Term Father = Deref(ARG1);
|
|
db_queue *father_key;
|
|
bool rc;
|
|
|
|
if (IsVarTerm(Father)) {
|
|
Yap_Error(INSTANTIATION_ERROR, Father, "enqueue");
|
|
return FALSE;
|
|
} else if (!IsIntegerTerm(Father)) {
|
|
Yap_Error(TYPE_ERROR_INTEGER, Father, "enqueue");
|
|
return FALSE;
|
|
} else
|
|
father_key = (db_queue *)IntegerOfTerm(Father);
|
|
WRITE_LOCK(father_key->QRWLock);
|
|
rc = Yap_enqueue_tqueue(father_key, Deref(ARG2) PASS_REGS);
|
|
WRITE_UNLOCK(father_key->QRWLock);
|
|
return rc;
|
|
}
|
|
|
|
static Int
|
|
p_enqueue_unlocked( USES_REGS1 )
|
|
{
|
|
Term Father = Deref(ARG1);
|
|
db_queue *father_key;
|
|
|
|
if (IsVarTerm(Father)) {
|
|
Yap_Error(INSTANTIATION_ERROR, Father, "enqueue");
|
|
return FALSE;
|
|
} else if (!IsIntegerTerm(Father)) {
|
|
Yap_Error(TYPE_ERROR_INTEGER, Father, "enqueue");
|
|
return FALSE;
|
|
} else
|
|
father_key = (db_queue *)IntegerOfTerm(Father);
|
|
return Yap_enqueue_tqueue(father_key, Deref(ARG2) PASS_REGS);
|
|
}
|
|
|
|
/* when reading an entry in the data base we are making it accessible from
|
|
the outside. If the entry was removed, and this was the last pointer, the
|
|
target entry would be immediately removed, leading to dangling pointers.
|
|
We avoid this problem by making every entry accessible.
|
|
|
|
Note that this could not happen with recorded, because the original db
|
|
entry itself is still accessible from a trail entry, so we could not remove
|
|
the target entry,
|
|
*/
|
|
static void
|
|
keepdbrefs(DBTerm *entryref USES_REGS)
|
|
{
|
|
DBRef *cp;
|
|
DBRef ref;
|
|
|
|
cp = entryref->DBRefs;
|
|
if (cp == NULL) {
|
|
return;
|
|
}
|
|
while ((ref = *--cp) != NIL) {
|
|
if (!(ref->Flags & LogUpdMask)) {
|
|
LOCK(ref->lock);
|
|
if(!(ref->Flags & InUseMask)) {
|
|
ref->Flags |= InUseMask;
|
|
TRAIL_REF(ref); /* So that fail will erase it */
|
|
}
|
|
UNLOCK(ref->lock);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static Int
|
|
p_dequeue( USES_REGS1 )
|
|
{
|
|
db_queue *father_key;
|
|
QueueEntry *cur_instance;
|
|
Term Father = Deref(ARG1);
|
|
|
|
if (IsVarTerm(Father)) {
|
|
Yap_Error(INSTANTIATION_ERROR, Father, "dequeue");
|
|
return FALSE;
|
|
} else if (!IsIntegerTerm(Father)) {
|
|
Yap_Error(TYPE_ERROR_INTEGER, Father, "dequeue");
|
|
return FALSE;
|
|
} else {
|
|
father_key = (db_queue *)IntegerOfTerm(Father);
|
|
WRITE_LOCK(father_key->QRWLock);
|
|
if ((cur_instance = father_key->FirstInQueue) == NULL) {
|
|
/* an empty queue automatically goes away */
|
|
WRITE_UNLOCK(father_key->QRWLock);
|
|
FreeDBSpace((char *)father_key);
|
|
return FALSE;
|
|
}
|
|
if (!Yap_dequeue_tqueue(father_key, ARG2, true, true PASS_REGS) )
|
|
return FALSE;
|
|
if (cur_instance == father_key->LastInQueue)
|
|
father_key->FirstInQueue = father_key->LastInQueue = NULL;
|
|
else
|
|
father_key->FirstInQueue = cur_instance->next;
|
|
WRITE_UNLOCK(father_key->QRWLock);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
static Int
|
|
p_dequeue_unlocked( USES_REGS1 )
|
|
{
|
|
db_queue *father_key;
|
|
QueueEntry *cur_instance;
|
|
Term Father = Deref(ARG1);
|
|
|
|
if (IsVarTerm(Father)) {
|
|
Yap_Error(INSTANTIATION_ERROR, Father, "dequeue");
|
|
return FALSE;
|
|
} else if (!IsIntegerTerm(Father)) {
|
|
Yap_Error(TYPE_ERROR_INTEGER, Father, "dequeue");
|
|
return FALSE;
|
|
} else {
|
|
father_key = (db_queue *)IntegerOfTerm(Father);
|
|
if ((cur_instance = father_key->FirstInQueue) == NULL) {
|
|
/* an empty queue automatically goes away */
|
|
FreeDBSpace((char *)father_key);
|
|
return FALSE;
|
|
}
|
|
if (!Yap_dequeue_tqueue(father_key, ARG2, true, true PASS_REGS) )
|
|
return FALSE;
|
|
if (cur_instance == father_key->LastInQueue)
|
|
father_key->FirstInQueue = father_key->LastInQueue = NULL;
|
|
else
|
|
father_key->FirstInQueue = cur_instance->next;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static Int
|
|
p_peek_queue( USES_REGS1 )
|
|
{
|
|
db_queue *father_key;
|
|
QueueEntry *cur_instance;
|
|
Term Father = Deref(ARG1);
|
|
|
|
if (IsVarTerm(Father)) {
|
|
Yap_Error(INSTANTIATION_ERROR, Father, "dequeue");
|
|
return FALSE;
|
|
} else if (!IsIntegerTerm(Father)) {
|
|
Yap_Error(TYPE_ERROR_INTEGER, Father, "dequeue");
|
|
return FALSE;
|
|
} else {
|
|
father_key = (db_queue *)IntegerOfTerm(Father);
|
|
if ((cur_instance = father_key->FirstInQueue) == NULL) {
|
|
/* an empty queue automatically goes away */
|
|
FreeDBSpace((char *)father_key);
|
|
return FALSE;
|
|
}
|
|
if (!Yap_dequeue_tqueue(father_key, ARG2, true, false PASS_REGS) )
|
|
return FALSE;
|
|
if (cur_instance == father_key->LastInQueue)
|
|
father_key->FirstInQueue = father_key->LastInQueue = NULL;
|
|
else
|
|
father_key->FirstInQueue = cur_instance->next;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
static Int
|
|
p_clean_queues( USES_REGS1 )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* set the logical updates flag */
|
|
static Int
|
|
p_slu( USES_REGS1 )
|
|
{
|
|
Term t = Deref(ARG1);
|
|
if (IsVarTerm(t)) {
|
|
Yap_Error(INSTANTIATION_ERROR, t, "switch_logical_updates/1");
|
|
return FALSE;
|
|
}
|
|
if (!IsIntTerm(t)) {
|
|
Yap_Error(TYPE_ERROR_INTEGER, t, "switch_logical_updates/1");
|
|
return FALSE;
|
|
}
|
|
UPDATE_MODE = IntOfTerm(t);
|
|
return TRUE;
|
|
}
|
|
|
|
/* check current status for logical updates */
|
|
static Int
|
|
p_lu( USES_REGS1 )
|
|
{
|
|
return Yap_unify(ARG1,MkIntTerm(UPDATE_MODE));
|
|
}
|
|
|
|
/* get a hold over the index table for logical update predicates */
|
|
static Int
|
|
p_hold_index( USES_REGS1 )
|
|
{
|
|
Yap_Error(SYSTEM_ERROR, TermNil, "hold_index in debugger");
|
|
return FALSE;
|
|
}
|
|
|
|
static Int
|
|
p_fetch_reference_from_index( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1), t2 = Deref(ARG2);
|
|
DBRef table, el;
|
|
Int pos;
|
|
|
|
if (IsVarTerm(t1) || !IsDBRefTerm(t1))
|
|
return FALSE;
|
|
table = DBRefOfTerm(t1);
|
|
|
|
if (IsVarTerm(t2) || !IsIntTerm(t2))
|
|
return FALSE;
|
|
pos = IntOfTerm(t2);
|
|
el = (DBRef)(table->DBT.Contents[pos]);
|
|
LOCK(el->lock);
|
|
#if MULTIPLE_STACKS
|
|
TRAIL_REF(el); /* So that fail will erase it */
|
|
INC_DBREF_COUNT(el);
|
|
#else
|
|
if (!(el->Flags & InUseMask)) {
|
|
el->Flags |= InUseMask;
|
|
TRAIL_REF(el);
|
|
}
|
|
#endif
|
|
UNLOCK(el->lock);
|
|
return Yap_unify(ARG3, MkDBRefTerm(el));
|
|
}
|
|
|
|
static Int
|
|
p_resize_int_keys( USES_REGS1 )
|
|
{
|
|
Term t1 = Deref(ARG1);
|
|
if (IsVarTerm(t1)) {
|
|
return Yap_unify(ARG1,MkIntegerTerm((Int)INT_KEYS_SIZE));
|
|
}
|
|
if (!IsIntegerTerm(t1)) {
|
|
Yap_Error(TYPE_ERROR_INTEGER, t1, "yap_flag(resize_db_int_keys,T)");
|
|
return FALSE;
|
|
}
|
|
return resize_int_keys(IntegerOfTerm(t1));
|
|
}
|
|
|
|
static void
|
|
ReleaseTermFromDB(DBTerm *ref USES_REGS)
|
|
{
|
|
if (!ref)
|
|
return;
|
|
keepdbrefs(ref PASS_REGS);
|
|
ErasePendingRefs(ref PASS_REGS);
|
|
FreeDBSpace((char *) ref);
|
|
}
|
|
|
|
void
|
|
Yap_ReleaseTermFromDB(DBTerm *ref)
|
|
{
|
|
CACHE_REGS
|
|
ReleaseTermFromDB(ref PASS_REGS);
|
|
}
|
|
|
|
static Int
|
|
p_install_thread_local( USES_REGS1 )
|
|
{ /* '$is_dynamic'(+P) */
|
|
PredEntry *pe;
|
|
Term t = Deref(ARG1);
|
|
Term mod = Deref(ARG2);
|
|
|
|
if (IsVarTerm(t)) {
|
|
return (FALSE);
|
|
}
|
|
if (mod == IDB_MODULE) {
|
|
pe = find_lu_entry(t);
|
|
if (!pe->cs.p_code.NOfClauses) {
|
|
if (IsIntegerTerm(t))
|
|
pe->PredFlags |= LogUpdatePredFlag|NumberDBPredFlag;
|
|
else if (IsAtomTerm(t))
|
|
pe->PredFlags |= LogUpdatePredFlag|AtomDBPredFlag;
|
|
else
|
|
pe->PredFlags |= LogUpdatePredFlag;
|
|
}
|
|
} else if (IsAtomTerm(t)) {
|
|
Atom at = AtomOfTerm(t);
|
|
pe = RepPredProp(PredPropByAtom(at, mod));
|
|
} else if (IsApplTerm(t)) {
|
|
Functor fun = FunctorOfTerm(t);
|
|
pe = RepPredProp(PredPropByFunc(fun, mod));
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
PELOCK(69,pe);
|
|
if (pe->PredFlags & (ThreadLocalPredFlag|LogUpdatePredFlag)) {
|
|
// second declaration, just ignore
|
|
UNLOCK(pe->PELock);
|
|
return TRUE;
|
|
}
|
|
if (pe->PredFlags & (UserCPredFlag|HiddenPredFlag|CArgsPredFlag|SyncPredFlag|TestPredFlag|AsmPredFlag|StandardPredFlag|CPredFlag|SafePredFlag|IndexedPredFlag|BinaryPredFlag) ||
|
|
pe->cs.p_code.NOfClauses) {
|
|
UNLOCK(pe->PELock);
|
|
return FALSE;
|
|
}
|
|
#if THREADS
|
|
pe->PredFlags |= ThreadLocalPredFlag|LogUpdatePredFlag;
|
|
pe->OpcodeOfPred = Yap_opcode(_thread_local);
|
|
pe->CodeOfPred = (yamop *)&pe->OpcodeOfPred;
|
|
#else
|
|
pe->PredFlags |= LogUpdatePredFlag;
|
|
#endif
|
|
UNLOCK(pe->PELock);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
Yap_InitDBPreds(void)
|
|
{
|
|
Yap_InitCPred("recorded", 3, p_recorded, SyncPredFlag);
|
|
/** @pred recorded(+ _K_, _T_, _R_)
|
|
|
|
|
|
Searches in the internal database under the key _K_, a term that
|
|
unifies with _T_ and whose reference matches _R_. This
|
|
built-in may be used in one of two ways:
|
|
|
|
+ _K_ may be given, in this case the built-in will return all
|
|
elements of the internal data-base that match the key.
|
|
+ _R_ may be given, if so returning the key and element that
|
|
match the reference.
|
|
|
|
|
|
|
|
*/
|
|
Yap_InitCPred("recorda", 3, p_rcda, SyncPredFlag);
|
|
/** @pred recorda(+ _K_, _T_,- _R_)
|
|
|
|
|
|
Makes term _T_ the first record under key _K_ and unifies _R_
|
|
with its reference.
|
|
|
|
|
|
*/
|
|
Yap_InitCPred("recordz", 3, p_rcdz, SyncPredFlag);
|
|
Yap_InitCPred("$still_variant", 2, p_still_variant, SyncPredFlag);
|
|
Yap_InitCPred("recorda_at", 3, p_rcda_at, SyncPredFlag);
|
|
Yap_InitCPred("recordz_at", 3, p_rcdz_at, SyncPredFlag);
|
|
Yap_InitCPred("$recordap", 3, p_rcdap, SyncPredFlag);
|
|
Yap_InitCPred("$recordzp", 3, p_rcdzp, SyncPredFlag);
|
|
Yap_InitCPred("$recordap", 4, p_drcdap, SyncPredFlag);
|
|
Yap_InitCPred("$recordzp", 4, p_drcdzp, SyncPredFlag);
|
|
Yap_InitCPred("erase", 1, p_erase, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$erase_clause", 2, p_erase_clause, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("increase_reference_count", 1, p_increase_reference_counter, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("decrease_reference_count", 1, p_decrease_reference_counter, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("current_reference_count", 2, p_current_reference_counter, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("erased", 1, p_erased, TestPredFlag | SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("instance", 2, p_instance, SyncPredFlag);
|
|
Yap_InitCPred("$instance_module", 2, p_instance_module, SyncPredFlag);
|
|
Yap_InitCPred("eraseall", 1, p_eraseall, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$record_stat_source", 4, p_rcdstatp, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$some_recordedp", 1, p_somercdedp, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$first_instance", 3, p_first_instance, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$init_db_queue", 1, p_init_queue, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$db_key", 2, p_db_key, 0L);
|
|
Yap_InitCPred("$db_enqueue", 2, p_enqueue, SyncPredFlag);
|
|
Yap_InitCPred("$db_enqueue_unlocked", 2, p_enqueue_unlocked, SyncPredFlag);
|
|
Yap_InitCPred("$db_dequeue", 2, p_dequeue, SyncPredFlag);
|
|
Yap_InitCPred("$db_dequeue_unlocked", 2, p_dequeue_unlocked, SyncPredFlag);
|
|
Yap_InitCPred("$db_peek_queue", 2, p_peek_queue, SyncPredFlag);
|
|
Yap_InitCPred("$db_clean_queues", 1, p_clean_queues, SyncPredFlag);
|
|
Yap_InitCPred("$switch_log_upd", 1, p_slu, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$log_upd", 1, p_lu, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$hold_index", 3, p_hold_index, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$fetch_reference_from_index", 3, p_fetch_reference_from_index, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("$resize_int_keys", 1, p_resize_int_keys, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("key_statistics", 4, p_key_statistics, SyncPredFlag);
|
|
Yap_InitCPred("$lu_statistics", 5, p_lu_statistics, SyncPredFlag);
|
|
Yap_InitCPred("total_erased", 4, p_total_erased, SyncPredFlag);
|
|
Yap_InitCPred("key_erased_statistics", 5, p_key_erased_statistics, SyncPredFlag);
|
|
Yap_InitCPred("heap_space_info", 3, p_heap_space_info, SyncPredFlag);
|
|
Yap_InitCPred("$jump_to_next_dynamic_clause", 0, p_jump_to_next_dynamic_clause, SyncPredFlag);
|
|
Yap_InitCPred("$install_thread_local", 2, p_install_thread_local, SafePredFlag);
|
|
}
|
|
|
|
void
|
|
Yap_InitBackDB(void)
|
|
{
|
|
Yap_InitCPredBack("$recorded_with_key", 3, 3, in_rded_with_key, co_rded, SyncPredFlag);
|
|
RETRY_C_RECORDED_K_CODE = NEXTOP(PredRecordedWithKey->cs.p_code.FirstClause,OtapFs);
|
|
Yap_InitCPredBack("$recordedp", 3, 3, in_rdedp, co_rdedp, SyncPredFlag);
|
|
RETRY_C_RECORDEDP_CODE = NEXTOP(RepPredProp(PredPropByFunc(Yap_MkFunctor(AtomRecordedP, 3),0))->cs.p_code.FirstClause,OtapFs);
|
|
Yap_InitCPredBack("$current_immediate_key", 2, 4, init_current_key, cont_current_key,
|
|
SyncPredFlag);
|
|
}
|
|
|
|
/**
|
|
@}
|
|
*/
|