This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/os/pl-table.c

474 lines
9.8 KiB
C
Raw Normal View History

2013-01-16 11:28:58 +00:00
/* Part of SWI-Prolog
2008-12-22 12:02:22 +00:00
Author: Jan Wielemaker
2013-01-16 11:28:58 +00:00
E-mail: J.Wielemaker@vu.nl
2008-12-22 12:02:22 +00:00
WWW: http://www.swi-prolog.org
2013-01-16 11:28:58 +00:00
Copyright (C): 1985-2012, University of Amsterdam
VU University Amsterdam
2008-12-22 12:02:22 +00:00
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
2013-01-16 11:28:58 +00:00
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2008-12-22 12:02:22 +00:00
*/
/*#define O_DEBUG 1*/
#include "pl-incl.h"
#ifdef O_PLMT
#define LOCK_TABLE(t) if ( t->mutex ) simpleMutexLock(t->mutex)
#define UNLOCK_TABLE(t) if ( t->mutex ) simpleMutexUnlock(t->mutex)
#else
#define LOCK_TABLE(t) (void)0
#define UNLOCK_TABLE(t) (void)0
#endif
static inline Symbol rawAdvanceTableEnum(TableEnum e);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
This file provides generic hash-tables. Most of the implementation is
rather straightforward. Special are the *TableEnum() functions to
create, advance over and destroy enumerator objects. These objects are
used to enumerate the symbols of these tables, used primarily for the
pl_current_* predicates.
2013-01-16 11:28:58 +00:00
The enumerators cause two things: (1) as long as enumerators are
2008-12-22 12:02:22 +00:00
associated, the table will not be rehashed and (2) if symbols are
deleted that are referenced by an enumerator, the enumerator is
2013-01-16 11:28:58 +00:00
automatically advanced to the next free symbol. This, in general, makes
2008-12-22 12:02:22 +00:00
the enumeration of hash-tables safe.
2013-01-16 11:28:58 +00:00
TBD: Resizing hash-tables causes major headaches for concurrent access.
We can avoid this by using a dynamic array for the list of hash-entries.
Ongoing work in the RDF store shows hash-tables that can handle
concurrent lock-free access.
2008-12-22 12:02:22 +00:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2013-01-16 11:28:58 +00:00
static Symbol *
allocHTableEntries(int buckets)
{ size_t bytes = buckets * sizeof(Symbol);
2008-12-22 12:02:22 +00:00
Symbol *p;
2013-01-16 11:28:58 +00:00
p = allocHeapOrHalt(bytes);
memset(p, 0, bytes);
2008-12-22 12:02:22 +00:00
2013-01-16 11:28:58 +00:00
return p;
2008-12-22 12:02:22 +00:00
}
Table
newHTable(int buckets)
2013-01-16 11:28:58 +00:00
{ Table ht;
2008-12-22 12:02:22 +00:00
2013-01-16 11:28:58 +00:00
ht = allocHeapOrHalt(sizeof(struct table));
2008-12-22 12:02:22 +00:00
ht->buckets = (buckets & ~TABLE_MASK);
ht->size = 0;
ht->enumerators = NULL;
ht->free_symbol = NULL;
ht->copy_symbol = NULL;
#ifdef O_PLMT
if ( (buckets & TABLE_UNLOCKED) )
ht->mutex = NULL;
else
2013-01-16 11:28:58 +00:00
{ ht->mutex = allocHeapOrHalt(sizeof(simpleMutex));
2008-12-22 12:02:22 +00:00
simpleMutexInit(ht->mutex);
}
#endif
2013-01-16 11:28:58 +00:00
ht->entries = allocHTableEntries(ht->buckets);
2008-12-22 12:02:22 +00:00
return ht;
}
void
destroyHTable(Table ht)
2013-01-16 11:28:58 +00:00
{
2008-12-22 12:02:22 +00:00
#ifdef O_PLMT
if ( ht->mutex )
{ simpleMutexDelete(ht->mutex);
freeHeap(ht->mutex, sizeof(*ht->mutex));
2013-01-16 11:28:58 +00:00
ht->mutex = NULL;
2008-12-22 12:02:22 +00:00
}
#endif
clearHTable(ht);
freeHeap(ht->entries, ht->buckets * sizeof(Symbol));
freeHeap(ht, sizeof(struct table));
}
2013-01-16 11:28:58 +00:00
#if O_DEBUG
2008-12-22 12:02:22 +00:00
static int lookups;
static int cmps;
2013-11-15 01:10:25 +00:00
int
2008-12-22 12:02:22 +00:00
exitTables(int status, void *arg)
2013-01-16 11:28:58 +00:00
{ (void)status;
(void)arg;
Sdprintf("hashstat: Anonymous tables: %d lookups using %d compares\n",
2008-12-22 12:02:22 +00:00
lookups, cmps);
2013-11-15 01:10:25 +00:00
return 0;
2008-12-22 12:02:22 +00:00
}
2013-01-16 11:28:58 +00:00
#endif
2008-12-22 12:02:22 +00:00
void
2011-02-10 00:02:05 +00:00
initTables(void)
2008-12-22 12:02:22 +00:00
{ static int done = FALSE;
if ( !done )
{ done = TRUE;
2011-02-10 00:01:19 +00:00
2013-01-16 11:28:58 +00:00
DEBUG(MSG_HASH_STAT, PL_on_halt(exitTables, NULL));
2008-12-22 12:02:22 +00:00
}
}
Symbol
lookupHTable(Table ht, void *name)
{ Symbol s = ht->entries[pointerHashValue(name, ht->buckets)];
2013-01-16 11:28:58 +00:00
DEBUG(MSG_HASH_STAT, lookups++);
2008-12-22 12:02:22 +00:00
for( ; s; s = s->next)
2013-01-16 11:28:58 +00:00
{ DEBUG(MSG_HASH_STAT, cmps++);
2008-12-22 12:02:22 +00:00
if ( s->name == name )
return s;
}
return NULL;
}
#ifdef O_DEBUG
void
checkHTable(Table ht)
{ int i;
int n = 0;
for(i=0; i<ht->buckets; i++)
{ Symbol s;
for(s=ht->entries[i]; s; s=s->next)
{ assert(lookupHTable(ht, s->name) == s);
n++;
}
}
assert(n == ht->size);
}
#endif
/* MT: Locked by calling addHTable()
*/
2013-01-16 11:28:58 +00:00
static Symbol
rehashHTable(Table ht, Symbol map)
{ Symbol *newentries, *oldentries;
int newbuckets, oldbuckets;
int i;
2013-11-15 01:10:25 +00:00
#if P_PLMT
2013-01-16 11:28:58 +00:00
int safe_copy = (ht->mutex != NULL);
2013-01-16 12:28:37 +00:00
#else
int safe_copy = TRUE;
#endif
2008-12-22 12:02:22 +00:00
2013-01-16 11:28:58 +00:00
newbuckets = ht->buckets*2;
newentries = allocHTableEntries(newbuckets);
2008-12-22 12:02:22 +00:00
2013-01-16 11:28:58 +00:00
DEBUG(MSG_HASH_STAT,
Sdprintf("Rehashing table %p to %d entries\n", ht, ht->buckets));
2008-12-22 12:02:22 +00:00
2013-01-16 11:28:58 +00:00
for(i=0; i<ht->buckets; i++)
2008-12-22 12:02:22 +00:00
{ Symbol s, n;
2013-01-16 11:28:58 +00:00
if ( safe_copy )
{ for(s=ht->entries[i]; s; s = n)
{ int v = (int)pointerHashValue(s->name, newbuckets);
Symbol s2 = allocHeapOrHalt(sizeof(*s2));
n = s->next;
if ( s == map )
map = s2;
*s2 = *s;
s2->next = newentries[v];
newentries[v] = s2;
}
} else
{ for(s=ht->entries[i]; s; s = n)
{ int v = (int)pointerHashValue(s->name, newbuckets);
n = s->next;
s->next = newentries[v];
newentries[v] = s;
}
}
}
oldentries = ht->entries;
oldbuckets = ht->buckets;
ht->entries = newentries;
ht->buckets = newbuckets;
if ( safe_copy )
{ /* Here we should be waiting until */
/* active lookup are finished */
for(i=0; i<oldbuckets; i++)
{ Symbol s, n;
2008-12-22 12:02:22 +00:00
2013-01-16 11:28:58 +00:00
for(s=oldentries[i]; s; s = n)
{ n = s->next;
s->next = NULL; /* that causes old readers to stop */
freeHeap(s, sizeof(*s));
}
2008-12-22 12:02:22 +00:00
}
}
2013-01-16 11:28:58 +00:00
freeHeap(oldentries, oldbuckets * sizeof(Symbol));
DEBUG(CHK_SECURE, checkHTable(ht));
return map;
2008-12-22 12:02:22 +00:00
}
Symbol
addHTable(Table ht, void *name, void *value)
2013-01-16 11:28:58 +00:00
{ Symbol s;
2008-12-22 12:02:22 +00:00
int v;
LOCK_TABLE(ht);
v = (int)pointerHashValue(name, ht->buckets);
if ( lookupHTable(ht, name) )
{ UNLOCK_TABLE(ht);
return NULL;
}
2013-01-16 11:28:58 +00:00
s = allocHeapOrHalt(sizeof(struct symbol));
2008-12-22 12:02:22 +00:00
s->name = name;
s->value = value;
s->next = ht->entries[v];
ht->entries[v] = s;
ht->size++;
DEBUG(9, Sdprintf("addHTable(0x%x, 0x%x, 0x%x) --> size = %d\n",
ht, name, value, ht->size));
if ( ht->buckets * 2 < ht->size && !ht->enumerators )
2013-01-16 11:28:58 +00:00
s = rehashHTable(ht, s);
2008-12-22 12:02:22 +00:00
UNLOCK_TABLE(ht);
DEBUG(1, checkHTable(ht));
return s;
2011-02-10 00:01:19 +00:00
}
2008-12-22 12:02:22 +00:00
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: s must be in the table!
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void
deleteSymbolHTable(Table ht, Symbol s)
2013-01-16 11:28:58 +00:00
{ int v;
2008-12-22 12:02:22 +00:00
Symbol *h;
TableEnum e;
LOCK_TABLE(ht);
v = (int)pointerHashValue(s->name, ht->buckets);
h = &ht->entries[v];
for( e=ht->enumerators; e; e = e->next )
{ if ( e->current == s )
rawAdvanceTableEnum(e);
}
for( ; *h; h = &(*h)->next )
{ if ( *h == s )
{ *h = (*h)->next;
2013-01-16 11:28:58 +00:00
s->next = NULL; /* force crash */
s->name = NULL;
s->value = NULL;
2008-12-22 12:02:22 +00:00
freeHeap(s, sizeof(struct symbol));
ht->size--;
break;
}
}
UNLOCK_TABLE(ht);
}
void
clearHTable(Table ht)
2013-01-16 11:28:58 +00:00
{ int n;
2008-12-22 12:02:22 +00:00
TableEnum e;
LOCK_TABLE(ht);
for( e=ht->enumerators; e; e = e->next )
{ e->current = NULL;
e->key = ht->buckets;
}
for(n=0; n < ht->buckets; n++)
{ Symbol s, q;
for(s = ht->entries[n]; s; s = q)
{ q = s->next;
if ( ht->free_symbol )
(*ht->free_symbol)(s);
freeHeap(s, sizeof(struct symbol));
}
ht->entries[n] = NULL;
}
ht->size = 0;
UNLOCK_TABLE(ht);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Table copyHTable(Table org)
Make a copy of a hash-table. This is used to realise the copy-on-write
as defined by SharedTable. The table is copied to have exactly the
same dimensions as the original. If the copy_symbol function is
provided, it is called to allow duplicating the symbols name or value
fields.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
Table
copyHTable(Table org)
2013-01-16 11:28:58 +00:00
{ Table ht;
2008-12-22 12:02:22 +00:00
int n;
2013-01-16 11:28:58 +00:00
ht = allocHeapOrHalt(sizeof(struct table));
2008-12-22 12:02:22 +00:00
LOCK_TABLE(org);
*ht = *org; /* copy all attributes */
#ifdef O_PLMT
ht->mutex = NULL;
#endif
2013-01-16 11:28:58 +00:00
ht->entries = allocHTableEntries(ht->buckets);
2008-12-22 12:02:22 +00:00
for(n=0; n < ht->buckets; n++)
{ Symbol s, *q;
q = &ht->entries[n];
for(s = org->entries[n]; s; s = s->next)
2013-01-16 11:28:58 +00:00
{ Symbol s2 = allocHeapOrHalt(sizeof(*s2));
2008-12-22 12:02:22 +00:00
*q = s2;
q = &s2->next;
s2->name = s->name;
s2->value = s->value;
if ( ht->copy_symbol )
(*ht->copy_symbol)(s2);
}
*q = NULL;
}
2011-02-10 00:01:19 +00:00
#ifdef O_PLMT
2008-12-22 12:02:22 +00:00
if ( org->mutex )
2013-01-16 11:28:58 +00:00
{ ht->mutex = allocHeapOrHalt(sizeof(simpleMutex));
2008-12-22 12:02:22 +00:00
simpleMutexInit(ht->mutex);
}
#endif
UNLOCK_TABLE(org);
return ht;
}
/*******************************
* ENUMERATING *
*******************************/
TableEnum
newTableEnum(Table ht)
2013-01-16 11:28:58 +00:00
{ TableEnum e = allocHeapOrHalt(sizeof(struct table_enum));
2008-12-22 12:02:22 +00:00
Symbol n;
LOCK_TABLE(ht);
e->table = ht;
e->key = 0;
e->next = ht->enumerators;
ht->enumerators = e;
n = ht->entries[0];
while(!n && ++e->key < ht->buckets)
n=ht->entries[e->key];
e->current = n;
UNLOCK_TABLE(ht);
return e;
}
void
freeTableEnum(TableEnum e)
2013-01-16 11:28:58 +00:00
{ TableEnum *ep;
2008-12-22 12:02:22 +00:00
Table ht;
if ( !e )
return;
ht = e->table;
LOCK_TABLE(ht);
for( ep=&ht->enumerators; *ep ; ep = &(*ep)->next )
{ if ( *ep == e )
{ *ep = (*ep)->next;
freeHeap(e, sizeof(*e));
break;
}
}
UNLOCK_TABLE(ht);
}
static inline Symbol
rawAdvanceTableEnum(TableEnum e)
{ Symbol s, n;
Table ht = e->table;
if ( !(s = e->current) )
return s;
n = s->next;
while(!n)
{ if ( ++e->key >= ht->buckets )
{ e->current = NULL;
return s;
}
n=ht->entries[e->key];
}
e->current = n;
return s;
}
Symbol
advanceTableEnum(TableEnum e)
{ Symbol s;
#ifdef O_PLMT
Table ht = e->table;
#endif
LOCK_TABLE(ht);
s = rawAdvanceTableEnum(e);
UNLOCK_TABLE(ht);
return s;
}