/* * %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% */ /* General purpose Memory allocator for fixed block size objects */ /* This allocater is at least an order of magnitude faster than malloc() */ #define NOPERBLOCK 20 #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "sys.h" #include "erl_vm.h" #include "global.h" #include "erl_db.h" #ifdef ERTS_ALC_N_MIN_A_FIXED_SIZE #if ERTS_ALC_MTA_FIXED_SIZE #include "erl_threads.h" #include "erl_smp.h" # ifdef ERTS_SMP # define FA_LOCK(FA) erts_smp_spin_lock(&(FA)->slck) # define FA_UNLOCK(FA) erts_smp_spin_unlock(&(FA)->slck) # else # define FA_LOCK(FA) erts_mtx_lock(&(FA)->mtx) # define FA_UNLOCK(FA) erts_mtx_unlock(&(FA)->mtx) # endif #else # define FA_LOCK(FA) # define FA_UNLOCK(FA) #endif typedef union {double d; long l;} align_t; typedef struct fix_alloc_block { struct fix_alloc_block *next; align_t mem[1]; } FixAllocBlock; typedef struct fix_alloc { Uint item_size; void *freelist; Uint no_free; Uint no_blocks; FixAllocBlock *blocks; #if ERTS_ALC_MTA_FIXED_SIZE # ifdef ERTS_SMP erts_smp_spinlock_t slck; # else erts_mtx_t mtx; # endif #endif } FixAlloc; static void *(*core_alloc)(Uint); static Uint xblk_sz; static FixAlloc **fa; #define FA_SZ (1 + ERTS_ALC_N_MAX_A_FIXED_SIZE - ERTS_ALC_N_MIN_A_FIXED_SIZE) #define FIX_IX(N) ((N) - ERTS_ALC_N_MIN_A_FIXED_SIZE) #define FIX_POOL_SZ(I_SZ) \ ((I_SZ)*NOPERBLOCK + sizeof(FixAllocBlock) - sizeof(align_t)) #if defined(DEBUG) && !ERTS_ALC_MTA_FIXED_SIZE static int first_time; #endif void erts_init_fix_alloc(Uint extra_block_size, void *(*alloc)(Uint)) { int i; xblk_sz = extra_block_size; core_alloc = alloc; fa = (FixAlloc **) (*core_alloc)(FA_SZ * sizeof(FixAlloc *)); if (!fa) erts_alloc_enomem(ERTS_ALC_T_UNDEF, FA_SZ * sizeof(FixAlloc *)); for (i = 0; i < FA_SZ; i++) fa[i] = NULL; #if defined(DEBUG) && !ERTS_ALC_MTA_FIXED_SIZE first_time = 1; #endif } Uint erts_get_fix_size(ErtsAlcType_t type) { Uint i = FIX_IX(ERTS_ALC_T2N(type)); return i < FA_SZ && fa[i] ? fa[i]->item_size : 0; } void erts_set_fix_size(ErtsAlcType_t type, Uint size) { Uint sz; Uint i; FixAlloc *fs; ErtsAlcType_t t_no = ERTS_ALC_T2N(type); sz = xblk_sz + size; #ifdef DEBUG ASSERT(ERTS_ALC_N_MIN_A_FIXED_SIZE <= t_no); ASSERT(t_no <= ERTS_ALC_N_MAX_A_FIXED_SIZE); #endif while (sz % sizeof(align_t) != 0) /* Alignment */ sz++; i = FIX_IX(t_no); fs = (FixAlloc *) (*core_alloc)(sizeof(FixAlloc)); if (!fs) erts_alloc_n_enomem(t_no, sizeof(FixAlloc)); fs->item_size = sz; fs->no_blocks = 0; fs->no_free = 0; fs->blocks = NULL; fs->freelist = NULL; if (fa[i]) erl_exit(-1, "Attempt to overwrite existing fix size (%d)", i); fa[i] = fs; #if ERTS_ALC_MTA_FIXED_SIZE #ifdef ERTS_SMP erts_smp_spinlock_init_x(&fs->slck, "fix_alloc", make_small(i)); #else erts_mtx_init_x(&fs->mtx, "fix_alloc", make_small(i)); #endif #endif } void erts_fix_info(ErtsAlcType_t type, ErtsFixInfo *efip) { Uint i; FixAlloc *f; #ifdef DEBUG FixAllocBlock *b; void *fp; #endif Uint real_item_size; ErtsAlcType_t t_no = ERTS_ALC_T2N(type); ASSERT(ERTS_ALC_N_MIN_A_FIXED_SIZE <= t_no); ASSERT(t_no <= ERTS_ALC_N_MAX_A_FIXED_SIZE); i = FIX_IX(t_no); f = fa[i]; efip->total = sizeof(FixAlloc *); efip->used = 0; if (!f) return; real_item_size = f->item_size - xblk_sz; FA_LOCK(f); efip->total += sizeof(FixAlloc); efip->total += f->no_blocks*FIX_POOL_SZ(real_item_size); efip->used = efip->total - f->no_free*real_item_size; #ifdef DEBUG ASSERT(efip->total >= efip->used); for(i = 0, b = f->blocks; b; i++, b = b->next); ASSERT(f->no_blocks == i); for (i = 0, fp = f->freelist; fp; i++, fp = *((void **) fp)); ASSERT(f->no_free == i); #endif FA_UNLOCK(f); } void erts_fix_free(ErtsAlcType_t t_no, void *extra, void* ptr) { Uint i; FixAlloc *f; ASSERT(ERTS_ALC_N_MIN_A_FIXED_SIZE <= t_no); ASSERT(t_no <= ERTS_ALC_N_MAX_A_FIXED_SIZE); i = FIX_IX(t_no); f = fa[i]; FA_LOCK(f); *((void **) ptr) = f->freelist; f->freelist = ptr; f->no_free++; FA_UNLOCK(f); } void *erts_fix_realloc(ErtsAlcType_t t_no, void *extra, void* ptr, Uint size) { erts_alc_fatal_error(ERTS_ALC_E_NOTSUP, ERTS_ALC_O_REALLOC, t_no); return NULL; } void *erts_fix_alloc(ErtsAlcType_t t_no, void *extra, Uint size) { void *ret; int i; FixAlloc *f; #if defined(DEBUG) && !ERTS_ALC_MTA_FIXED_SIZE ASSERT(ERTS_ALC_N_MIN_A_FIXED_SIZE <= t_no); ASSERT(t_no <= ERTS_ALC_N_MAX_A_FIXED_SIZE); if (first_time) { /* Check that all sizes have been initialized */ int i; for (i = 0; i < FA_SZ; i++) ASSERT(fa[i]); first_time = 0; } #endif i = FIX_IX(t_no); f = fa[i]; ASSERT(f); ASSERT(f->item_size >= size); FA_LOCK(f); if (f->freelist == NULL) { /* Gotta alloc some more mem */ char *ptr; FixAllocBlock *bl; Uint n; FA_UNLOCK(f); bl = (*core_alloc)(FIX_POOL_SZ(f->item_size)); if (!bl) return NULL; FA_LOCK(f); bl->next = f->blocks; /* link in first */ f->blocks = bl; n = NOPERBLOCK; ptr = (char *) &f->blocks->mem[0]; while(n--) { *((void **) ptr) = f->freelist; f->freelist = (void *) ptr; ptr += f->item_size; } #if !ERTS_ALC_MTA_FIXED_SIZE ASSERT(f->no_free == 0); #endif f->no_free += NOPERBLOCK; f->no_blocks++; } ret = f->freelist; f->freelist = *((void **) f->freelist); ASSERT(f->no_free > 0); f->no_free--; FA_UNLOCK(f); return ret; } #endif /* #ifdef ERTS_ALC_N_MIN_A_FIXED_SIZE */