diff options
Diffstat (limited to 'erts/emulator/beam/erl_process_dict.c')
-rw-r--r-- | erts/emulator/beam/erl_process_dict.c | 1001 |
1 files changed, 1001 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c new file mode 100644 index 0000000000..93466da3aa --- /dev/null +++ b/erts/emulator/beam/erl_process_dict.c @@ -0,0 +1,1001 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * Code for process dictionaries. + * + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "erl_process.h" /* Will include erl_process_dict.h */ +#include "error.h" +#include "erl_driver.h" +#include "bif.h" +#include "big.h" +#include "dist.h" +#include "erl_version.h" + +/* #define HARDDEBUG */ + +/* +** Utility macros +*/ + +/* Flags to pd_get_hash */ +#define PD_GET_OTHER_PROCESS 1UL + +/* Hash constant macros */ +#define MAX_HASH 1342177280UL +#define INITIAL_SIZE 10 + +/* Hash utility macros */ +#define HASH_RANGE(PDict) ((PDict)->homeSize + (PDict)->splitPosition) + +#define MAKE_HASH(Term) \ +((is_small(Term)) ? unsigned_val(Term) : \ + ((is_atom(Term)) ? \ + (atom_tab(atom_val(term))->slot.bucket.hvalue) : \ + make_hash2(Term))) + +#define PD_SZ2BYTES(Sz) (sizeof(ProcDict) + ((Sz) - 1)*sizeof(Eterm)) + +/* Memory allocation macros */ +#define PD_ALLOC(Sz) \ + erts_alloc(ERTS_ALC_T_PROC_DICT, (Sz)) +#define PD_FREE(P, Sz) \ + erts_free(ERTS_ALC_T_PROC_DICT, (P)) +#define PD_REALLOC(P, OSz, NSz) \ + erts_realloc(ERTS_ALC_T_PROC_DICT, (P), (NSz)) + + +#define TCAR(Term) CAR(list_val(Term)) +#define TCDR(Term) CDR(list_val(Term)) + +/* Array access macro */ +#define ARRAY_GET(PDict, Index) (((PDict)->size > (Index)) ? \ + (PDict)->data[Index] : NIL) + +/* + * Forward decalarations + */ +static void pd_hash_erase(Process *p, Eterm id, Eterm *ret); +static void pd_hash_erase_all(Process *p); +static Eterm pd_hash_get_keys(Process *p, Eterm value); +static Eterm pd_hash_get_all(Process *p, ProcDict *pd); +static Eterm pd_hash_put(Process *p, Eterm id, Eterm value); + +static void shrink(Process *p, Eterm* ret); +static void grow(Process *p); + +static void array_shrink(ProcDict **ppd, unsigned int need); +static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term); + +static unsigned int pd_hash_value(ProcDict *pdict, Eterm term); +static unsigned int next_array_size(unsigned int need); + +/* +** Debugging prototypes and macros +*/ +#ifdef HARDDEBUG + +#include <stdarg.h> + +static int hdebugf(char *format, ...); +static char *hdebugf_file = ""; +static int hdebugf_line; +#define HDEBUGF(X) ((int) hdebugf_file = __FILE__, hdebugf_line = __LINE__, \ + hdebugf X) +#ifndef DEBUG +#define DEBUG 1 +#endif + +#else /* !HARDDEBUG */ + +#define HDEBUGF(X) /* Nothing */ + +#endif /* HARDDEBUG (else) */ + +#ifdef DEBUG + +static void pd_check(ProcDict *pd); + +#define PD_CHECK(PD) pd_check(PD) + +#else /* !DEBUG */ + +#define PD_CHECK(PD) /* Nothing */ + +#endif /* DEBUG (else) */ + +/* +** External interface +*/ + +/* + * Called from break handler + */ +void +erts_dictionary_dump(int to, void *to_arg, ProcDict *pd) +{ + unsigned int i; +#ifdef DEBUG + + /*PD_CHECK(pd);*/ + if (pd == NULL) + return; + erts_print(to, to_arg, "(size = %d, used = %d, homeSize = %d, " + "splitPosition = %d, numElements = %d)\n", + pd->size, pd->used, pd->homeSize, + pd->splitPosition, (unsigned int) pd->numElements); + for (i = 0; i < HASH_RANGE(pd); ++i) { + erts_print(to, to_arg, "%d: %T\n", i, ARRAY_GET(pd, i)); + } + +#else /* !DEBUG */ + + int written = 0; + Eterm t; + + erts_print(to, to_arg, "["); + if (pd != NULL) { + for (i = 0; i < HASH_RANGE(pd); ++i) { + t = ARRAY_GET(pd, i); + if (is_list(t)) { + for (; t != NIL; t = TCDR(t)) { + erts_print(to, to_arg, written++ ? ",%T" : "%T", TCAR(t)); + } + } else if (is_tuple(t)) { + erts_print(to, to_arg, written++ ? ",%T" : "%T", t); + } + } + } + erts_print(to, to_arg, "]"); + +#endif /* DEBUG (else) */ +} + +void +erts_deep_dictionary_dump(int to, void *to_arg, + ProcDict* pd, void (*cb)(int, void *, Eterm)) +{ + unsigned int i; + Eterm t; + + if (pd != NULL) { + for (i = 0; i < HASH_RANGE(pd); ++i) { + t = ARRAY_GET(pd, i); + if (is_list(t)) { + for (; t != NIL; t = TCDR(t)) { + (*cb)(to, to_arg, TCAR(t)); + } + } else if (is_tuple(t)) { + (*cb)(to, to_arg, t); + } + } + } +} + +Uint +erts_dicts_mem_size(Process *p) +{ + Uint size = 0; + if (p->dictionary) + size += PD_SZ2BYTES(p->dictionary->size); + return size; +} + +void +erts_erase_dicts(Process *p) +{ + if (p->dictionary) { + pd_hash_erase_all(p); + p->dictionary = NULL; + } +} + +/* + * Called from process_info/1,2. + */ +Eterm erts_dictionary_copy(Process *p, ProcDict *pd) +{ + Eterm* hp; + Eterm* heap_start; + Eterm res = NIL; + Eterm tmp, tmp2; + unsigned int i, num; + + if (pd == NULL) { + return res; + } + + PD_CHECK(pd); + num = HASH_RANGE(pd); + heap_start = hp = (Eterm *) erts_alloc(ERTS_ALC_T_TMP, + sizeof(Eterm) * pd->numElements * 2); + for (i = 0; i < num; ++i) { + tmp = ARRAY_GET(pd, i); + if (is_boxed(tmp)) { + ASSERT(is_tuple(tmp)); + res = CONS(hp, tmp, res); + hp += 2; + } else if (is_list(tmp)) { + while (tmp != NIL) { + tmp2 = TCAR(tmp); + res = CONS(hp, tmp2, res); + hp += 2; + tmp = TCDR(tmp); + } + } + } + res = copy_object(res, p); + erts_free(ERTS_ALC_T_TMP, (void *) heap_start); + return res; +} + + +/* +** BIF interface +*/ +BIF_RETTYPE get_0(BIF_ALIST_0) +{ + Eterm ret; + PD_CHECK(BIF_P->dictionary); + ret = pd_hash_get_all(BIF_P, BIF_P->dictionary); + PD_CHECK(BIF_P->dictionary); + BIF_RET(ret); +} + +BIF_RETTYPE get_1(BIF_ALIST_1) +{ + Eterm ret; + PD_CHECK(BIF_P->dictionary); + ret = erts_pd_hash_get(BIF_P, BIF_ARG_1); + PD_CHECK(BIF_P->dictionary); + BIF_RET(ret); +} + +BIF_RETTYPE get_keys_1(BIF_ALIST_1) +{ + Eterm ret; + + PD_CHECK(BIF_P->dictionary); + ret = pd_hash_get_keys(BIF_P, BIF_ARG_1); + PD_CHECK(BIF_P->dictionary); + BIF_RET(ret); +} + +BIF_RETTYPE put_2(BIF_ALIST_2) +{ + Eterm ret; + + PD_CHECK(BIF_P->dictionary); + ret = pd_hash_put(BIF_P, BIF_ARG_1, BIF_ARG_2); + PD_CHECK(BIF_P->dictionary); + BIF_RET(ret); +} + +BIF_RETTYPE erase_0(BIF_ALIST_0) +{ + Eterm ret; + PD_CHECK(BIF_P->dictionary); + ret = pd_hash_get_all(BIF_P, BIF_P->dictionary); + pd_hash_erase_all(BIF_P); + PD_CHECK(BIF_P->dictionary); + BIF_RET(ret); +} + +BIF_RETTYPE erase_1(BIF_ALIST_1) +{ + Eterm ret; + PD_CHECK(BIF_P->dictionary); + pd_hash_erase(BIF_P, BIF_ARG_1, &ret); + PD_CHECK(BIF_P->dictionary); + BIF_RET(ret); +} + +/* + * BIF implementations + */ +static void pd_hash_erase(Process *p, Eterm id, Eterm *ret) +{ + unsigned int hval; + Eterm old; + Eterm tmp; + unsigned int range; + + *ret = am_undefined; + if (p->dictionary == NULL) { + return; + } + hval = pd_hash_value(p->dictionary, id); + old = ARRAY_GET(p->dictionary, hval); + if (is_boxed(old)) { /* Tuple */ + ASSERT(is_tuple(old)); + if (EQ(tuple_val(old)[1], id)) { + array_put(&(p->dictionary), hval, NIL); + --(p->dictionary->numElements); + *ret = tuple_val(old)[2]; + } + } else if (is_list(old)) { + /* Find cons cell for identical value */ + Eterm* prev = &p->dictionary->data[hval]; + + for (tmp = *prev; tmp != NIL; prev = &TCDR(tmp), tmp = *prev) { + if (EQ(tuple_val(TCAR(tmp))[1], id)) { + *prev = TCDR(tmp); + *ret = tuple_val(TCAR(tmp))[2]; + --(p->dictionary->numElements); + } + } + + /* If there is only one element left in the list we must remove the list. */ + old = ARRAY_GET(p->dictionary, hval); + ASSERT(is_list(old)); + if (is_nil(TCDR(old))) { + array_put(&p->dictionary, hval, TCAR(old)); + } + } else if (is_not_nil(old)) { +#ifdef DEBUG + erts_fprintf(stderr, + "Process dictionary for process %T is broken, trying to " + "display term found in line %d:\n" + "%T\n", p->id, __LINE__, old); +#endif + erl_exit(1, "Damaged process dictionary found during erase/1."); + } + if ((range = HASH_RANGE(p->dictionary)) > INITIAL_SIZE && + range / 2 > (p->dictionary->numElements)) { + shrink(p, ret); + } +} + +static void pd_hash_erase_all(Process *p) +{ + if (p->dictionary != NULL) { + PD_FREE(p->dictionary, PD_SZ2BYTES(p->dictionary->size)); + p->dictionary = NULL; + } +} + +Eterm erts_pd_hash_get(Process *p, Eterm id) +{ + unsigned int hval; + Eterm tmp; + ProcDict *pd = p->dictionary; + + if (pd == NULL) + return am_undefined; + hval = pd_hash_value(pd, id); + tmp = ARRAY_GET(pd, hval); + if (is_boxed(tmp)) { /* Tuple */ + ASSERT(is_tuple(tmp)); + if (EQ(tuple_val(tmp)[1], id)) { + return tuple_val(tmp)[2]; + } + } else if (is_list(tmp)) { + for (; tmp != NIL && !EQ(tuple_val(TCAR(tmp))[1], id); tmp = TCDR(tmp)) { + ; + } + if (tmp != NIL) { + return tuple_val(TCAR(tmp))[2]; + } + } else if (is_not_nil(tmp)) { +#ifdef DEBUG + erts_fprintf(stderr, + "Process dictionary for process %T is broken, trying to " + "display term found in line %d:\n" + "%T\n", p->id, __LINE__, tmp); +#endif + erl_exit(1, "Damaged process dictionary found during get/1."); + } + return am_undefined; +} + +static Eterm pd_hash_get_keys(Process *p, Eterm value) +{ + Eterm *hp; + Eterm res = NIL; + ProcDict *pd = p->dictionary; + unsigned int i, num; + Eterm tmp, tmp2; + + if (pd == NULL) { + return res; + } + + num = HASH_RANGE(pd); + for (i = 0; i < num; ++i) { + tmp = ARRAY_GET(pd, i); + if (is_boxed(tmp)) { + ASSERT(is_tuple(tmp)); + if (EQ(tuple_val(tmp)[2], value)) { + hp = HAlloc(p, 2); + res = CONS(hp, tuple_val(tmp)[1], res); + } + } else if (is_list(tmp)) { + while (tmp != NIL) { + tmp2 = TCAR(tmp); + if (EQ(tuple_val(tmp2)[2], value)) { + hp = HAlloc(p, 2); + res = CONS(hp, tuple_val(tmp2)[1], res); + } + tmp = TCDR(tmp); + } + } + } + return res; +} + + +static Eterm +pd_hash_get_all(Process *p, ProcDict *pd) +{ + Eterm* hp; + Eterm res = NIL; + Eterm tmp, tmp2; + unsigned int i; + unsigned int num; + + if (pd == NULL) { + return res; + } + num = HASH_RANGE(pd); + hp = HAlloc(p, pd->numElements * 2); + + for (i = 0; i < num; ++i) { + tmp = ARRAY_GET(pd, i); + if (is_boxed(tmp)) { + ASSERT(is_tuple(tmp)); + res = CONS(hp, tmp, res); + hp += 2; + } else if (is_list(tmp)) { + while (tmp != NIL) { + tmp2 = TCAR(tmp); + res = CONS(hp, tmp2, res); + hp += 2; + tmp = TCDR(tmp); + } + } + } + return res; +} + +static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) +{ + unsigned int hval; + Eterm *hp; + Eterm tpl; + Eterm old; + Eterm tmp; + int needed; + int i = 0; +#ifdef DEBUG + Eterm *hp_limit; +#endif + + if (p->dictionary == NULL) { + /* Create it */ + array_put(&(p->dictionary), INITIAL_SIZE - 1, NIL); + p->dictionary->homeSize = INITIAL_SIZE; + } + hval = pd_hash_value(p->dictionary, id); + old = ARRAY_GET(p->dictionary, hval); + + /* + * Calculate the number of heap words needed and garbage + * collect if necessary. (Might be a slight overestimation.) + */ + needed = 3; /* {Key,Value} tuple */ + if (is_boxed(old)) { + /* + * We don't want to compare keys twice, so we'll always + * reserve the space for two CONS cells. + */ + needed += 2+2; + } else if (is_list(old)) { + i = 0; + for (tmp = old; tmp != NIL && !EQ(tuple_val(TCAR(tmp))[1], id); tmp = TCDR(tmp)) { + ++i; + } + if (is_nil(tmp)) { + i = -1; + needed += 2; + } else { + needed += 2*(i+1); + } + } + if (HeapWordsLeft(p) < needed) { + Eterm root[3]; + root[0] = id; + root[1] = value; + root[2] = old; + BUMP_REDS(p, erts_garbage_collect(p, needed, root, 3)); + id = root[0]; + value = root[1]; + old = root[2]; + } +#ifdef DEBUG + hp_limit = p->htop + needed; +#endif + + /* + * Create the {Key,Value} tuple. + */ + hp = HeapOnlyAlloc(p, 3); + tpl = TUPLE2(hp, id, value); + + /* + * Update the dictionary. + */ + if (is_nil(old)) { + array_put(&(p->dictionary), hval, tpl); + ++(p->dictionary->numElements); + } else if (is_boxed(old)) { + ASSERT(is_tuple(old)); + if (EQ(tuple_val(old)[1],id)) { + array_put(&(p->dictionary), hval, tpl); + return tuple_val(old)[2]; + } else { + hp = HeapOnlyAlloc(p, 4); + tmp = CONS(hp, old, NIL); + hp += 2; + ++(p->dictionary->numElements); + array_put(&(p->dictionary), hval, CONS(hp, tpl, tmp)); + hp += 2; + ASSERT(hp <= hp_limit); + } + } else if (is_list(old)) { + if (i == -1) { + /* + * New key. Simply prepend the tuple to the beginning of the list. + */ + hp = HeapOnlyAlloc(p, 2); + array_put(&(p->dictionary), hval, CONS(hp, tpl, old)); + hp += 2; + ASSERT(hp <= hp_limit); + ++(p->dictionary->numElements); + } else { + /* + * i = Number of CDRs to skip to reach the changed element in the list. + * + * Replace old value in list. To avoid pointers from the old generation + * to the new, we must rebuild the list from the beginning up to and + * including the changed element. + */ + Eterm nlist; + int j; + + hp = HeapOnlyAlloc(p, (i+1)*2); + + /* Find the list element to change. */ + for (j = 0, nlist = old; j < i; j++, nlist = TCDR(nlist)) { + ; + } + ASSERT(EQ(tuple_val(TCAR(nlist))[1], id)); + nlist = TCDR(nlist); /* Unchanged part of list. */ + + /* Rebuild list before the updated element. */ + for (tmp = old; i-- > 0; tmp = TCDR(tmp)) { + nlist = CONS(hp, TCAR(tmp), nlist); + hp += 2; + } + ASSERT(EQ(tuple_val(TCAR(tmp))[1], id)); + + /* Put the updated element first in the new list. */ + nlist = CONS(hp, tpl, nlist); + hp += 2; + ASSERT(hp <= hp_limit); + array_put(&(p->dictionary), hval, nlist); + return tuple_val(TCAR(tmp))[2]; + } + } else { +#ifdef DEBUG + erts_fprintf(stderr, + "Process dictionary for process %T is broken, trying to " + "display term found in line %d:\n" + "%T\n", p->id, __LINE__, old); +#endif + + erl_exit(1, "Damaged process dictionary found during put/2."); + } + if (HASH_RANGE(p->dictionary) <= p->dictionary->numElements) { + grow(p); + } + return am_undefined; +} + +/* + * Hash table utilities, rehashing + */ + +static void shrink(Process *p, Eterm* ret) +{ + unsigned int range = HASH_RANGE(p->dictionary); + unsigned int steps = (range*3) / 10; + Eterm hi, lo, tmp; + unsigned int i; + Eterm *hp; +#ifdef DEBUG + Eterm *hp_limit; +#endif + + if (range - steps < INITIAL_SIZE) { + steps = range - INITIAL_SIZE; + } + + for (i = 0; i < steps; ++i) { + ProcDict *pd = p->dictionary; + if (pd->splitPosition == 0) { + pd->homeSize /= 2; + pd->splitPosition = pd->homeSize; + } + --(pd->splitPosition); + hi = ARRAY_GET(pd, (pd->splitPosition + pd->homeSize)); + lo = ARRAY_GET(pd, pd->splitPosition); + if (hi != NIL) { + if (lo == NIL) { + array_put(&(p->dictionary), pd->splitPosition, hi); + } else { + int needed = 4; + if (is_list(hi) && is_list(lo)) { + needed = 2*list_length(hi); + } + if (HeapWordsLeft(p) < needed) { + BUMP_REDS(p, erts_garbage_collect(p, needed, ret, 1)); + hi = pd->data[(pd->splitPosition + pd->homeSize)]; + lo = pd->data[pd->splitPosition]; + } +#ifdef DEBUG + hp_limit = p->htop + needed; +#endif + if (is_tuple(lo)) { + if (is_tuple(hi)) { + hp = HeapOnlyAlloc(p, 4); + tmp = CONS(hp, hi, NIL); + hp += 2; + array_put(&(p->dictionary), pd->splitPosition, + CONS(hp,lo,tmp)); + hp += 2; + ASSERT(hp <= hp_limit); + } else { /* hi is a list */ + hp = HeapOnlyAlloc(p, 2); + array_put(&(p->dictionary), pd->splitPosition, + CONS(hp, lo, hi)); + hp += 2; + ASSERT(hp <= hp_limit); + } + } else { /* lo is a list */ + if (is_tuple(hi)) { + hp = HeapOnlyAlloc(p, 2); + array_put(&(p->dictionary), pd->splitPosition, + CONS(hp, hi, lo)); + hp += 2; + ASSERT(hp <= hp_limit); + + } else { /* Two lists */ + hp = HeapOnlyAlloc(p, needed); + for (tmp = hi; tmp != NIL; tmp = TCDR(tmp)) { + lo = CONS(hp, TCAR(tmp), lo); + hp += 2; + } + ASSERT(hp <= hp_limit); + array_put(&(p->dictionary), pd->splitPosition, lo); + } + } + } + } + array_put(&(p->dictionary), (pd->splitPosition + pd->homeSize), NIL); + } + if (HASH_RANGE(p->dictionary) <= (p->dictionary->size / 4)) { + array_shrink(&(p->dictionary), (HASH_RANGE(p->dictionary) * 3) / 2); + } +} + +static void grow(Process *p) +{ + unsigned int i,j; + unsigned int steps = p->dictionary->homeSize / 5; + Eterm l1,l2; + Eterm l; + Eterm *hp; + unsigned int pos; + unsigned int homeSize; + int needed = 0; + ProcDict *pd; +#ifdef DEBUG + Eterm *hp_limit; +#endif + + HDEBUGF(("grow: steps = %d", steps)); + if (steps == 0) + steps = 1; + /* Dont grow over MAX_HASH */ + if ((MAX_HASH - steps) <= HASH_RANGE(p->dictionary)) { + return; + } + + /* + * Calculate total number of heap words needed, and garbage collect + * if necessary. + */ + + pd = p->dictionary; + pos = pd->splitPosition; + homeSize = pd->homeSize; + for (i = 0; i < steps; ++i) { + if (pos == homeSize) { + homeSize *= 2; + pos = 0; + } + l = ARRAY_GET(pd, pos); + pos++; + if (is_not_tuple(l)) { + while (l != NIL) { + needed += 2; + l = TCDR(l); + } + } + } + if (HeapWordsLeft(p) < needed) { + BUMP_REDS(p, erts_garbage_collect(p, needed, 0, 0)); + } +#ifdef DEBUG + hp_limit = p->htop + needed; +#endif + + /* + * Now grow. + */ + + for (i = 0; i < steps; ++i) { + ProcDict *pd = p->dictionary; + if (pd->splitPosition == pd->homeSize) { + pd->homeSize *= 2; + pd->splitPosition = 0; + } + pos = pd->splitPosition; + ++pd->splitPosition; /* For the hashes */ + l = ARRAY_GET(pd, pos); + if (is_tuple(l)) { + if (pd_hash_value(pd, tuple_val(l)[1]) != pos) { + array_put(&(p->dictionary), pos + + p->dictionary->homeSize, l); + array_put(&(p->dictionary), pos, NIL); + } + } else { + l2 = NIL; + l1 = l; + for (j = 0; l1 != NIL; l1 = TCDR(l1)) + j += 2; + hp = HeapOnlyAlloc(p, j); + + while (l != NIL) { + if (pd_hash_value(pd, tuple_val(TCAR(l))[1]) == pos) + l1 = CONS(hp, TCAR(l), l1); + else + l2 = CONS(hp, TCAR(l), l2); + hp += 2; + l = TCDR(l); + } + if (l1 != NIL && TCDR(l1) == NIL) + l1 = TCAR(l1); + if (l2 != NIL && TCDR(l2) == NIL) + l2 = TCAR(l2); + ASSERT(hp <= hp_limit); + /* After array_put pd is no longer valid */ + array_put(&(p->dictionary), pos, l1); + array_put(&(p->dictionary), pos + + p->dictionary->homeSize, l2); + } + } + +#ifdef HARDDEBUG + dictionary_dump(p->dictionary,CERR); +#endif +} + +/* +** Array oriented operations +*/ + +static void array_shrink(ProcDict **ppd, unsigned int need) +{ + unsigned int siz = next_array_size(need); + + HDEBUGF(("array_shrink: size = %d, used = %d, need = %d", + (*ppd)->size, (*ppd)->used, need)); + + if (siz > (*ppd)->size) + return; /* Only shrink */ + + *ppd = PD_REALLOC(((void *) *ppd), + PD_SZ2BYTES((*ppd)->size), + PD_SZ2BYTES(siz)); + + (*ppd)->size = siz; + if ((*ppd)->size < (*ppd)->used) + (*ppd)->used = (*ppd)->size; +} + + +static Eterm array_put(ProcDict **ppdict, unsigned int ndx, Eterm term) +{ + unsigned int i; + Eterm ret; + if (*ppdict == NULL) { + Uint siz = next_array_size(ndx+1); + ProcDict *p; + + p = PD_ALLOC(PD_SZ2BYTES(siz)); + for (i = 0; i < siz; ++i) + p->data[i] = NIL; + p->size = siz; + p->homeSize = p->splitPosition = p->numElements = p->used = 0; + *ppdict = p; + } else if (ndx >= (*ppdict)->size) { + Uint osize = (*ppdict)->size; + Uint nsize = next_array_size(ndx+1); + *ppdict = PD_REALLOC(((void *) *ppdict), + PD_SZ2BYTES(osize), + PD_SZ2BYTES(nsize)); + for (i = osize; i < nsize; ++i) + (*ppdict)->data[i] = NIL; + (*ppdict)->size = nsize; + } + ret = (*ppdict)->data[ndx]; + (*ppdict)->data[ndx] = term; + if ((ndx + 1) > (*ppdict)->used) + (*ppdict)->used = ndx + 1; +#ifdef HARDDEBUG + HDEBUGF(("array_put: (*ppdict)->size = %d, (*ppdict)->used = %d, ndx = %d", + (*ppdict)->size, (*ppdict)->used, ndx)); + erts_fprintf(stderr, "%T", term); +#endif /* HARDDEBUG */ + return ret; +} + +/* +** Basic utilities +*/ + +static unsigned int pd_hash_value(ProcDict *pdict, Eterm term) +{ + Uint hash, high; + + hash = MAKE_HASH(term); + high = hash % (pdict->homeSize*2); + if (high >= HASH_RANGE(pdict)) + return hash % pdict->homeSize; + return high; +} + +static unsigned int next_array_size(unsigned int need) +{ + static unsigned int tab[] = + { + 10UL, + 20UL, + 40UL, + 80UL, + 160UL, + 320UL, + 640UL, + 1280UL, + 2560UL, + 5120UL, + 10240UL, + 20480UL, + 40960UL, + 81920UL, + 163840UL, + 327680UL, + 655360UL, + 1310720UL, + 2621440UL, + 5242880UL, + 10485760UL, + 20971520UL, + 41943040UL, + 83886080UL, + 167772160UL, + 335544320UL, + 671088640UL, + 1342177280UL, + 2684354560UL + }; + int hi = sizeof(tab) / sizeof(Uint) - 1; + int lo = 1; + int cur = 4; + + while (hi >= lo) { + if (tab[cur] >= need && tab[cur - 1] < need) + return tab[cur]; + if (tab[cur] > need) + hi = cur - 1; + else + lo = cur + 1; + cur = (hi + lo) / 2; + } + return need; +} + + +/* +** Debug functions +*/ +#ifdef DEBUG + +static void pd_check(ProcDict *pd) +{ + unsigned int i; + Uint num; + if (pd == NULL) + return; + ASSERT(pd->size >= pd->used); + ASSERT(HASH_RANGE(pd) <= MAX_HASH); + for (i = 0, num = 0; i < pd->used; ++i) { + Eterm t = pd->data[i]; + if (is_nil(t)) { + continue; + } else if (is_tuple(t)) { + ++num; + ASSERT(arityval(*tuple_val(t)) == 2); + continue; + } else if (is_list(t)) { + while (t != NIL) { + ++num; + ASSERT(is_tuple(TCAR(t))); + ASSERT(arityval(*(tuple_val(TCAR(t)))) == 2); + t = TCDR(t); + } + continue; + } else { + erl_exit(1, + "Found tag 0x%08x in process dictionary at position %d", + (unsigned long) t, (int) i); + } + } + ASSERT(num == pd->numElements); + ASSERT(pd->splitPosition <= pd->homeSize); +} + +#endif /* DEBUG */ + + +#ifdef HARDDEBUG + +static int hdebugf(char *format, ...) +{ + va_list ap; + + erts_fprintf(stderr, "DEBUG: %s:%d :", hdebugf_file, hdebugf_line); + va_start(ap, format); + erts_vfprintf(stderr, format, ap); + va_end(ap); + erts_fprintf(stderr, "\n"); + return 0; +} + +#endif /* HARDDEBUG */ + |