diff options
Diffstat (limited to 'erts/emulator/beam/atom.c')
-rw-r--r-- | erts/emulator/beam/atom.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c new file mode 100644 index 0000000000..dfc3cde6a7 --- /dev/null +++ b/erts/emulator/beam/atom.c @@ -0,0 +1,354 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-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% + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_sys_driver.h" +#include "erl_vm.h" +#include "global.h" +#include "hash.h" +#include "atom.h" + + +#define ATOM_SIZE 3000 + +IndexTable erts_atom_table; /* The index table */ + +#include "erl_smp.h" + +static erts_smp_rwmtx_t atom_table_lock; + +#define atom_read_lock() erts_smp_rwmtx_rlock(&atom_table_lock) +#define atom_read_unlock() erts_smp_rwmtx_runlock(&atom_table_lock) +#define atom_write_lock() erts_smp_rwmtx_rwlock(&atom_table_lock) +#define atom_write_unlock() erts_smp_rwmtx_rwunlock(&atom_table_lock) +#define atom_init_lock() erts_smp_rwmtx_init(&atom_table_lock, \ + "atom_tab") +#if 0 +#define ERTS_ATOM_PUT_OPS_STAT +#endif +#ifdef ERTS_ATOM_PUT_OPS_STAT +static erts_smp_atomic_t atom_put_ops; +#endif + +/* Functions for allocating space for the ext of atoms. We do not + * use malloc for each atom to prevent excessive memory fragmentation + */ + +typedef struct _atom_text { + struct _atom_text* next; + unsigned char text[ATOM_TEXT_SIZE]; +} AtomText; + +static AtomText* text_list; /* List of text buffers */ +static byte *atom_text_pos; +static byte *atom_text_end; +static Uint reserved_atom_space; /* Total amount of atom text space */ +static Uint atom_space; /* Amount of atom text space used */ + +/* + * Print info about atom tables + */ +void atom_info(int to, void *to_arg) +{ + int lock = !ERTS_IS_CRASH_DUMPING; + if (lock) + atom_read_lock(); + index_info(to, to_arg, &erts_atom_table); +#ifdef ERTS_ATOM_PUT_OPS_STAT + erts_print(to, to_arg, "atom_put_ops: %ld\n", + erts_smp_atomic_read(&atom_put_ops)); +#endif + + if (lock) + atom_read_unlock(); +} + +/* + * Allocate an atom text segment. + */ +static void +more_atom_space(void) +{ + AtomText* ptr; + + ptr = (AtomText*) erts_alloc(ERTS_ALC_T_ATOM_TXT, sizeof(AtomText)); + + ptr->next = text_list; + text_list = ptr; + + atom_text_pos = ptr->text; + atom_text_end = atom_text_pos + ATOM_TEXT_SIZE; + reserved_atom_space += sizeof(AtomText); + + VERBOSE(DEBUG_SYSTEM,("Allocated %d atom space\n",ATOM_TEXT_SIZE)); +} + +/* + * Allocate string space within an atom text segment. + */ + +static byte* +atom_text_alloc(int bytes) +{ + byte *res; + + ASSERT(bytes <= MAX_ATOM_LENGTH); + if (atom_text_pos + bytes >= atom_text_end) { + more_atom_space(); + } + res = atom_text_pos; + atom_text_pos += bytes; + atom_space += bytes; + return res; +} + +/* + * Calculate atom hash value (using the hash algorithm + * hashpjw from the Dragon Book). + */ + +static HashValue +atom_hash(Atom* obj) +{ + byte* p = obj->name; + int len = obj->len; + HashValue h = 0, g; + + while(len--) { + h = (h << 4) + *p++; + if ((g = h & 0xf0000000)) { + h ^= (g >> 24); + h ^= g; + } + } + return h; +} + + +static int +atom_cmp(Atom* tmpl, Atom* obj) +{ + if (tmpl->len == obj->len && + sys_memcmp(tmpl->name, obj->name, tmpl->len) == 0) + return 0; + return 1; +} + + +static Atom* +atom_alloc(Atom* tmpl) +{ + Atom* obj = (Atom*) erts_alloc(ERTS_ALC_T_ATOM, sizeof(Atom)); + + obj->name = atom_text_alloc(tmpl->len); + sys_memcpy(obj->name, tmpl->name, tmpl->len); + obj->len = tmpl->len; + obj->slot.index = -1; + + /* + * Precompute ordinal value of first 3 bytes + 7 bits. + * This is used by utils.c:cmp_atoms(). + * We cannot use the full 32 bits of the first 4 bytes, + * since we use the sign of the difference between two + * ordinal values to represent their relative order. + */ + { + unsigned char c[4]; + int i; + int j; + + j = (tmpl->len < 4) ? tmpl->len : 4; + for(i = 0; i < j; ++i) + c[i] = tmpl->name[i]; + for(; i < 4; ++i) + c[i] = '\0'; + obj->ord0 = (c[0] << 23) + (c[1] << 15) + (c[2] << 7) + (c[3] >> 1); + } + return obj; +} + +static void +atom_free(Atom* obj) +{ + erts_free(ERTS_ALC_T_ATOM, (void*) obj); +} + +Eterm +am_atom_put(const char* name, int len) +{ + Atom a; + Eterm ret; + int aix; + + /* + * Silently truncate the atom if it is too long. Overlong atoms + * could occur in situations where we have no good way to return + * an error, such as in the I/O system. (Unfortunately, many + * drivers don't check for errors.) + * + * If an error should be produced for overlong atoms (such in + * list_to_atom/1), the caller should check the length before + * calling this function. + */ + if (len > MAX_ATOM_LENGTH) { + len = MAX_ATOM_LENGTH; + } +#ifdef ERTS_ATOM_PUT_OPS_STAT + erts_smp_atomic_inc(&atom_put_ops); +#endif + a.len = len; + a.name = (byte*)name; + atom_read_lock(); + aix = index_get(&erts_atom_table, (void*) &a); + atom_read_unlock(); + if (aix >= 0) + ret = make_atom(aix); + else { + atom_write_lock(); + ret = make_atom(index_put(&erts_atom_table, (void*) &a)); + atom_write_unlock(); + } + return ret; +} + + +int atom_table_size(void) +{ + int ret; +#ifdef ERTS_SMP + int lock = !ERTS_IS_CRASH_DUMPING; + if (lock) + atom_read_lock(); +#endif + ret = erts_atom_table.entries; +#ifdef ERTS_SMP + if (lock) + atom_read_unlock(); +#endif + return ret; +} + +int atom_table_sz(void) +{ + int ret; +#ifdef ERTS_SMP + int lock = !ERTS_IS_CRASH_DUMPING; + if (lock) + atom_read_lock(); +#endif + ret = index_table_sz(&erts_atom_table); +#ifdef ERTS_SMP + if (lock) + atom_read_unlock(); +#endif + return ret; +} + +int +erts_atom_get(const char *name, int len, Eterm* ap) +{ + Atom a; + int i; + int res; + + a.len = len; + a.name = (byte *)name; + atom_read_lock(); + i = index_get(&erts_atom_table, (void*) &a); + res = i < 0 ? 0 : (*ap = make_atom(i), 1); + atom_read_unlock(); + return res; +} + +void +erts_atom_get_text_space_sizes(Uint *reserved, Uint *used) +{ +#ifdef ERTS_SMP + int lock = !ERTS_IS_CRASH_DUMPING; + if (lock) + atom_read_lock(); +#endif + if (reserved) + *reserved = reserved_atom_space; + if (used) + *used = atom_space; +#ifdef ERTS_SMP + if (lock) + atom_read_unlock(); +#endif +} + +void +init_atom_table(void) +{ + HashFunctions f; + int i; + Atom a; + +#ifdef ERTS_ATOM_PUT_OPS_STAT + erts_smp_atomic_init(&atom_put_ops, 0); +#endif + + atom_init_lock(); + f.hash = (H_FUN) atom_hash; + f.cmp = (HCMP_FUN) atom_cmp; + f.alloc = (HALLOC_FUN) atom_alloc; + f.free = (HFREE_FUN) atom_free; + + atom_text_pos = NULL; + atom_text_end = NULL; + reserved_atom_space = 0; + atom_space = 0; + text_list = NULL; + + erts_index_init(ERTS_ALC_T_ATOM_TABLE, &erts_atom_table, + "atom_tab", ATOM_SIZE, ATOM_LIMIT, f); + more_atom_space(); + + /* Ordinary atoms */ + for (i = 0; erl_atom_names[i] != 0; i++) { + int ix; + a.len = strlen(erl_atom_names[i]); + a.name = (byte*)erl_atom_names[i]; + a.slot.index = i; + ix = index_put(&erts_atom_table, (void*) &a); + atom_text_pos -= a.len; + atom_space -= a.len; + atom_tab(ix)->name = (byte*)erl_atom_names[i]; + } +} + +void +dump_atoms(int to, void *to_arg) +{ + int i = erts_atom_table.entries; + + /* + * Print out the atom table starting from the end. + */ + while (--i >= 0) { + if (erts_index_lookup(&erts_atom_table, i)) { + erts_print(to, to_arg, "%T\n", make_atom(i)); + } + } +} |