📄 elib_malloc.c
字号:
/* ``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 via the world wide web 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. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' * * $Id$ *//*** Description: Faster malloc().*/#ifdef HAVE_CONFIG_H# include "config.h"#endif#include "sys.h"#ifdef ENABLE_ELIB_MALLOC#undef THREAD_SAFE_ELIB_MALLOC#ifdef USE_THREADS#define THREAD_SAFE_ELIB_MALLOC 1#else#define THREAD_SAFE_ELIB_MALLOC 0#endif#include "erl_driver.h"#include "erl_threads.h"#include "elib_stat.h"#include <stdio.h>#include <stdlib.h>/* To avoid clobbering of names becaure of reclaim on VxWorks, we undefine all possible malloc, calloc etc. */#undef malloc#undef calloc#undef free#undef realloc#define ELIB_INLINE /* inline all possible functions */#ifndef ELIB_ALIGN#define ELIB_ALIGN sizeof(double)#endif#ifndef ELIB_HEAP_SIZE#define ELIB_HEAP_SIZE (64*1024) /* Default 64K */#endif#ifndef ELIB_HEAP_INCREAMENT#define ELIB_HEAP_INCREAMENT (32*1024) /* Default 32K */#endif#ifndef ELIB_FAILURE#define ELIB_FAILURE abort()#endif#undef ASSERT#ifdef DEBUG#define ASSERT(B) \ ((void) ((B) ? 1 : (fprintf(stderr, "%s:%d: Assertion failed: %s\n", \ __FILE__, __LINE__, #B), abort(), 0)))#else#define ASSERT(B) ((void) 1)#endif#ifndef USE_RECURSIVE_MALLOC_MUTEX#define USE_RECURSIVE_MALLOC_MUTEX 0#endif#if USE_RECURSIVE_MALLOC_MUTEXstatic erts_mtx_t malloc_mutex = ERTS_REC_MTX_INITER;#else /* #if USE_RECURSIVE_MALLOC_MUTEX */static erts_mtx_t malloc_mutex = ERTS_MTX_INITER;#if THREAD_SAFE_ELIB_MALLOCstatic erts_cnd_t malloc_cond = ERTS_CND_INITER;#endif#endif /* #if USE_RECURSIVE_MALLOC_MUTEX */typedef unsigned long EWord; /* Assume 32-bit in this implementation */typedef unsigned short EHalfWord; /* Assume 16-bit in this implementation */typedef unsigned char EByte; /* Assume 8-bit byte */#define elib_printf fprintf#define elib_putc fputc#if defined(__STDC__) || defined(__WIN32__)#define CONCAT(x,y) x##y#else#define CONCAT(x,y) x/**/y#endif#ifdef ELIB_DEBUG#define ELIB_PREFIX(fun, args) CONCAT(elib__,fun) args#else#define ELIB_PREFIX(fun, args) CONCAT(elib_,fun) args#endif#if defined(__STDC__)void *ELIB_PREFIX(malloc, (size_t));void *ELIB_PREFIX(calloc, (size_t, size_t));void ELIB_PREFIX(cfree, (EWord *));void ELIB_PREFIX(free, (EWord *));void *ELIB_PREFIX(realloc, (EWord *, size_t));void* ELIB_PREFIX(memresize, (EWord *, int));void* ELIB_PREFIX(memalign, (int, int));void* ELIB_PREFIX(valloc, (int));void* ELIB_PREFIX(pvalloc, (int));int ELIB_PREFIX(memsize, (EWord *));/* Extern interfaces used by VxWorks */size_t elib_sizeof(void *);void elib_init(EWord *, EWord);void elib_force_init(EWord *, EWord);#endif#if defined(__STDC__)/* define prototypes for missing */void* memalign(size_t a, size_t s);void* pvalloc(size_t nb);void* memresize(void *p, int nb);int memsize(void *p);#endif/* bytes to pages */#define PAGES(x) (((x)+page_size-1) / page_size)#define PAGE_ALIGN(p) ((char*)((((EWord)(p))+page_size-1)&~(page_size-1)))/* bytes to words */#define WORDS(x) (((x)+sizeof(EWord)-1) / sizeof(EWord))/* Align an address */#define ALIGN(p) ((EWord*)((((EWord)(p)+ELIB_ALIGN-1)&~(ELIB_ALIGN-1))))/* Calculate the size needed to keep alignment */#define ALIGN_BSZ(nb) ((nb+sizeof(EWord)+ELIB_ALIGN-1) & ~(ELIB_ALIGN-1))#define ALIGN_WSZ(nb) WORDS(ALIGN_BSZ(nb))#define ALIGN_SIZE(nb) (ALIGN_WSZ(nb) - 1)/* PARAMETERS */#if defined(ELIB_HEAP_SBRK)#undef PAGE_SIZE/* Get the system page size (NEED MORE DEFINES HERE) */#ifdef _SC_PAGESIZE#define PAGE_SIZE sysconf(_SC_PAGESIZE)#elif defined(_MSC_VER)# ifdef _M_ALPHA# define PAGE_SIZE 0x2000# else# define PAGE_SIZE 0x1000# endif#else#define PAGE_SIZE getpagesize()#endif#define ELIB_EXPAND(need) expand_sbrk(need)static FUNCTION(int, expand_sbrk, (EWord));#elif defined(ELIB_HEAP_FIXED)#define PAGE_SIZE 1024#define ELIB_EXPAND(need) -1static EWord fix_heap[WORDS(ELIB_HEAP_SIZE)];#elif defined(ELIB_HEAP_USER)#define PAGE_SIZE 1024#define ELIB_EXPAND(need) -1#else#error "ELIB HEAP TYPE NOT SET"#endif#define STAT_ALLOCED_BLOCK(SZ) \do { \ tot_allocated += (SZ); \ if (max_allocated < tot_allocated) \ max_allocated = tot_allocated; \} while (0)#define STAT_FREED_BLOCK(SZ) \do { \ tot_allocated -= (SZ); \} while (0)static int max_allocated = 0;static int tot_allocated = 0;static EWord* eheap; /* Align heap start */static EWord* eheap_top; /* Point to end of heap */EWord page_size = 0; /* Set by elib_init */#if defined(ELIB_DEBUG) || defined(DEBUG)#define ALIGN_CHECK(a, p) \ do { \ if ((EWord)(p) & (a-1)) { \ elib_printf(stderr, \ "RUNTIME ERROR: bad alignment (0x%lx:%d:%d)\n", \ (unsigned long) (p), (int) a, __LINE__); \ ELIB_FAILURE; \ } \ } while(0)#define ELIB_ALIGN_CHECK(p) ALIGN_CHECK(ELIB_ALIGN, p)#else#define ALIGN_CHECK(a, p)#define ELIB_ALIGN_CHECK(p)#endif#define DYNAMIC 32/*** Free block layout** 1 1 30** +--------------------------+** |F|P| Size |** +--------------------------+**** Where F is the free bit** P is the free above bit** Size is messured in words and does not include the hdr word**** If block is on the free list the size is also stored last in the block.** */typedef struct _free_block FreeBlock;struct _free_block { EWord hdr; Uint flags; FreeBlock* parent; FreeBlock* left; FreeBlock* right; EWord v[1];};typedef struct _allocated_block { EWord hdr; EWord v[5];} AllocatedBlock;/* * Interface to tree routines. */typedef Uint Block_t;static Block_t* get_free_block(Uint);static void link_free_block(Block_t *);static void unlink_free_block(Block_t *del);#define FREE_BIT 0x80000000#define FREE_ABOVE_BIT 0x40000000#define SIZE_MASK 0x3fffffff /* 2^30 words = 2^32 bytes *//* Work on both FreeBlock and AllocatedBlock */#define SIZEOF(p) ((p)->hdr & SIZE_MASK)#define IS_FREE(p) (((p)->hdr & FREE_BIT) != 0)#define IS_FREE_ABOVE(p) (((p)->hdr & FREE_ABOVE_BIT) != 0)/* Given that we have a free block above find its size */#define SIZEOF_ABOVE(p) *(((EWord*) (p)) - 1)#define MIN_BLOCK_SIZE (sizeof(FreeBlock)/sizeof(EWord))#define MIN_WORD_SIZE (MIN_BLOCK_SIZE-1)#define MIN_BYTE_SIZE (sizeof(FreeBlock)-sizeof(EWord))#define MIN_ALIGN_SIZE ALIGN_SIZE(MIN_BYTE_SIZE)static AllocatedBlock* heap_head = 0;static AllocatedBlock* heap_tail = 0;static EWord eheap_size = 0;static int heap_locked;static int elib_need_init = 1;#if THREAD_SAFE_ELIB_MALLOCstatic int elib_is_initing = 0;#endiftypedef FreeBlock RBTree_t;static RBTree_t* root = NULL;static FUNCTION(void, deallocate, (AllocatedBlock*, int));/* * Unlink a free block */#define mark_allocated(p, szp) do { \ (p)->hdr = ((p)->hdr & FREE_ABOVE_BIT) | (szp); \ (p)->v[szp] &= ~FREE_ABOVE_BIT; \ } while(0)#define mark_free(p, szp) do { \ (p)->hdr = FREE_BIT | (szp); \ ((FreeBlock *)p)->v[szp-sizeof(FreeBlock)/sizeof(EWord)+1] = (szp); \ } while(0)#if 0/* Help macros to log2 */#define LOG_1(x) (((x) > 1) ? 1 : 0)#define LOG_2(x) (((x) > 3) ? 2+LOG_1((x) >> 2) : LOG_1(x))#define LOG_4(x) (((x) > 15) ? 4+LOG_2((x) >> 4) : LOG_2(x))#define LOG_8(x) (((x) > 255) ? 8+LOG_4((x)>>8) : LOG_4(x))#define LOG_16(x) (((x) > 65535) ? 16+LOG_8((x)>>16) : LOG_8(x))#define log2(x) LOG_16(x)#endif/* * Split a block to be allocated. * Mark block as ALLOCATED and clear * FREE_ABOVE_BIT on next block * * nw is SIZE aligned and szp is SIZE aligned + 1 */static voidsplit_block(FreeBlock* p, EWord nw, EWord szp){ EWord szq; FreeBlock* q; szq = szp - nw; /* Preserve FREE_ABOVE bit in p->hdr !!! */ if (szq >= MIN_ALIGN_SIZE+1) { szq--; p->hdr = (p->hdr & FREE_ABOVE_BIT) | nw; q = (FreeBlock*) (((EWord*) p) + nw + 1); mark_free(q, szq); link_free_block((Block_t *) q); q = (FreeBlock*) (((EWord*) q) + szq + 1); q->hdr |= FREE_ABOVE_BIT; } else { mark_allocated((AllocatedBlock*)p, szp); }}/* * Find a free block */static FreeBlock*alloc_block(EWord nw){ for (;;) { FreeBlock* p = (FreeBlock *) get_free_block(nw); if (p != NULL) { return p; } else if (ELIB_EXPAND(nw+MIN_WORD_SIZE)) { return 0; } }}size_t elib_sizeof(void *p){ AllocatedBlock* pp; if (p != 0) { pp = (AllocatedBlock*) (((char *)p)-1); return SIZEOF(pp); } return 0;}static void locked_elib_init(EWord*, EWord);static void init_elib_malloc(EWord*, EWord);/*** Initialize the elib** The addr and sz is only used when compiled with EXPAND_ADDR *//* Not static, this is used by VxWorks */void elib_init(EWord* addr, EWord sz){ if (!elib_need_init) return; erts_mtx_lock(&malloc_mutex); locked_elib_init(addr, sz); erts_mtx_unlock(&malloc_mutex);}static void locked_elib_init(EWord* addr, EWord sz){ if (!elib_need_init) return;#if THREAD_SAFE_ELIB_MALLOC#if !USE_RECURSIVE_MALLOC_MUTEX { static erts_tid_t initer_tid; if(elib_is_initing) { if(erts_equal_tids(initer_tid, erts_thr_self())) return; /* Wait until initializing thread is done with initialization */ while(elib_need_init) erts_cnd_wait(&malloc_cond, &malloc_mutex); return; } else { initer_tid = erts_thr_self(); elib_is_initing = 1; } }#else if(elib_is_initing) return; elib_is_initing = 1;#endif#endif /* #if THREAD_SAFE_ELIB_MALLOC */ /* Do the actual initialization of the malloc implementation */ init_elib_malloc(addr, sz);#if THREAD_SAFE_ELIB_MALLOC#if !USE_RECURSIVE_MALLOC_MUTEX erts_mtx_unlock(&malloc_mutex);#endif /* Recursive calls to malloc are allowed here... */ erts_mtx_set_forksafe(&malloc_mutex);#if !USE_RECURSIVE_MALLOC_MUTEX erts_mtx_lock(&malloc_mutex); elib_is_initing = 0;#endif#endif /* #if THREAD_SAFE_ELIB_MALLOC */ elib_need_init = 0;#if THREAD_SAFE_ELIB_MALLOC && !USE_RECURSIVE_MALLOC_MUTEX erts_cnd_broadcast(&malloc_cond);#endif}static void init_elib_malloc(EWord* addr, EWord sz){ int i; FreeBlock* freep; EWord tmp_sz;#ifdef ELIB_HEAP_SBRK char* top; EWord n;#endif max_allocated = 0; tot_allocated = 0; root = NULL; /* Get the page size (may involve system call!!!) */ page_size = PAGE_SIZE;#if defined(ELIB_HEAP_SBRK) sz = PAGES(ELIB_HEAP_SIZE)*page_size; if ((top = (char*) sbrk(0)) == (char*)-1) { elib_printf(stderr, "could not initialize elib, sbrk(0)"); ELIB_FAILURE; } n = PAGE_ALIGN(top) - top; if ((top = (char*) sbrk(n)) == (char*)-1) { elib_printf(stderr, "could not initialize elib, sbrk(n)"); ELIB_FAILURE; } if ((eheap = (EWord*) sbrk(sz)) == (EWord*)-1) { elib_printf(stderr, "could not initialize elib, sbrk(SIZE)"); ELIB_FAILURE; } sz = WORDS(ELIB_HEAP_SIZE);#elif defined(ELIB_HEAP_FIXED) eheap = fix_heap; sz = WORDS(ELIB_HEAP_SIZE);#elif defined(ELIB_HEAP_USER) eheap = addr; sz = WORDS(sz);#else return -1;#endif eheap_size = 0; /* Make sure that the first word of the heap_head is aligned */ addr = ALIGN(eheap+1); sz -= ((addr - 1) - eheap); /* Subtract unusable size */ eheap_top = eheap = addr - 1; /* Set new aligned heap start */ eheap_top[sz-1] = 0; /* Heap stop mark */ addr = eheap; heap_head = (AllocatedBlock*) addr; heap_head->hdr = MIN_ALIGN_SIZE; for (i = 0; i < MIN_ALIGN_SIZE; i++) heap_head->v[i] = 0; addr += (MIN_ALIGN_SIZE+1); freep = (FreeBlock*) addr; tmp_sz = sz - (((MIN_ALIGN_SIZE+1) + MIN_BLOCK_SIZE) + 1 + 1); mark_free(freep, tmp_sz); link_free_block((Block_t *) freep); /* No need to align heap tail */ heap_tail = (AllocatedBlock*) &eheap_top[sz-MIN_BLOCK_SIZE-1]; heap_tail->hdr = FREE_ABOVE_BIT | MIN_WORD_SIZE; heap_tail->v[0] = 0; heap_tail->v[1] = 0; heap_tail->v[2] = 0; eheap_top += sz; eheap_size += sz; heap_locked = 0;}#ifdef ELIB_HEAP_USERvoid elib_force_init(EWord* addr, EWord sz){ elib_need_init = 1; elib_init(addr,sz);}#endif#ifdef ELIB_HEAP_SBRK/*** need in number of words (should include head and tail words)*/static int expand_sbrk(EWord sz){ EWord* p; EWord bytes = sz * sizeof(EWord); EWord size; AllocatedBlock* tail; if (bytes < ELIB_HEAP_SIZE) size = PAGES(ELIB_HEAP_INCREAMENT)*page_size; else size = PAGES(bytes)*page_size; if ((p = (EWord*) sbrk(size)) == ((EWord*) -1)) return -1; if (p != eheap_top) { elib_printf(stderr, "panic: sbrk moved\n"); ELIB_FAILURE; } sz = WORDS(size);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -