/erts/emulator/beam/atom.c
C | 358 lines | 252 code | 51 blank | 55 comment | 24 complexity | e340308d66c7850d2a990755e17d3ce6 MD5 | raw file
1/*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1996-2010. All Rights Reserved.
5 *
6 * The contents of this file are subject to the Erlang Public License,
7 * Version 1.1, (the "License"); you may not use this file except in
8 * compliance with the License. You should have received a copy of the
9 * Erlang Public License along with this software. If not, it can be
10 * retrieved online at http://www.erlang.org/.
11 *
12 * Software distributed under the License is distributed on an "AS IS"
13 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14 * the License for the specific language governing rights and limitations
15 * under the License.
16 *
17 * %CopyrightEnd%
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "sys.h"
25#include "erl_sys_driver.h"
26#include "erl_vm.h"
27#include "global.h"
28#include "hash.h"
29#include "atom.h"
30
31
32#define ATOM_SIZE 3000
33
34IndexTable erts_atom_table; /* The index table */
35
36#include "erl_smp.h"
37
38static erts_smp_rwmtx_t atom_table_lock;
39
40#define atom_read_lock() erts_smp_rwmtx_rlock(&atom_table_lock)
41#define atom_read_unlock() erts_smp_rwmtx_runlock(&atom_table_lock)
42#define atom_write_lock() erts_smp_rwmtx_rwlock(&atom_table_lock)
43#define atom_write_unlock() erts_smp_rwmtx_rwunlock(&atom_table_lock)
44
45#if 0
46#define ERTS_ATOM_PUT_OPS_STAT
47#endif
48#ifdef ERTS_ATOM_PUT_OPS_STAT
49static erts_smp_atomic_t atom_put_ops;
50#endif
51
52/* Functions for allocating space for the ext of atoms. We do not
53 * use malloc for each atom to prevent excessive memory fragmentation
54 */
55
56typedef struct _atom_text {
57 struct _atom_text* next;
58 unsigned char text[ATOM_TEXT_SIZE];
59} AtomText;
60
61static AtomText* text_list; /* List of text buffers */
62static byte *atom_text_pos;
63static byte *atom_text_end;
64static Uint reserved_atom_space; /* Total amount of atom text space */
65static Uint atom_space; /* Amount of atom text space used */
66
67/*
68 * Print info about atom tables
69 */
70void atom_info(int to, void *to_arg)
71{
72 int lock = !ERTS_IS_CRASH_DUMPING;
73 if (lock)
74 atom_read_lock();
75 index_info(to, to_arg, &erts_atom_table);
76#ifdef ERTS_ATOM_PUT_OPS_STAT
77 erts_print(to, to_arg, "atom_put_ops: %ld\n",
78 erts_smp_atomic_read(&atom_put_ops));
79#endif
80
81 if (lock)
82 atom_read_unlock();
83}
84
85/*
86 * Allocate an atom text segment.
87 */
88static void
89more_atom_space(void)
90{
91 AtomText* ptr;
92
93 ptr = (AtomText*) erts_alloc(ERTS_ALC_T_ATOM_TXT, sizeof(AtomText));
94
95 ptr->next = text_list;
96 text_list = ptr;
97
98 atom_text_pos = ptr->text;
99 atom_text_end = atom_text_pos + ATOM_TEXT_SIZE;
100 reserved_atom_space += sizeof(AtomText);
101
102 VERBOSE(DEBUG_SYSTEM,("Allocated %d atom space\n",ATOM_TEXT_SIZE));
103}
104
105/*
106 * Allocate string space within an atom text segment.
107 */
108
109static byte*
110atom_text_alloc(int bytes)
111{
112 byte *res;
113
114 ASSERT(bytes <= MAX_ATOM_LENGTH);
115 if (atom_text_pos + bytes >= atom_text_end) {
116 more_atom_space();
117 }
118 res = atom_text_pos;
119 atom_text_pos += bytes;
120 atom_space += bytes;
121 return res;
122}
123
124/*
125 * Calculate atom hash value (using the hash algorithm
126 * hashpjw from the Dragon Book).
127 */
128
129static HashValue
130atom_hash(Atom* obj)
131{
132 byte* p = obj->name;
133 int len = obj->len;
134 HashValue h = 0, g;
135
136 while(len--) {
137 h = (h << 4) + *p++;
138 if ((g = h & 0xf0000000)) {
139 h ^= (g >> 24);
140 h ^= g;
141 }
142 }
143 return h;
144}
145
146
147static int
148atom_cmp(Atom* tmpl, Atom* obj)
149{
150 if (tmpl->len == obj->len &&
151 sys_memcmp(tmpl->name, obj->name, tmpl->len) == 0)
152 return 0;
153 return 1;
154}
155
156
157static Atom*
158atom_alloc(Atom* tmpl)
159{
160 Atom* obj = (Atom*) erts_alloc(ERTS_ALC_T_ATOM, sizeof(Atom));
161
162 obj->name = atom_text_alloc(tmpl->len);
163 sys_memcpy(obj->name, tmpl->name, tmpl->len);
164 obj->len = tmpl->len;
165 obj->slot.index = -1;
166
167 /*
168 * Precompute ordinal value of first 3 bytes + 7 bits.
169 * This is used by utils.c:cmp_atoms().
170 * We cannot use the full 32 bits of the first 4 bytes,
171 * since we use the sign of the difference between two
172 * ordinal values to represent their relative order.
173 */
174 {
175 unsigned char c[4];
176 int i;
177 int j;
178
179 j = (tmpl->len < 4) ? tmpl->len : 4;
180 for(i = 0; i < j; ++i)
181 c[i] = tmpl->name[i];
182 for(; i < 4; ++i)
183 c[i] = '\0';
184 obj->ord0 = (c[0] << 23) + (c[1] << 15) + (c[2] << 7) + (c[3] >> 1);
185 }
186 return obj;
187}
188
189static void
190atom_free(Atom* obj)
191{
192 erts_free(ERTS_ALC_T_ATOM, (void*) obj);
193}
194
195Eterm
196am_atom_put(const char* name, int len)
197{
198 Atom a;
199 Eterm ret;
200 int aix;
201
202 /*
203 * Silently truncate the atom if it is too long. Overlong atoms
204 * could occur in situations where we have no good way to return
205 * an error, such as in the I/O system. (Unfortunately, many
206 * drivers don't check for errors.)
207 *
208 * If an error should be produced for overlong atoms (such in
209 * list_to_atom/1), the caller should check the length before
210 * calling this function.
211 */
212 if (len > MAX_ATOM_LENGTH) {
213 len = MAX_ATOM_LENGTH;
214 }
215#ifdef ERTS_ATOM_PUT_OPS_STAT
216 erts_smp_atomic_inc(&atom_put_ops);
217#endif
218 a.len = len;
219 a.name = (byte*)name;
220 atom_read_lock();
221 aix = index_get(&erts_atom_table, (void*) &a);
222 atom_read_unlock();
223 if (aix >= 0)
224 ret = make_atom(aix);
225 else {
226 atom_write_lock();
227 ret = make_atom(index_put(&erts_atom_table, (void*) &a));
228 atom_write_unlock();
229 }
230 return ret;
231}
232
233
234int atom_table_size(void)
235{
236 int ret;
237#ifdef ERTS_SMP
238 int lock = !ERTS_IS_CRASH_DUMPING;
239 if (lock)
240 atom_read_lock();
241#endif
242 ret = erts_atom_table.entries;
243#ifdef ERTS_SMP
244 if (lock)
245 atom_read_unlock();
246#endif
247 return ret;
248}
249
250int atom_table_sz(void)
251{
252 int ret;
253#ifdef ERTS_SMP
254 int lock = !ERTS_IS_CRASH_DUMPING;
255 if (lock)
256 atom_read_lock();
257#endif
258 ret = index_table_sz(&erts_atom_table);
259#ifdef ERTS_SMP
260 if (lock)
261 atom_read_unlock();
262#endif
263 return ret;
264}
265
266int
267erts_atom_get(const char *name, int len, Eterm* ap)
268{
269 Atom a;
270 int i;
271 int res;
272
273 a.len = len;
274 a.name = (byte *)name;
275 atom_read_lock();
276 i = index_get(&erts_atom_table, (void*) &a);
277 res = i < 0 ? 0 : (*ap = make_atom(i), 1);
278 atom_read_unlock();
279 return res;
280}
281
282void
283erts_atom_get_text_space_sizes(Uint *reserved, Uint *used)
284{
285#ifdef ERTS_SMP
286 int lock = !ERTS_IS_CRASH_DUMPING;
287 if (lock)
288 atom_read_lock();
289#endif
290 if (reserved)
291 *reserved = reserved_atom_space;
292 if (used)
293 *used = atom_space;
294#ifdef ERTS_SMP
295 if (lock)
296 atom_read_unlock();
297#endif
298}
299
300void
301init_atom_table(void)
302{
303 HashFunctions f;
304 int i;
305 Atom a;
306 erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
307
308 rwmtx_opt.type = ERTS_SMP_RWMTX_TYPE_FREQUENT_READ;
309 rwmtx_opt.lived = ERTS_SMP_RWMTX_LONG_LIVED;
310
311#ifdef ERTS_ATOM_PUT_OPS_STAT
312 erts_smp_atomic_init(&atom_put_ops, 0);
313#endif
314
315 erts_smp_rwmtx_init_opt(&atom_table_lock, &rwmtx_opt, "atom_tab");
316
317 f.hash = (H_FUN) atom_hash;
318 f.cmp = (HCMP_FUN) atom_cmp;
319 f.alloc = (HALLOC_FUN) atom_alloc;
320 f.free = (HFREE_FUN) atom_free;
321
322 atom_text_pos = NULL;
323 atom_text_end = NULL;
324 reserved_atom_space = 0;
325 atom_space = 0;
326 text_list = NULL;
327
328 erts_index_init(ERTS_ALC_T_ATOM_TABLE, &erts_atom_table,
329 "atom_tab", ATOM_SIZE, erts_atom_table_size, f);
330 more_atom_space();
331
332 /* Ordinary atoms */
333 for (i = 0; erl_atom_names[i] != 0; i++) {
334 int ix;
335 a.len = strlen(erl_atom_names[i]);
336 a.name = (byte*)erl_atom_names[i];
337 a.slot.index = i;
338 ix = index_put(&erts_atom_table, (void*) &a);
339 atom_text_pos -= a.len;
340 atom_space -= a.len;
341 atom_tab(ix)->name = (byte*)erl_atom_names[i];
342 }
343}
344
345void
346dump_atoms(int to, void *to_arg)
347{
348 int i = erts_atom_table.entries;
349
350 /*
351 * Print out the atom table starting from the end.
352 */
353 while (--i >= 0) {
354 if (erts_index_lookup(&erts_atom_table, i)) {
355 erts_print(to, to_arg, "%T\n", make_atom(i));
356 }
357 }
358}