├── sysdeps ├── generic │ ├── atomic.h │ ├── thread-st.h │ └── malloc-machine.h ├── solaris │ ├── thread-st.h │ └── malloc-machine.h ├── sproc │ ├── thread-st.h │ └── malloc-machine.h └── pthread │ ├── thread-st.h │ └── malloc-machine.h ├── examples └── list │ ├── Makefile │ ├── README.md │ └── list.cpp ├── lran2.h ├── ChangeLog ├── COPYRIGHT ├── fmalloc.cpp ├── tst-independent-alloc.c ├── t-test.h ├── fmptr.hpp ├── fmalloc.hpp ├── t-test2.c ├── malloc-private.h ├── README.md ├── t-test1.c ├── Makefile ├── README ├── malloc-2.8.3.h └── ptmalloc3.c /sysdeps/generic/atomic.h: -------------------------------------------------------------------------------- 1 | /* Empty placeholder */ 2 | -------------------------------------------------------------------------------- /examples/list/Makefile: -------------------------------------------------------------------------------- 1 | PROGS = fmalloc_list 2 | 3 | CLEANFILES = $(PROGS) *.o 4 | 5 | SRCDIR ?= ./ 6 | 7 | FMALLOCDIR ?= ../../ 8 | 9 | CC=g++ 10 | 11 | NO_MAN= 12 | CPPFLAGS = -O3 -pipe -g 13 | CPPFLAGS += -Werror -Wall -Wunused-function 14 | CPPFLAGS += -Wextra 15 | CPPFLAGS += -I$(SRCDIR) -I$(FMALLOCDIR) 16 | CPPFLAGS += -std=c++11 17 | CPPFLAGS += -L$(FMALLOCDIR) 18 | 19 | LDLIBS += -lfmalloc -lpthread 20 | LDFLAGS += -L$(FMALLOCDIR) 21 | 22 | SRCS = list.cpp 23 | OBJS += $(SRCS:.cpp=.o) 24 | 25 | .PHONY: all 26 | all: $(PROGS) 27 | 28 | $(PROGS): $(OBJS) 29 | $(CC) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) 30 | 31 | clean: 32 | -@rm -rf $(CLEANFILES) 33 | -------------------------------------------------------------------------------- /examples/list/README.md: -------------------------------------------------------------------------------- 1 | # A persistent list implementation using fmalloc 2 | 3 | If you do not compile the fmalloc library yet, please do that before starting this demo. 4 | 5 | ``` 6 | $ make 7 | g++ -O3 -pipe -g -Werror -Wall -Wunused-function -Wextra -I./ -I../../ -std=c++11 -L../../ -c -o list.o list.cpp 8 | g++ -O3 -pipe -g -Werror -Wall -Wunused-function -Wextra -I./ -I../../ -std=c++11 -L../../ -L../../ list.o -o fmalloc_list -lfmalloc -lpthread 9 | 10 | $ dd if=/dev/zero of=./list.dat bs=4k count=16 11 | 16+0 records in 12 | 16+0 records out 13 | 65536 bytes (66 kB, 64 KiB) copied, 0.000341662 s, 192 MB/s 14 | 15 | $ ./fmalloc_list ./list.dat 16 | writing list data... 17 | done. 18 | 19 | $ ./fmalloc_list ./list.dat 20 | reading list data... 21 | 0 22 | 1 23 | 2 24 | 3 25 | 4 26 | 5 27 | 6 28 | 7 29 | 8 30 | 9 31 | done. 32 | ``` 33 | -------------------------------------------------------------------------------- /sysdeps/generic/thread-st.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id:$ 3 | * Generic version: no threads. 4 | * by Wolfram Gloger 2004 5 | */ 6 | 7 | #include 8 | 9 | struct thread_st { 10 | char *sp; /* stack pointer, can be 0 */ 11 | void (*func)(struct thread_st* st); /* must be set by user */ 12 | int id; 13 | int flags; 14 | struct user_data u; 15 | }; 16 | 17 | static void 18 | thread_init(void) 19 | { 20 | printf("No threads.\n"); 21 | } 22 | 23 | /* Create a thread. */ 24 | static int 25 | thread_create(struct thread_st *st) 26 | { 27 | st->flags = 0; 28 | st->id = 1; 29 | st->func(st); 30 | return 0; 31 | } 32 | 33 | /* Wait for one of several subthreads to finish. */ 34 | static void 35 | wait_for_thread(struct thread_st st[], int n_thr, 36 | int (*end_thr)(struct thread_st*)) 37 | { 38 | int i; 39 | for(i=0; iv[j] = x; 29 | } 30 | d->x = (IA*x + IC) % LRAN2_MAX; 31 | d->y = d->x; 32 | } 33 | 34 | #ifdef __GNUC__ 35 | __inline__ 36 | #endif 37 | static long 38 | lran2(struct lran2_st* d) 39 | { 40 | int j = (d->y % 97); 41 | 42 | d->y = d->v[j]; 43 | d->x = (IA*d->x + IC) % LRAN2_MAX; 44 | d->v[j] = d->x; 45 | return d->y; 46 | } 47 | 48 | #undef IA 49 | #undef IC 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2006-05-31 Wolfram Gloger 2 | 3 | * sysdeps/pthread/malloc-machine.h (mutex_unlock): Unlock needs 4 | full memory barrier (thanks Bart Robinson). 5 | 6 | 2006-03-31 Wolfram Gloger 7 | 8 | * ptmalloc3.c (public_iCALLOc, public_iCOMALLOc): New functions. 9 | 10 | 2006-03-30 Wolfram Gloger 11 | 12 | * malloc/malloc.c: Upgrade to version pre-2.8.4-29mar06. 13 | * malloc/malloc-private.h: New fields in malloc_state. 14 | 15 | 2004-03-29 Wolfram Gloger 16 | 17 | * malloc/malloc.c (mmap_alloc): Use page_align instead of 18 | granularity_align. 19 | (mmap_resize): Likewise. 20 | 21 | * malloc/ptmalloc3.c (ptmalloc_init): 22 | Add MALLOC_GRANULARITY_ and synonymous MALLOC_TOP_PAD_ environment 23 | parameters. 24 | 25 | 2006-03-25 Wolfram Gloger 26 | 27 | * malloc/malloc-private.h: New file. 28 | 29 | 2005-12-31 Wolfram Gloger 30 | 31 | * malloc/malloc.c: Imported from Doug Lea's malloc-2.8.3. 32 | * malloc/malloc.h-2.8.3.h: Likewise. 33 | * malloc/ptmalloc3.c: New file. 34 | -------------------------------------------------------------------------------- /sysdeps/solaris/thread-st.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id:$ 3 | * Solaris version 4 | * by Wolfram Gloger 2004 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #ifndef STACKSIZE 11 | #define STACKSIZE 32768 12 | #endif 13 | 14 | struct thread_st { 15 | char *sp; /* stack pointer, can be 0 */ 16 | void (*func)(struct thread_st* st); /* must be set by user */ 17 | thread_id id; 18 | int flags; 19 | struct user_data u; 20 | }; 21 | 22 | static void 23 | thread_init(void) 24 | { 25 | printf("Using Solaris threads.\n"); 26 | } 27 | 28 | static void * 29 | thread_wrapper(void *ptr) 30 | { 31 | struct thread_st *st = (struct thread_st*)ptr; 32 | 33 | /*printf("begin %p\n", st->sp);*/ 34 | st->func(st); 35 | /*printf("end %p\n", st->sp);*/ 36 | return NULL; 37 | } 38 | 39 | /* Create a thread. */ 40 | static int 41 | thread_create(struct thread_st *st) 42 | { 43 | st->flags = 0; 44 | if(!st->sp) 45 | st->sp = malloc(STACKSIZE); 46 | if(!st->sp) return -1; 47 | thr_create(st->sp, STACKSIZE, thread_wrapper, st, THR_NEW_LWP, &st->id); 48 | return 0; 49 | } 50 | 51 | /* Wait for one of several subthreads to finish. */ 52 | static void 53 | wait_for_thread(struct thread_st st[], int n_thr, 54 | int (*end_thr)(struct thread_st*)) 55 | { 56 | int i; 57 | thread_t id; 58 | 59 | thr_join(0, &id, NULL); 60 | for(i=0; i 2 | 3 | /* list implementation */ 4 | struct node { 5 | int val; 6 | fm_ptr next; 7 | } __attribute__((packed)); 8 | 9 | static void list_append(struct node *head, struct node *newnode) 10 | { 11 | struct node *n = head; 12 | while (n->next) { 13 | n = n->next; 14 | } 15 | n->next = newnode; 16 | } 17 | 18 | /* app reserved super block */ 19 | struct app_super { 20 | fm_ptr head; 21 | }; 22 | 23 | /* example append operation */ 24 | static void append_nodes(struct node *head) 25 | { 26 | int i; 27 | printf("writing list data...\n"); 28 | for (i = 0; i < 10; i++) { 29 | struct node *n = (struct node *) fmalloc(sizeof(struct node)); 30 | n->val = i; 31 | n->next = NULL; 32 | list_append(head, n); 33 | } 34 | printf("done.\n"); 35 | } 36 | 37 | /* example read operation */ 38 | static void read_nodes(struct node *head) 39 | { 40 | struct node *n = head->next; 41 | printf("reading list data...\n"); 42 | while (n) { 43 | printf("%d\n", n->val); 44 | n = n->next; 45 | } 46 | printf("done.\n"); 47 | } 48 | 49 | int main(int argc, char const* argv[]) 50 | { 51 | struct app_super *super; 52 | bool init = false; 53 | struct fm_info *fi; 54 | 55 | if (argc < 2) { 56 | printf("please specify a data file\n"); 57 | exit(1); 58 | } 59 | 60 | fi = fmalloc_init(argv[1], &init); 61 | fmalloc_set_target(fi); 62 | 63 | /* fmalloc reserves 4KB ~ 8KB for app for locating super block */ 64 | super = (struct app_super *) ((unsigned long) fi->mem + PAGE_SIZE); 65 | 66 | if (init) { 67 | super->head = (struct node *) fmalloc(sizeof(struct node)); 68 | append_nodes(super->head); 69 | } else { 70 | if (super->head) { 71 | read_nodes(super->head); 72 | } 73 | } 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /sysdeps/sproc/thread-st.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id:$ 3 | * sproc version 4 | * by Wolfram Gloger 2001, 2004, 2006 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef STACKSIZE 13 | #define STACKSIZE 32768 14 | #endif 15 | 16 | struct thread_st { 17 | char *sp; /* stack pointer, can be 0 */ 18 | void (*func)(struct thread_st* st); /* must be set by user */ 19 | int id; 20 | int flags; 21 | struct user_data u; 22 | }; 23 | 24 | static void 25 | thread_init(void) 26 | { 27 | printf("Using sproc() threads.\n"); 28 | } 29 | 30 | static void 31 | thread_wrapper(void *ptr, size_t stack_len) 32 | { 33 | struct thread_st *st = (struct thread_st*)ptr; 34 | 35 | /*printf("begin %p\n", st->sp);*/ 36 | st->func(st); 37 | /*printf("end %p\n", st->sp);*/ 38 | } 39 | 40 | /* Create a thread. */ 41 | static int 42 | thread_create(struct thread_st *st) 43 | { 44 | st->flags = 0; 45 | if(!st->sp) 46 | st->sp = malloc(STACKSIZE); 47 | if(!st->sp) return -1; 48 | st->id = sprocsp(thread_wrapper, PR_SALL, st, st->sp+STACKSIZE, STACKSIZE); 49 | if(st->id < 0) { 50 | return -1; 51 | } 52 | return 0; 53 | } 54 | 55 | /* Wait for one of several subthreads to finish. */ 56 | static void 57 | wait_for_thread(struct thread_st st[], int n_thr, 58 | int (*end_thr)(struct thread_st*)) 59 | { 60 | int i; 61 | int id; 62 | 63 | int status = 0; 64 | id = wait(&status); 65 | if(status != 0) { 66 | if(WIFSIGNALED(status)) 67 | printf("thread %id terminated by signal %d\n", 68 | id, WTERMSIG(status)); 69 | else 70 | printf("thread %id exited with status %d\n", 71 | id, WEXITSTATUS(status)); 72 | } 73 | for(i=0; i. 5 | 6 | Permission to use, copy, modify, distribute, and sell this software 7 | and its documentation for any purpose is hereby granted without fee, 8 | provided that (i) the above copyright notices and this permission 9 | notice appear in all copies of the software and related documentation, 10 | and (ii) the name of Wolfram Gloger may not be used in any advertising 11 | or publicity relating to the software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 15 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 16 | 17 | IN NO EVENT SHALL WOLFRAM GLOGER BE LIABLE FOR ANY SPECIAL, 18 | INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY 19 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 20 | WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY 21 | OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22 | PERFORMANCE OF THIS SOFTWARE. 23 | */ 24 | 25 | #ifndef _SOLARIS_MALLOC_MACHINE_H 26 | #define _SOLARIS_MALLOC_MACHINE_H 27 | 28 | #include 29 | 30 | typedef thread_t thread_id; 31 | 32 | #define MUTEX_INITIALIZER { 0 } 33 | #define mutex_init(m) mutex_init(m, USYNC_THREAD, NULL) 34 | 35 | /* 36 | * Hack for thread-specific data on Solaris. We can't use thr_setspecific 37 | * because that function calls malloc() itself. 38 | */ 39 | typedef void *tsd_key_t[256]; 40 | #define tsd_key_create(key, destr) do { \ 41 | int i; \ 42 | for(i=0; i<256; i++) (*key)[i] = 0; \ 43 | } while(0) 44 | #define tsd_setspecific(key, data) (key[(unsigned)thr_self() % 256] = (data)) 45 | #define tsd_getspecific(key, vptr) (vptr = key[(unsigned)thr_self() % 256]) 46 | 47 | #define thread_atfork(prepare, parent, child) do {} while(0) 48 | 49 | #include 50 | 51 | #endif /* !defined(_SOLARIS_MALLOC_MACHINE_H) */ 52 | -------------------------------------------------------------------------------- /fmalloc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2020 Kenichi Yasukata 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | 21 | __thread uint64_t __fm_addr_base = 0; 22 | 23 | extern void do_ptmalloc_init(unsigned long chunk_size); 24 | 25 | /* init routine */ 26 | struct fm_info *fmalloc_init(const char *filepath, bool *init) 27 | { 28 | struct stat st; 29 | struct fm_super *s; 30 | void *mem; 31 | uint64_t *magicp; 32 | int fd; 33 | size_t len; 34 | 35 | if (stat(filepath, &st) < 0) { 36 | perror("stat"); 37 | exit(1); 38 | } 39 | 40 | len = st.st_size; 41 | 42 | fd = open(filepath, O_RDWR, 0644); 43 | if (fd < 0) { 44 | perror("open"); 45 | fprintf(stderr, "file: %s (%d)\n", filepath, fd); 46 | exit(1); 47 | } 48 | 49 | mem = mmap(0, len, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); 50 | assert(mem != MAP_FAILED); 51 | 52 | s = (struct fm_super *) mem; 53 | 54 | magicp = (uint64_t *) mem; 55 | if (*magicp != FMALLOC_MAGIC) { 56 | int *initialized; 57 | 58 | s->magic = FMALLOC_MAGIC; 59 | s->set_total_size(len); 60 | s->bitmap_set(0); 61 | 62 | initialized = (int *)((uint64_t) mem + FMALLOC_OFF); 63 | *initialized = -1; 64 | *init = true; 65 | } 66 | 67 | __fm_addr_base = (uint64_t) mem; 68 | 69 | do_ptmalloc_init(s->chunk_size); 70 | 71 | return new fm_info(fd, mem, s); 72 | } 73 | 74 | void fmalloc_set_target(struct fm_info *fi) 75 | { 76 | __fm_addr_base = (uint64_t) fi->mem; 77 | } 78 | 79 | void *fmalloc(size_t size) 80 | { 81 | return dlmalloc(size); 82 | } 83 | 84 | void ffree(void *addr) 85 | { 86 | dlfree(addr); 87 | } 88 | -------------------------------------------------------------------------------- /sysdeps/sproc/malloc-machine.h: -------------------------------------------------------------------------------- 1 | /* Basic platform-independent macro definitions for mutexes, 2 | thread-specific data and parameters for malloc. 3 | SGI threads (sprocs) version. 4 | Copyright (C) 2004 Wolfram Gloger . 5 | 6 | Permission to use, copy, modify, distribute, and sell this software 7 | and its documentation for any purpose is hereby granted without fee, 8 | provided that (i) the above copyright notices and this permission 9 | notice appear in all copies of the software and related documentation, 10 | and (ii) the name of Wolfram Gloger may not be used in any advertising 11 | or publicity relating to the software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 15 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 16 | 17 | IN NO EVENT SHALL WOLFRAM GLOGER BE LIABLE FOR ANY SPECIAL, 18 | INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY 19 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 20 | WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY 21 | OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22 | PERFORMANCE OF THIS SOFTWARE. 23 | */ 24 | 25 | #ifndef _SPROC_MALLOC_MACHINE_H 26 | #define _SPROC_MALLOC_MACHINE_H 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | typedef abilock_t mutex_t; 34 | 35 | #define MUTEX_INITIALIZER { 0 } 36 | #define mutex_init(m) init_lock(m) 37 | #define mutex_lock(m) (spin_lock(m), 0) 38 | #define mutex_trylock(m) acquire_lock(m) 39 | #define mutex_unlock(m) release_lock(m) 40 | 41 | typedef int tsd_key_t; 42 | int tsd_key_next; 43 | #define tsd_key_create(key, destr) ((*key) = tsd_key_next++) 44 | #define tsd_setspecific(key, data) (((void **)(&PRDA->usr_prda))[key] = data) 45 | #define tsd_getspecific(key, vptr) (vptr = ((void **)(&PRDA->usr_prda))[key]) 46 | 47 | #define thread_atfork(prepare, parent, child) do {} while(0) 48 | 49 | #include 50 | 51 | #endif /* !defined(_SPROC_MALLOC_MACHINE_H) */ 52 | -------------------------------------------------------------------------------- /sysdeps/generic/malloc-machine.h: -------------------------------------------------------------------------------- 1 | /* Basic platform-independent macro definitions for mutexes, 2 | thread-specific data and parameters for malloc. 3 | Copyright (C) 2003 Free Software Foundation, Inc. 4 | This file is part of the GNU C Library. 5 | 6 | The GNU C Library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | The GNU C Library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with the GNU C Library; if not, write to the Free 18 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 19 | 02111-1307 USA. */ 20 | 21 | #ifndef _GENERIC_MALLOC_MACHINE_H 22 | #define _GENERIC_MALLOC_MACHINE_H 23 | 24 | #include 25 | 26 | #ifndef mutex_init /* No threads, provide dummy macros */ 27 | 28 | # define NO_THREADS 29 | 30 | /* The mutex functions used to do absolutely nothing, i.e. lock, 31 | trylock and unlock would always just return 0. However, even 32 | without any concurrently active threads, a mutex can be used 33 | legitimately as an `in use' flag. To make the code that is 34 | protected by a mutex async-signal safe, these macros would have to 35 | be based on atomic test-and-set operations, for example. */ 36 | typedef int mutex_t; 37 | 38 | # define mutex_init(m) (*(m) = 0) 39 | # define mutex_lock(m) ((*(m) = 1), 0) 40 | # define mutex_trylock(m) (*(m) ? 1 : ((*(m) = 1), 0)) 41 | # define mutex_unlock(m) (*(m) = 0) 42 | 43 | typedef void *tsd_key_t; 44 | # define tsd_key_create(key, destr) do {} while(0) 45 | # define tsd_setspecific(key, data) ((key) = (data)) 46 | # define tsd_getspecific(key, vptr) (vptr = (key)) 47 | 48 | # define thread_atfork(prepare, parent, child) do {} while(0) 49 | 50 | #endif /* !defined mutex_init */ 51 | 52 | #ifndef atomic_full_barrier 53 | # define atomic_full_barrier() __asm ("" ::: "memory") 54 | #endif 55 | 56 | #ifndef atomic_read_barrier 57 | # define atomic_read_barrier() atomic_full_barrier () 58 | #endif 59 | 60 | #ifndef atomic_write_barrier 61 | # define atomic_write_barrier() atomic_full_barrier () 62 | #endif 63 | 64 | #ifndef DEFAULT_TOP_PAD 65 | # define DEFAULT_TOP_PAD 131072 66 | #endif 67 | 68 | #endif /* !defined(_GENERIC_MALLOC_MACHINE_H) */ 69 | -------------------------------------------------------------------------------- /sysdeps/pthread/thread-st.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: thread-st.h$ 3 | * pthread version 4 | * by Wolfram Gloger 2004 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | pthread_cond_t finish_cond = PTHREAD_COND_INITIALIZER; 11 | pthread_mutex_t finish_mutex = PTHREAD_MUTEX_INITIALIZER; 12 | 13 | #ifndef USE_PTHREADS_STACKS 14 | #define USE_PTHREADS_STACKS 0 15 | #endif 16 | 17 | #ifndef STACKSIZE 18 | #define STACKSIZE 32768 19 | #endif 20 | 21 | struct thread_st { 22 | char *sp; /* stack pointer, can be 0 */ 23 | void (*func)(struct thread_st* st); /* must be set by user */ 24 | pthread_t id; 25 | int flags; 26 | struct user_data u; 27 | }; 28 | 29 | static void 30 | thread_init(void) 31 | { 32 | printf("Using posix threads.\n"); 33 | pthread_cond_init(&finish_cond, NULL); 34 | pthread_mutex_init(&finish_mutex, NULL); 35 | } 36 | 37 | static void * 38 | thread_wrapper(void *ptr) 39 | { 40 | struct thread_st *st = (struct thread_st*)ptr; 41 | 42 | /*printf("begin %p\n", st->sp);*/ 43 | st->func(st); 44 | pthread_mutex_lock(&finish_mutex); 45 | st->flags = 1; 46 | pthread_mutex_unlock(&finish_mutex); 47 | pthread_cond_signal(&finish_cond); 48 | /*printf("end %p\n", st->sp);*/ 49 | return NULL; 50 | } 51 | 52 | /* Create a thread. */ 53 | static int 54 | thread_create(struct thread_st *st) 55 | { 56 | st->flags = 0; 57 | { 58 | pthread_attr_t* attr_p = 0; 59 | #if USE_PTHREADS_STACKS 60 | pthread_attr_t attr; 61 | 62 | pthread_attr_init (&attr); 63 | if(!st->sp) 64 | st->sp = malloc(STACKSIZE+16); 65 | if(!st->sp) 66 | return -1; 67 | if(pthread_attr_setstacksize(&attr, STACKSIZE)) 68 | fprintf(stderr, "error setting stacksize"); 69 | else 70 | pthread_attr_setstackaddr(&attr, st->sp + STACKSIZE); 71 | /*printf("create %p\n", st->sp);*/ 72 | attr_p = &attr; 73 | #endif 74 | return pthread_create(&st->id, attr_p, thread_wrapper, st); 75 | } 76 | return 0; 77 | } 78 | 79 | /* Wait for one of several subthreads to finish. */ 80 | static void 81 | wait_for_thread(struct thread_st st[], int n_thr, 82 | int (*end_thr)(struct thread_st*)) 83 | { 84 | int i; 85 | 86 | pthread_mutex_lock(&finish_mutex); 87 | for(;;) { 88 | int term = 0; 89 | for(i=0; i 0) 101 | break; 102 | pthread_cond_wait(&finish_cond, &finish_mutex); 103 | } 104 | pthread_mutex_unlock(&finish_mutex); 105 | } 106 | 107 | /* 108 | * Local variables: 109 | * tab-width: 4 110 | * End: 111 | */ 112 | -------------------------------------------------------------------------------- /tst-independent-alloc.c: -------------------------------------------------------------------------------- 1 | /* Test for special independant_... allocation functions in ptmalloc3. 2 | Contributed by Wolfram Gloger , 2006. 3 | 4 | The GNU C Library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | 9 | The GNU C Library is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | Lesser General Public License for more details. 13 | 14 | You should have received a copy of the GNU Lesser General Public 15 | License along with the GNU C Library; if not, write to the Free 16 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 17 | 02111-1307 USA. */ 18 | 19 | #include 20 | 21 | #include "malloc-2.8.3.h" 22 | 23 | #define CSIZE 104 24 | 25 | static int errors = 0; 26 | 27 | static void 28 | merror (const char *msg) 29 | { 30 | ++errors; 31 | printf ("Error: %s\n", msg); 32 | } 33 | 34 | int 35 | main (void) 36 | { 37 | char *p1, **p2, **p3; 38 | int i, j; 39 | size_t sizes[10] = { 8, 104, 12, 333, 7, 4098, 119, 331, 1, 707 }; 40 | 41 | printf("---- Start:\n"); 42 | malloc_stats(); 43 | p1 = malloc (18); 44 | if (p1 == NULL) 45 | merror ("malloc (10) failed."); 46 | p2 = (char **)independent_calloc (25, CSIZE, 0); 47 | if (p2 == NULL) 48 | merror ("independent_calloc (25, ...) failed."); 49 | p3 = (char **)independent_comalloc(10, sizes, 0); 50 | if (p3 == NULL) 51 | merror ("independent_comalloc (10, ...) failed."); 52 | 53 | for (i=0; i<25; ++i) { 54 | for (j=0; j 0 25 | 26 | static void 27 | mem_init(unsigned char *ptr, unsigned long size) 28 | { 29 | unsigned long i, j; 30 | 31 | if(size == 0) return; 32 | for(i=0; i>8)) & 0xFF); 35 | } 36 | j = (unsigned long)ptr ^ (size-1); 37 | ptr[size-1] = ((j ^ (j>>8)) & 0xFF); 38 | } 39 | 40 | static int 41 | mem_check(unsigned char *ptr, unsigned long size) 42 | { 43 | unsigned long i, j; 44 | 45 | if(size == 0) return 0; 46 | for(i=0; i>8)) & 0xFF)) return 1; 49 | } 50 | j = (unsigned long)ptr ^ (size-1); 51 | if(ptr[size-1] != ((j ^ (j>>8)) & 0xFF)) return 2; 52 | return 0; 53 | } 54 | 55 | static int 56 | zero_check(unsigned* ptr, unsigned long size) 57 | { 58 | unsigned char* ptr2; 59 | 60 | while(size >= sizeof(*ptr)) { 61 | if(*ptr++ != 0) 62 | return -1; 63 | size -= sizeof(*ptr); 64 | } 65 | ptr2 = (unsigned char*)ptr; 66 | while(size > 0) { 67 | if(*ptr2++ != 0) 68 | return -1; 69 | --size; 70 | } 71 | return 0; 72 | } 73 | 74 | #endif /* TEST > 0 */ 75 | 76 | /* Allocate a bin with malloc(), realloc() or memalign(). r must be a 77 | random number >= 1024. */ 78 | 79 | static void 80 | bin_alloc(struct bin *m, unsigned long size, int r) 81 | { 82 | #if TEST > 0 83 | if(mem_check(m->ptr, m->size)) { 84 | printf("memory corrupt!\n"); 85 | exit(1); 86 | } 87 | #endif 88 | r %= 1024; 89 | /*printf("%d ", r);*/ 90 | if(r < 4) { /* memalign */ 91 | if(m->size > 0) free(m->ptr); 92 | m->ptr = (unsigned char *)memalign(sizeof(int) << r, size); 93 | } else if(r < 20) { /* calloc */ 94 | if(m->size > 0) free(m->ptr); 95 | m->ptr = (unsigned char *)calloc(size, 1); 96 | #if TEST > 0 97 | if(zero_check((unsigned*)m->ptr, size)) { 98 | long i; 99 | for(i=0; iptr[i] != 0) 101 | break; 102 | printf("calloc'ed memory non-zero (ptr=%p, i=%ld)!\n", m->ptr, i); 103 | exit(1); 104 | } 105 | #endif 106 | } else if(r < 100 && m->size < REALLOC_MAX) { /* realloc */ 107 | if(m->size == 0) m->ptr = NULL; 108 | m->ptr = realloc(m->ptr, size); 109 | } else { /* plain malloc */ 110 | if(m->size > 0) free(m->ptr); 111 | m->ptr = (unsigned char *)malloc(size); 112 | } 113 | if(!m->ptr) { 114 | printf("out of memory (r=%d, size=%ld)!\n", r, (long)size); 115 | exit(1); 116 | } 117 | m->size = size; 118 | #if TEST > 0 119 | mem_init(m->ptr, m->size); 120 | #endif 121 | } 122 | 123 | /* Free a bin. */ 124 | 125 | static void 126 | bin_free(struct bin *m) 127 | { 128 | if(m->size == 0) return; 129 | #if TEST > 0 130 | if(mem_check(m->ptr, m->size)) { 131 | printf("memory corrupt!\n"); 132 | exit(1); 133 | } 134 | #endif 135 | free(m->ptr); 136 | m->size = 0; 137 | } 138 | 139 | /* 140 | * Local variables: 141 | * tab-width: 4 142 | * End: 143 | */ 144 | -------------------------------------------------------------------------------- /fmptr.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2020 Kenichi Yasukata 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef _FMPTR_H 20 | #define _FMPTR_H 21 | 22 | #include 23 | #include 24 | 25 | extern __thread uint64_t __fm_addr_base; 26 | 27 | template 28 | class fm_ptr { 29 | private: 30 | uint64_t off; 31 | public: 32 | template 33 | U operator = (U p) { 34 | if (p) { 35 | this->off = (uint64_t) p - __fm_addr_base; 36 | return (U) (__fm_addr_base + this->off); 37 | } else { 38 | this->off = 0; 39 | return (U) 0; 40 | } 41 | } 42 | 43 | template 44 | operator U* () const { 45 | if (this->off) { 46 | return (U*) (__fm_addr_base + this->off); 47 | } else { 48 | return NULL; 49 | } 50 | } 51 | 52 | fm_ptr() : off(0) { 53 | } 54 | 55 | fm_ptr* operator & () { 56 | return (fm_ptr*) this; 57 | } 58 | 59 | T& operator * () { 60 | if (this->off) 61 | return *((T*) (__fm_addr_base + this->off)); 62 | else 63 | return NULL; 64 | } 65 | 66 | T* operator -> () { 67 | if (this->off) 68 | return (T*) (__fm_addr_base + this->off); 69 | else 70 | return NULL; 71 | } 72 | 73 | fm_ptr& operator = (fm_ptr& other) { 74 | this->off = other.off; 75 | return *this; 76 | } 77 | 78 | explicit operator bool () { 79 | return (bool) (this->off != 0); 80 | } 81 | 82 | #define op_overload(__op) \ 83 | template \ 84 | T* operator __op (U other) { \ 85 | if (this->off) \ 86 | return (T*) ((__fm_addr_base + this->off) __op (uint64_t) other); \ 87 | else \ 88 | return (T*) (0 __op other); \ 89 | } 90 | op_overload(+) 91 | op_overload(-) 92 | op_overload(*) 93 | op_overload(/) 94 | op_overload(|) 95 | op_overload(^) 96 | op_overload(<<) 97 | op_overload(>>) 98 | #undef op_overload 99 | 100 | #define cmp_overload(__op) \ 101 | bool operator __op (fm_ptr& other) { \ 102 | return (this->off __op other.off); \ 103 | } \ 104 | template \ 105 | bool operator __op (U other) { \ 106 | if (this->off == 0) \ 107 | return ((uint64_t) (0) __op (uint64_t) other); \ 108 | else \ 109 | return ((uint64_t)((U) (__fm_addr_base + this->off)) __op (uint64_t) other); \ 110 | } 111 | cmp_overload(==) 112 | cmp_overload(!=) 113 | cmp_overload(<) 114 | cmp_overload(<=) 115 | cmp_overload(>) 116 | cmp_overload(>=) 117 | #undef cmp_overload 118 | 119 | #define cast_overload(__op) \ 120 | explicit operator __op () { \ 121 | if (this->off) \ 122 | return (__op) (__fm_addr_base + this->off); \ 123 | else \ 124 | return 0; \ 125 | } 126 | cast_overload(short) 127 | cast_overload(int) 128 | cast_overload(long) 129 | cast_overload(long long) 130 | cast_overload(unsigned short) 131 | cast_overload(unsigned int) 132 | cast_overload(unsigned long) 133 | cast_overload(unsigned long long) 134 | #undef cast_overload 135 | } __attribute__((packed)); 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /fmalloc.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2020 Kenichi Yasukata 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #ifndef _FMALLOC_H 20 | #define _FMALLOC_H 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #ifndef USE_DL_PREFIX 36 | #define USE_DL_PREFIX 1 37 | #endif 38 | 39 | #include 40 | 41 | #include 42 | 43 | #ifndef PAGE_SIZE 44 | #define PAGE_SIZE (4096) 45 | #endif 46 | 47 | #ifndef FMALLOC_MAGIC 48 | #define FMALLOC_MAGIC (123456) 49 | #endif 50 | 51 | #define FMALLOC_OFF (PAGE_SIZE * 2) 52 | #define FMALLOC_MIN_CHUNK (1UL << 24) 53 | 54 | /* 55 | * data layout on a file 56 | * 57 | * 0 4KB 8KB end 58 | * |-- fm_super --|-- for app --|-- ... malloc ...--| 59 | * 60 | * 4KB ~ 8KB is reserved for app, and assumes to have 61 | * a pointer to a root object. 62 | */ 63 | 64 | /* 65 | * on-disk representation of super block 66 | * this always occupies first 4KB of a file 67 | */ 68 | struct fm_super { 69 | uint64_t magic; 70 | uint64_t total_size; 71 | uint64_t chunk_size; 72 | uint64_t num_chunk; 73 | uint8_t bm[0]; /* this must be at the end */ 74 | 75 | void set_total_size(uint64_t size) 76 | { 77 | this->total_size = (size / PAGE_SIZE) * PAGE_SIZE; 78 | if (total_size < FMALLOC_MIN_CHUNK) { 79 | num_chunk = 1; 80 | chunk_size = total_size; 81 | } else { 82 | num_chunk = (PAGE_SIZE - sizeof(struct fm_super)) * 8; 83 | chunk_size = (((size - FMALLOC_OFF) / PAGE_SIZE) * PAGE_SIZE) / num_chunk; 84 | if (chunk_size < FMALLOC_MIN_CHUNK) { 85 | chunk_size = FMALLOC_MIN_CHUNK; 86 | num_chunk = total_size / chunk_size; 87 | } 88 | } 89 | } 90 | 91 | void munmap_locked(void *mem) 92 | { 93 | int idx = m2i((void *)((uint64_t) this + FMALLOC_OFF), mem); 94 | bitmap_release(bm, idx); 95 | } 96 | 97 | void *mmap_locked(void) 98 | { 99 | int idx = bitmap_grab(bm); 100 | if (idx < 0) { 101 | fprintf(stderr, "bitmap_grab failed\n"); 102 | return MAP_FAILED; 103 | } 104 | return i2m(this, idx); 105 | } 106 | 107 | void bitmap_set(int idx) 108 | { 109 | bm[idx / 8] |= (1UL << (idx % 8)); 110 | } 111 | 112 | void bitmap_release(uint8_t *bm, int idx) 113 | { 114 | bm[idx / 8] &= ~(1UL << (idx % 8)); 115 | } 116 | 117 | int bitmap_grab(uint8_t *bm) 118 | { 119 | unsigned long i, j; 120 | for (i = 0; i < (total_size / chunk_size); i++) { 121 | for (j = 0; j < 8; j++) { 122 | if (num_chunk <= i * 8 + j) 123 | return -1; 124 | if (!(bm[i] & (1UL << j))) { 125 | bm[i] |= (1UL << j); 126 | return i * 8 + j; 127 | } 128 | } 129 | } 130 | return -1; 131 | } 132 | 133 | int m2i(void *mem, void *ptr) 134 | { 135 | return (int) (((uint64_t) ptr - (uint64_t) (mem)) / chunk_size); 136 | } 137 | 138 | void *i2m(void *mem, int idx) 139 | { 140 | return (void *) ((uint64_t) mem + (uint64_t) (chunk_size * idx)); 141 | } 142 | } __attribute__((packed)); 143 | 144 | /* in-memory reference to super block */ 145 | struct fm_info { 146 | int fd; 147 | void *mem; 148 | struct fm_super *s; 149 | 150 | fm_info(int _fd, void *_mem, struct fm_super *_s) : fd(_fd), mem(_mem), s(_s) 151 | { 152 | } 153 | }; 154 | 155 | struct fm_info *fmalloc_init(const char *filepath, bool *init); 156 | void fmalloc_set_target(struct fm_info *fi); 157 | 158 | void *fmalloc(size_t size); 159 | void ffree(void *addr); 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /sysdeps/pthread/malloc-machine.h: -------------------------------------------------------------------------------- 1 | /* Basic platform-independent macro definitions for mutexes, 2 | thread-specific data and parameters for malloc. 3 | Posix threads (pthreads) version. 4 | Copyright (C) 2004 Wolfram Gloger . 5 | 6 | Permission to use, copy, modify, distribute, and sell this software 7 | and its documentation for any purpose is hereby granted without fee, 8 | provided that (i) the above copyright notices and this permission 9 | notice appear in all copies of the software and related documentation, 10 | and (ii) the name of Wolfram Gloger may not be used in any advertising 11 | or publicity relating to the software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 15 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 16 | 17 | IN NO EVENT SHALL WOLFRAM GLOGER BE LIABLE FOR ANY SPECIAL, 18 | INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY 19 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 20 | WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY 21 | OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22 | PERFORMANCE OF THIS SOFTWARE. 23 | */ 24 | 25 | #ifndef _PTHREAD_MALLOC_MACHINE_H 26 | #define _PTHREAD_MALLOC_MACHINE_H 27 | 28 | #include 29 | 30 | #undef thread_atfork_static 31 | 32 | /* Use fast inline spinlocks with gcc. */ 33 | #if (defined __i386__ || defined __x86_64__) && defined __GNUC__ && \ 34 | !defined USE_NO_SPINLOCKS 35 | 36 | #include 37 | #include 38 | 39 | typedef struct { 40 | volatile unsigned int lock; 41 | int pad0_; 42 | } mutex_t; 43 | 44 | #define MUTEX_INITIALIZER { 0 } 45 | #define mutex_init(m) ((m)->lock = 0) 46 | static inline int mutex_lock(mutex_t *m) { 47 | int cnt = 0, r; 48 | struct timespec tm; 49 | 50 | for(;;) { 51 | __asm__ __volatile__ 52 | ("xchgl %0, %1" 53 | : "=r"(r), "=m"(m->lock) 54 | : "0"(1), "m"(m->lock) 55 | : "memory"); 56 | if(!r) 57 | return 0; 58 | if(cnt < 50) { 59 | sched_yield(); 60 | cnt++; 61 | } else { 62 | tm.tv_sec = 0; 63 | tm.tv_nsec = 2000001; 64 | nanosleep(&tm, NULL); 65 | cnt = 0; 66 | } 67 | } 68 | } 69 | static inline int mutex_trylock(mutex_t *m) { 70 | int r; 71 | 72 | __asm__ __volatile__ 73 | ("xchgl %0, %1" 74 | : "=r"(r), "=m"(m->lock) 75 | : "0"(1), "m"(m->lock) 76 | : "memory"); 77 | return r; 78 | } 79 | static inline int mutex_unlock(mutex_t *m) { 80 | __asm__ __volatile__ ("movl %1, %0" : "=m" (m->lock) : "g"(0) : "memory"); 81 | return 0; 82 | } 83 | 84 | #else 85 | 86 | /* Normal pthread mutex. */ 87 | typedef pthread_mutex_t mutex_t; 88 | 89 | #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER 90 | #define mutex_init(m) pthread_mutex_init(m, NULL) 91 | #define mutex_lock(m) pthread_mutex_lock(m) 92 | #define mutex_trylock(m) pthread_mutex_trylock(m) 93 | #define mutex_unlock(m) pthread_mutex_unlock(m) 94 | 95 | #endif /* (__i386__ || __x86_64__) && __GNUC__ && !USE_NO_SPINLOCKS */ 96 | 97 | /* thread specific data */ 98 | #if defined(__sgi) || defined(USE_TSD_DATA_HACK) 99 | 100 | /* Hack for thread-specific data, e.g. on Irix 6.x. We can't use 101 | pthread_setspecific because that function calls malloc() itself. 102 | The hack only works when pthread_t can be converted to an integral 103 | type. */ 104 | 105 | typedef void *tsd_key_t[256]; 106 | #define tsd_key_create(key, destr) do { \ 107 | int i; \ 108 | for(i=0; i<256; i++) (*key)[i] = 0; \ 109 | } while(0) 110 | #define tsd_setspecific(key, data) \ 111 | (key[(unsigned)pthread_self() % 256] = (data)) 112 | #define tsd_getspecific(key, vptr) \ 113 | (vptr = key[(unsigned)pthread_self() % 256]) 114 | 115 | #else 116 | 117 | typedef pthread_key_t tsd_key_t; 118 | 119 | #define tsd_key_create(key, destr) pthread_key_create(key, destr) 120 | #define tsd_setspecific(key, data) pthread_setspecific(key, data) 121 | #define tsd_getspecific(key, vptr) (vptr = pthread_getspecific(key)) 122 | 123 | #endif 124 | 125 | /* at fork */ 126 | #define thread_atfork(prepare, parent, child) \ 127 | pthread_atfork(prepare, parent, child) 128 | 129 | #include 130 | 131 | #endif /* !defined(_MALLOC_MACHINE_H) */ 132 | -------------------------------------------------------------------------------- /t-test2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: t-test2.c,v 1.2 2006/03/27 16:06:20 wg Exp $ 3 | * by Wolfram Gloger 1996-1999, 2001, 2004 4 | * A multi-thread test for malloc performance, maintaining a single 5 | * global pool of allocated bins. 6 | */ 7 | 8 | #if (defined __STDC__ && __STDC__) || defined __cplusplus 9 | # include 10 | #endif 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #if !USE_MALLOC 19 | #include 20 | #else 21 | #include "malloc-2.8.3.h" 22 | #endif 23 | 24 | #include "lran2.h" 25 | #include "t-test.h" 26 | 27 | struct user_data { 28 | int max; 29 | unsigned long size; 30 | long seed; 31 | }; 32 | #include "thread-st.h" 33 | #include "malloc-machine.h" /* for mutex */ 34 | 35 | #define N_TOTAL 10 36 | #ifndef N_THREADS 37 | #define N_THREADS 2 38 | #endif 39 | #ifndef N_TOTAL_PRINT 40 | #define N_TOTAL_PRINT 50 41 | #endif 42 | #define STACKSIZE 32768 43 | #ifndef MEMORY 44 | #define MEMORY 8000000l 45 | #endif 46 | #define SIZE 10000 47 | #define I_MAX 10000 48 | #define BINS_PER_BLOCK 20 49 | 50 | #define RANDOM(d,s) (lran2(d) % (s)) 51 | 52 | struct block { 53 | struct bin b[BINS_PER_BLOCK]; 54 | mutex_t mutex; 55 | } *blocks; 56 | 57 | int n_blocks; 58 | 59 | #if TEST > 0 60 | 61 | void 62 | bin_test(void) 63 | { 64 | int b, i; 65 | 66 | for(b=0; bu.seed); 90 | for(i=0; i<=st->u.max;) { 91 | #if TEST > 1 92 | bin_test(); 93 | #endif 94 | bl = &blocks[RANDOM(&ld, n_blocks)]; 95 | r = RANDOM(&ld, 1024); 96 | if(r < 200) { /* free only */ 97 | mutex_lock(&bl->mutex); 98 | for(b=0; bb[b]); 100 | mutex_unlock(&bl->mutex); 101 | i += BINS_PER_BLOCK; 102 | } else { /* alloc/realloc */ 103 | /* Generate random numbers in advance. */ 104 | for(b=0; bu.size) + 1; 106 | rnum[b] = lran2(&ld); 107 | } 108 | mutex_lock(&bl->mutex); 109 | for(b=0; bb[b], rsize[b], rnum[b]); 111 | mutex_unlock(&bl->mutex); 112 | i += BINS_PER_BLOCK; 113 | } 114 | #if TEST > 2 115 | bin_test(); 116 | #endif 117 | } 118 | } 119 | 120 | int n_total=0, n_total_max=N_TOTAL, n_running; 121 | 122 | int 123 | my_end_thread(struct thread_st *st) 124 | { 125 | /* Thread st has finished. Start a new one. */ 126 | #if 0 127 | printf("Thread %lx terminated.\n", (long)st->id); 128 | #endif 129 | if(n_total >= n_total_max) { 130 | n_running--; 131 | } else if(st->u.seed++, thread_create(st)) { 132 | printf("Creating thread #%d failed.\n", n_total); 133 | } else { 134 | n_total++; 135 | if(n_total%N_TOTAL_PRINT == 0) 136 | printf("n_total = %d\n", n_total); 137 | } 138 | return 0; 139 | } 140 | 141 | int 142 | main(int argc, char *argv[]) 143 | { 144 | int i, j, bins; 145 | int n_thr=N_THREADS; 146 | int i_max=I_MAX; 147 | unsigned long size=SIZE; 148 | struct thread_st *st; 149 | 150 | #if USE_MALLOC && USE_STARTER==2 151 | ptmalloc_init(); 152 | printf("ptmalloc_init\n"); 153 | #endif 154 | 155 | if(argc > 1) n_total_max = atoi(argv[1]); 156 | if(n_total_max < 1) n_thr = 1; 157 | if(argc > 2) n_thr = atoi(argv[2]); 158 | if(n_thr < 1) n_thr = 1; 159 | if(n_thr > 100) n_thr = 100; 160 | if(argc > 3) i_max = atoi(argv[3]); 161 | 162 | if(argc > 4) size = atol(argv[4]); 163 | if(size < 2) size = 2; 164 | 165 | bins = MEMORY/size; 166 | if(argc > 5) bins = atoi(argv[5]); 167 | if(bins < BINS_PER_BLOCK) bins = BINS_PER_BLOCK; 168 | 169 | n_blocks = bins/BINS_PER_BLOCK; 170 | blocks = (struct block *)malloc(n_blocks*sizeof(*blocks)); 171 | if(!blocks) 172 | exit(1); 173 | 174 | thread_init(); 175 | printf("total=%d threads=%d i_max=%d size=%ld bins=%d\n", 176 | n_total_max, n_thr, i_max, size, n_blocks*BINS_PER_BLOCK); 177 | 178 | for(i=0; i0;) { 207 | wait_for_thread(st, n_thr, my_end_thread); 208 | } 209 | 210 | for(i=0; i /* for O_RDWR */ 40 | #define MMAP_FLAGS (MAP_PRIVATE) 41 | static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ 42 | #define CALL_MMAP(s) ((dev_zero_fd < 0) ? \ 43 | (dev_zero_fd = open("/dev/zero", O_RDWR), \ 44 | mmap(0, (s), PROT_READ|PROT_WRITE, MMAP_FLAGS, dev_zero_fd, 0)) : \ 45 | mmap(0, (s), PROT_READ|PROT_WRITE, MMAP_FLAGS, dev_zero_fd, 0)) 46 | #endif /* MAP_ANONYMOUS */ 47 | #define CALL_MUNMAP(a, s) munmap((a), (s)) 48 | 49 | struct malloc_chunk { 50 | size_t prev_foot; /* Size of previous chunk (if free). */ 51 | size_t head; /* Size and inuse bits. */ 52 | fm_ptr fd; /* double links -- used only if free. */ 53 | fm_ptr bk; 54 | } __attribute__((packed)); 55 | 56 | typedef struct malloc_chunk mchunk; 57 | typedef struct malloc_chunk* mchunkptr; 58 | 59 | typedef unsigned int binmap_t; 60 | typedef unsigned int flag_t; 61 | 62 | struct malloc_tree_chunk; 63 | typedef struct malloc_tree_chunk* tbinptr; 64 | 65 | struct malloc_segment { 66 | fm_ptr base; /* base address */ 67 | size_t size; /* allocated size */ 68 | fm_ptr next; /* ptr to next segment */ 69 | flag_t sflags; /* mmap and extern flag */ 70 | } __attribute__((packed)); 71 | 72 | typedef struct malloc_segment msegment; 73 | 74 | #define NSMALLBINS (32U) 75 | #define NTREEBINS (32U) 76 | 77 | struct malloc_state { 78 | binmap_t smallmap; 79 | binmap_t treemap; 80 | size_t dvsize; 81 | size_t topsize; 82 | fm_ptr least_addr; 83 | fm_ptr dv; 84 | fm_ptr top; 85 | size_t trim_check; 86 | size_t release_checks; 87 | size_t magic; 88 | fm_ptr smallbins[(NSMALLBINS+1)*2]; 89 | fm_ptr treebins[NTREEBINS]; 90 | size_t footprint; 91 | size_t max_footprint; 92 | flag_t mflags; 93 | #if USE_LOCKS 94 | MLOCK_T mutex; 95 | #endif /* USE_LOCKS */ 96 | msegment seg; 97 | //void* extp; 98 | size_t exts; 99 | } __attribute__((packed)); 100 | 101 | /* 102 | TOP_FOOT_SIZE is padding at the end of a segment, including space 103 | that may be needed to place segment records and fenceposts when new 104 | noncontiguous segments are added. 105 | */ 106 | #define TOP_FOOT_SIZE\ 107 | (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) 108 | 109 | /* ------------------- Chunks sizes and alignments ----------------------- */ 110 | 111 | #define MCHUNK_SIZE (sizeof(mchunk)) 112 | 113 | #define CHUNK_OVERHEAD (SIZE_T_SIZE) 114 | 115 | /* MMapped chunks need a second word of overhead ... */ 116 | #define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) 117 | /* ... and additional padding for fake next-chunk at foot */ 118 | #define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) 119 | 120 | /* The smallest size we can malloc is an aligned minimal chunk */ 121 | #define MIN_CHUNK_SIZE\ 122 | ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) 123 | 124 | /* conversion from malloc headers to user pointers, and back */ 125 | #define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) 126 | #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) 127 | /* chunk associated with aligned address A */ 128 | #define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) 129 | 130 | /* pad request bytes into a usable size */ 131 | #define pad_request(req) \ 132 | (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) 133 | 134 | /* The byte and bit size of a size_t */ 135 | #define SIZE_T_SIZE (sizeof(size_t)) 136 | #define SIZE_T_BITSIZE (sizeof(size_t) << 3) 137 | 138 | /* Some constants coerced to size_t */ 139 | /* Annoying but necessary to avoid errors on some platforms */ 140 | #define SIZE_T_ZERO ((size_t)0) 141 | #define SIZE_T_ONE ((size_t)1) 142 | #define SIZE_T_TWO ((size_t)2) 143 | #define SIZE_T_FOUR ((size_t)4) 144 | #define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) 145 | #define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) 146 | #define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) 147 | #define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) 148 | 149 | #define IS_MMAPPED_BIT (SIZE_T_ONE) 150 | #define PINUSE_BIT (SIZE_T_ONE) 151 | #define CINUSE_BIT (SIZE_T_TWO) 152 | #define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|SIZE_T_FOUR) 153 | 154 | /* head field is or'ed with NON_MAIN_ARENA if the chunk was obtained 155 | from a non-main arena. This is only set immediately before handing 156 | the chunk to the user, if necessary. */ 157 | #define NON_MAIN_ARENA (SIZE_T_FOUR) 158 | 159 | #define cinuse(p) ((p)->head & CINUSE_BIT) 160 | #define pinuse(p) ((p)->head & PINUSE_BIT) 161 | #define chunksize(p) ((p)->head & ~(FLAG_BITS)) 162 | 163 | #define is_mmapped(p)\ 164 | (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_MMAPPED_BIT)) 165 | 166 | /* Get the internal overhead associated with chunk p */ 167 | #define overhead_for(p)\ 168 | (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) 169 | 170 | #endif /* MALLOC_PRIVATE_H */ 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fmalloc - malloc for memory-mapped files 2 | 3 | This package is a modified version of Wolfram Gloger's ptmalloc3 4 | that is a modified version of Doug Lea's malloc-2.8.3 implementation. 5 | 6 | As part of the GNU C library, the source files may be available under 7 | the GNU Library General Public License (see the comments in the 8 | files). But as part of this stand-alone package, the code is also 9 | available under the (probably less restrictive) conditions described 10 | in the file 'COPYRIGHT'. In any case, there is no warranty whatsoever 11 | for this package. 12 | 13 | ## What is fmalloc? 14 | 15 | fmalloc aims to bridge the representation gap between in-memory and on-disk data structures. 16 | 17 | fmalloc allows applications to directly store im-memory data structures on files through the memory-mapped file interface. 18 | 19 | Technically, fmalloc implements two functionalities. 20 | 21 | 1. fmalloc allocates memory from a virtual memory region of a memory-mapped file. 22 | 2. fmalloc offers a smart pointer named fmalloc pointer (fm\_ptr) that automatically translates pointer values for a virtual memory address and file offset accordingly. 23 | 24 | ### 1. Efficient memory allocation 25 | 26 | Essentially, fmalloc is a wrapper of ptmalloc that is widely used as part of the GNU C library. 27 | fmalloc slightly modifies ptmalloc to allocate memory from 28 | the virtual memory region of a memory-mapped file instead of calling the brk and mmap system calls. 29 | 30 | In other words, fmalloc's internal memory allocation algorithm is the one implemented in ptmalloc that 31 | has been testified to be efficient and performant. 32 | 33 | ### 2. fm\_ptr: A smart pointer for automatic file offset and memory address translation 34 | 35 | fm\_ptr is a key mediator of in-memory and on-disk pointer representations. 36 | 37 | Commonly, pointers for in-memory data structures are virtual addresses, 38 | and for on-disk data structures, they are file offsets. 39 | 40 | Usually, the transformation between these two types of pointers is done by application programs. 41 | 42 | However, such pointer translation is not common in the standard in-memory data structure programming and tends to increase the implementation complexity. 43 | 44 | fm\_ptr obviates the necessity of manual pointer translation, and highly simplifies the programming using memory-mapped files. 45 | 46 | ## API 47 | 48 | ### Primary data structure associated with a memory-mapped file 49 | 50 | ```struct fm_info``` is a data structure associated with a memory-mapped file. 51 | 52 | ### Allocate struct fm_info 53 | 54 | ```c 55 | struct fm_info *fmalloc_init(const char *filepath, bool *init) 56 | ``` 57 | 58 | - ```filepath```: specifies the file path to store the in-memory data. 59 | - ```init```: The ```fmalloc_init``` function perform initialization if the file specified by ```filepath``` is not initialized for fmalloc. If ```fmalloc_init``` initializes the file, it stores ```true``` for the bool variable pointed by ```init```. 60 | 61 | ### Set target memory-mapped file 62 | 63 | ```c 64 | void fmalloc_set_target(struct fm_info *fi) 65 | ``` 66 | 67 | ### Allocate memory 68 | 69 | ```c 70 | void *fmalloc(size_t size) 71 | ``` 72 | 73 | ```fmalloc``` allocates memory from a virtual memory region of a memory-mapped file that is associated with 74 | the currently targeting ```fm_info``` structure. 75 | 76 | As seen, it is substantially similar to the standard malloc. 77 | 78 | ### Free memory 79 | 80 | ```c 81 | void ffree(void *addr) 82 | ``` 83 | 84 | ```ffree``` frees memory for a memory-mapped file that is associated with 85 | the currently targeting ```fm_info``` structure. 86 | 87 | ### Data layout on a file 88 | 89 | The following depicts the data layout on a file. The first 4KB is used for the internal information for fmalloc itself. 90 | The range between 4KB~8KB is reserved for application programs. fmalloc assumes that applications put their root objects there. 91 | From 8KB to the end is managed by ptmalloc. 92 | 93 | ``` 94 | 0 4KB 8KB end 95 | |-- fm_super --|-- for app --|-- ... malloc ...--| 96 | ``` 97 | 98 | ## Compilation 99 | 100 | The following command will generate a library file named ```libfmalloc.a```. 101 | 102 | ``` 103 | $ make 104 | ``` 105 | 106 | ## Demo 107 | 108 | The following is a simple persistent list implementation using fmalloc. This is found in the ```examples/list``` directory. 109 | 110 | ```c 111 | #include 112 | 113 | /* list implementation */ 114 | struct node { 115 | int val; 116 | fm_ptr next; 117 | } __attribute__((packed)); 118 | 119 | static void list_append(struct node *head, struct node *newnode) 120 | { 121 | struct node *n = head; 122 | while (n->next) { 123 | n = n->next; 124 | } 125 | n->next = newnode; 126 | } 127 | 128 | /* app reserved super block */ 129 | struct app_super { 130 | fm_ptr head; 131 | }; 132 | 133 | /* example append operation */ 134 | static void append_nodes(struct node *head) 135 | { 136 | int i; 137 | printf("writing list data...\n"); 138 | for (i = 0; i < 10; i++) { 139 | struct node *n = (struct node *) fmalloc(sizeof(struct node)); 140 | n->val = i; 141 | n->next = NULL; 142 | list_append(head, n); 143 | } 144 | printf("done.\n"); 145 | } 146 | 147 | /* example read operation */ 148 | static void read_nodes(struct node *head) 149 | { 150 | struct node *n = head->next; 151 | printf("reading list data...\n"); 152 | while (n) { 153 | printf("%d\n", n->val); 154 | n = n->next; 155 | } 156 | printf("done.\n"); 157 | } 158 | 159 | int main(int argc, char const* argv[]) 160 | { 161 | struct app_super *super; 162 | bool init = false; 163 | struct fm_info *fi; 164 | 165 | if (argc < 2) { 166 | printf("please specify a data file\n"); 167 | exit(1); 168 | } 169 | 170 | fi = fmalloc_init(argv[1], &init); 171 | fmalloc_set_target(fi); 172 | 173 | /* fmalloc reserves 4KB ~ 8KB for app for locating super block */ 174 | super = (struct app_super *) ((unsigned long) fi->mem + PAGE_SIZE); 175 | 176 | if (init) { 177 | super->head = (struct node *) fmalloc(sizeof(struct node)); 178 | append_nodes(super->head); 179 | } else { 180 | if (super->head) { 181 | read_nodes(super->head); 182 | } 183 | } 184 | 185 | return 0; 186 | } 187 | ``` 188 | 189 | ### A few points 190 | 191 | - ```struct node``` has fm\_ptr to point the next node that is located on a memory-mapped file. 192 | - ```list_append``` is the same as the standard in-memory list implementation. But, actually, this implementation stores and reads the list on a file. 193 | 194 | ## Note 195 | 196 | The size of the memory-mapped file should be large enough. Otherwise, fmalloc fails to allocate memory. 197 | -------------------------------------------------------------------------------- /t-test1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: t-test1.c,v 1.2 2006/03/27 16:05:13 wg Exp $ 3 | * by Wolfram Gloger 1996-1999, 2001, 2004, 2006 4 | * A multi-thread test for malloc performance, maintaining one pool of 5 | * allocated bins per thread. 6 | */ 7 | 8 | #if (defined __STDC__ && __STDC__) || defined __cplusplus 9 | # include 10 | #endif 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #if !USE_MALLOC 20 | #include 21 | #else 22 | #include "malloc-2.8.3.h" 23 | #endif 24 | 25 | #include "lran2.h" 26 | #include "t-test.h" 27 | 28 | struct user_data { 29 | int bins, max; 30 | unsigned long size; 31 | long seed; 32 | }; 33 | #include "thread-st.h" 34 | 35 | #define N_TOTAL 10 36 | #ifndef N_THREADS 37 | #define N_THREADS 2 38 | #endif 39 | #ifndef N_TOTAL_PRINT 40 | #define N_TOTAL_PRINT 50 41 | #endif 42 | #ifndef MEMORY 43 | #define MEMORY 8000000l 44 | #endif 45 | #define SIZE 10000 46 | #define I_MAX 10000 47 | #define ACTIONS_MAX 30 48 | #ifndef TEST_FORK 49 | #define TEST_FORK 0 50 | #endif 51 | 52 | #define RANDOM(d,s) (lran2(d) % (s)) 53 | 54 | struct bin_info { 55 | struct bin *m; 56 | unsigned long size, bins; 57 | }; 58 | 59 | #if TEST > 0 60 | 61 | void 62 | bin_test(struct bin_info *p) 63 | { 64 | int b; 65 | 66 | for(b=0; bbins; b++) { 67 | if(mem_check(p->m[b].ptr, p->m[b].size)) { 68 | printf("memory corrupt!\n"); 69 | abort(); 70 | } 71 | } 72 | } 73 | 74 | #endif 75 | 76 | void 77 | malloc_test(struct thread_st *st) 78 | { 79 | int b, i, j, actions, pid = 1; 80 | struct bin_info p; 81 | struct lran2_st ld; /* data for random number generator */ 82 | 83 | lran2_init(&ld, st->u.seed); 84 | #if TEST_FORK>0 85 | if(RANDOM(&ld, TEST_FORK) == 0) { 86 | int status; 87 | 88 | #if !USE_THR 89 | pid = fork(); 90 | #else 91 | pid = fork1(); 92 | #endif 93 | if(pid > 0) { 94 | /*printf("forked, waiting for %d...\n", pid);*/ 95 | waitpid(pid, &status, 0); 96 | printf("done with %d...\n", pid); 97 | if(!WIFEXITED(status)) { 98 | printf("child term with signal %d\n", WTERMSIG(status)); 99 | exit(1); 100 | } 101 | return; 102 | } 103 | exit(0); 104 | } 105 | #endif 106 | p.m = (struct bin *)malloc(st->u.bins*sizeof(*p.m)); 107 | p.bins = st->u.bins; 108 | p.size = st->u.size; 109 | for(b=0; bu.max;) { 116 | #if TEST > 1 117 | bin_test(&p); 118 | #endif 119 | actions = RANDOM(&ld, ACTIONS_MAX); 120 | #if USE_MALLOC && MALLOC_DEBUG 121 | if(actions < 2) { mallinfo(); } 122 | #endif 123 | for(j=0; j 2 133 | bin_test(&p); 134 | #endif 135 | } 136 | #if 0 /* Test illegal free()s while setting MALLOC_CHECK_ */ 137 | for(j=0; j<8; j++) { 138 | b = RANDOM(&ld, p.bins); 139 | if(p.m[b].ptr) { 140 | int offset = (RANDOM(&ld, 11) - 5)*8; 141 | char *rogue = (char*)(p.m[b].ptr) + offset; 142 | /*printf("p=%p rogue=%p\n", p.m[b].ptr, rogue);*/ 143 | free(rogue); 144 | } 145 | } 146 | #endif 147 | i += actions; 148 | } 149 | for(b=0; bid); 164 | #endif 165 | if(n_total >= n_total_max) { 166 | n_running--; 167 | } else if(st->u.seed++, thread_create(st)) { 168 | printf("Creating thread #%d failed.\n", n_total); 169 | } else { 170 | n_total++; 171 | if(n_total%N_TOTAL_PRINT == 0) 172 | printf("n_total = %d\n", n_total); 173 | } 174 | return 0; 175 | } 176 | 177 | #if 0 178 | /* Protect address space for allocation of n threads by LinuxThreads. */ 179 | static void 180 | protect_stack(int n) 181 | { 182 | char buf[2048*1024]; 183 | char* guard; 184 | size_t guard_size = 2*2048*1024UL*(n+2); 185 | 186 | buf[0] = '\0'; 187 | guard = (char*)(((unsigned long)buf - 4096)& ~4095UL) - guard_size; 188 | printf("Setting up stack guard at %p\n", guard); 189 | if(mmap(guard, guard_size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 190 | -1, 0) 191 | != guard) 192 | printf("failed!\n"); 193 | } 194 | #endif 195 | 196 | int 197 | main(int argc, char *argv[]) 198 | { 199 | int i, bins; 200 | int n_thr=N_THREADS; 201 | int i_max=I_MAX; 202 | unsigned long size=SIZE; 203 | struct thread_st *st; 204 | 205 | #if USE_MALLOC && USE_STARTER==2 206 | ptmalloc_init(); 207 | printf("ptmalloc_init\n"); 208 | #endif 209 | 210 | if(argc > 1) n_total_max = atoi(argv[1]); 211 | if(n_total_max < 1) n_thr = 1; 212 | if(argc > 2) n_thr = atoi(argv[2]); 213 | if(n_thr < 1) n_thr = 1; 214 | if(n_thr > 100) n_thr = 100; 215 | if(argc > 3) i_max = atoi(argv[3]); 216 | 217 | if(argc > 4) size = atol(argv[4]); 218 | if(size < 2) size = 2; 219 | 220 | bins = MEMORY/(size*n_thr); 221 | if(argc > 5) bins = atoi(argv[5]); 222 | if(bins < 4) bins = 4; 223 | 224 | /*protect_stack(n_thr);*/ 225 | 226 | thread_init(); 227 | printf("total=%d threads=%d i_max=%d size=%ld bins=%d\n", 228 | n_total_max, n_thr, i_max, size, bins); 229 | 230 | st = (struct thread_st *)malloc(n_thr*sizeof(*st)); 231 | if(!st) exit(-1); 232 | 233 | #if !defined NO_THREADS && (defined __sun__ || defined sun) 234 | /* I know of no other way to achieve proper concurrency with Solaris. */ 235 | thr_setconcurrency(n_thr); 236 | #endif 237 | 238 | /* Start all n_thr threads. */ 239 | for(i=0; i0;) { 268 | wait_for_thread(st, n_thr, my_end_thread); 269 | } 270 | for(i=0; iptmalloc3-current.tar.gz 207 | 208 | dist: 209 | cd ..; tar $(TAR_FLAGS) -c -f - $(DIST_FILES2:%=ptmalloc3/%) | \ 210 | gzip -9 >ptmalloc3.tar.gz 211 | 212 | # dependencies 213 | ptmalloc3.o: malloc-private.h 214 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ptmalloc3 - a multi-thread malloc implementation 2 | ================================================ 3 | 4 | Wolfram Gloger (wg@malloc.de) 5 | 6 | Jan 2006 7 | 8 | 9 | Thanks 10 | ====== 11 | 12 | This release was partly funded by Pixar Animation Studios. I would 13 | like to thank David Baraff of Pixar for his support and Doug Lea 14 | (dl@cs.oswego.edu) for the great original malloc implementation. 15 | 16 | 17 | Introduction 18 | ============ 19 | 20 | This package is a modified version of Doug Lea's malloc-2.8.3 21 | implementation (available seperately from ftp://g.oswego.edu/pub/misc) 22 | that I adapted for multiple threads, while trying to avoid lock 23 | contention as much as possible. 24 | 25 | As part of the GNU C library, the source files may be available under 26 | the GNU Library General Public License (see the comments in the 27 | files). But as part of this stand-alone package, the code is also 28 | available under the (probably less restrictive) conditions described 29 | in the file 'COPYRIGHT'. In any case, there is no warranty whatsoever 30 | for this package. 31 | 32 | The current distribution should be available from: 33 | 34 | http://www.malloc.de/malloc/ptmalloc3.tar.gz 35 | 36 | 37 | Compilation 38 | =========== 39 | 40 | It should be possible to build ptmalloc3 on any UN*X-like system that 41 | implements the sbrk(), mmap(), munmap() and mprotect() calls. Since 42 | there are now several source files, a library (libptmalloc3.a) is 43 | generated. See the Makefile for examples of the compile-time options. 44 | 45 | Note that support for non-ANSI compilers is no longer there. 46 | 47 | Several example targets are provided in the Makefile: 48 | 49 | o Posix threads (pthreads), compile with "make posix" 50 | 51 | o Posix threads with explicit initialization, compile with 52 | "make posix-explicit" (known to be required on HPUX) 53 | 54 | o Posix threads without "tsd data hack" (see below), compile with 55 | "make posix-with-tsd" 56 | 57 | o Solaris threads, compile with "make solaris" 58 | 59 | o SGI sproc() threads, compile with "make sproc" 60 | 61 | o no threads, compile with "make nothreads" (currently out of order?) 62 | 63 | For Linux: 64 | 65 | o make "linux-pthread" (almost the same as "make posix") or 66 | make "linux-shared" 67 | 68 | Note that some compilers need special flags for multi-threaded code, 69 | e.g. with Solaris cc with Posix threads, one should use: 70 | 71 | % make posix SYS_FLAGS='-mt' 72 | 73 | Some additional targets, ending in `-libc', are also provided in the 74 | Makefile, to compare performance of the test programs to the case when 75 | linking with the standard malloc implementation in libc. 76 | 77 | A potential problem remains: If any of the system-specific functions 78 | for getting/setting thread-specific data or for locking a mutex call 79 | one of the malloc-related functions internally, the implementation 80 | cannot work at all due to infinite recursion. One example seems to be 81 | Solaris 2.4. I would like to hear if this problem occurs on other 82 | systems, and whether similar workarounds could be applied. 83 | 84 | For Posix threads, too, an optional hack like that has been integrated 85 | (activated when defining USE_TSD_DATA_HACK) which depends on 86 | `pthread_t' being convertible to an integral type (which is of course 87 | not generally guaranteed). USE_TSD_DATA_HACK is now the default 88 | because I haven't yet found a non-glibc pthreads system where this 89 | hack is _not_ needed. 90 | 91 | *NEW* and _important_: In (currently) one place in the ptmalloc3 92 | source, a write memory barrier is needed, named 93 | atomic_write_barrier(). This macro needs to be defined at the end of 94 | malloc-machine.h. For gcc, a fallback in the form of a full memory 95 | barrier is already defined, but you may need to add another definition 96 | if you don't use gcc. 97 | 98 | Usage 99 | ===== 100 | 101 | Just link libptmalloc3 into your application. 102 | 103 | Some wicked systems (e.g. HPUX apparently) won't let malloc call _any_ 104 | thread-related functions before main(). On these systems, 105 | USE_STARTER=2 must be defined during compilation (see "make 106 | posix-explicit" above) and the global initialization function 107 | ptmalloc_init() must be called explicitly, preferably at the start of 108 | main(). 109 | 110 | Otherwise, when using ptmalloc3, no special precautions are necessary. 111 | 112 | Link order is important 113 | ======================= 114 | 115 | On some systems, when overriding malloc and linking against shared 116 | libraries, the link order becomes very important. E.g., when linking 117 | C++ programs on Solaris with Solaris threads [this is probably now 118 | obsolete], don't rely on libC being included by default, but instead 119 | put `-lthread' behind `-lC' on the command line: 120 | 121 | CC ... libptmalloc3.a -lC -lthread 122 | 123 | This is because there are global constructors in libC that need 124 | malloc/ptmalloc, which in turn needs to have the thread library to be 125 | already initialized. 126 | 127 | Debugging hooks 128 | =============== 129 | 130 | All calls to malloc(), realloc(), free() and memalign() are routed 131 | through the global function pointers __malloc_hook, __realloc_hook, 132 | __free_hook and __memalign_hook if they are not NULL (see the malloc.h 133 | header file for declarations of these pointers). Therefore the malloc 134 | implementation can be changed at runtime, if care is taken not to call 135 | free() or realloc() on pointers obtained with a different 136 | implementation than the one currently in effect. (The easiest way to 137 | guarantee this is to set up the hooks before any malloc call, e.g. 138 | with a function pointed to by the global variable 139 | __malloc_initialize_hook). 140 | 141 | You can now also tune other malloc parameters (normally adjused via 142 | mallopt() calls from the application) with environment variables: 143 | 144 | MALLOC_TRIM_THRESHOLD_ for deciding to shrink the heap (in bytes) 145 | 146 | MALLOC_GRANULARITY_ The unit for allocating and deallocating 147 | MALLOC_TOP_PAD_ memory from the system. The default 148 | is 64k and this parameter _must_ be a 149 | power of 2. 150 | 151 | MALLOC_MMAP_THRESHOLD_ min. size for chunks allocated via 152 | mmap() (in bytes) 153 | 154 | Tests 155 | ===== 156 | 157 | Two testing applications, t-test1 and t-test2, are included in this 158 | source distribution. Both perform pseudo-random sequences of 159 | allocations/frees, and can be given numeric arguments (all arguments 160 | are optional): 161 | 162 | % t-test[12] 163 | 164 | n-total = total number of threads executed (default 10) 165 | n-parallel = number of threads running in parallel (2) 166 | n-allocs = number of malloc()'s / free()'s per thread (10000) 167 | size-max = max. size requested with malloc() in bytes (10000) 168 | bins = number of bins to maintain 169 | 170 | The first test `t-test1' maintains a completely seperate pool of 171 | allocated bins for each thread, and should therefore show full 172 | parallelism. On the other hand, `t-test2' creates only a single pool 173 | of bins, and each thread randomly allocates/frees any bin. Some lock 174 | contention is to be expected in this case, as the threads frequently 175 | cross each others arena. 176 | 177 | Performance results from t-test1 should be quite repeatable, while the 178 | behaviour of t-test2 depends on scheduling variations. 179 | 180 | Conclusion 181 | ========== 182 | 183 | I'm always interested in performance data and feedback, just send mail 184 | to ptmalloc@malloc.de. 185 | 186 | Good luck! 187 | -------------------------------------------------------------------------------- /malloc-2.8.3.h: -------------------------------------------------------------------------------- 1 | /* 2 | Default header file for malloc-2.8.x, written by Doug Lea 3 | and released to the public domain, as explained at 4 | http://creativecommons.org/licenses/publicdomain. 5 | 6 | last update: Mon Aug 15 08:55:52 2005 Doug Lea (dl at gee) 7 | 8 | This header is for ANSI C/C++ only. You can set any of 9 | the following #defines before including: 10 | 11 | * If USE_DL_PREFIX is defined, it is assumed that malloc.c 12 | was also compiled with this option, so all routines 13 | have names starting with "dl". 14 | 15 | * If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this 16 | file will be #included AFTER . This is needed only if 17 | your system defines a struct mallinfo that is incompatible with the 18 | standard one declared here. Otherwise, you can include this file 19 | INSTEAD of your system system . At least on ANSI, all 20 | declarations should be compatible with system versions 21 | 22 | * If MSPACES is defined, declarations for mspace versions are included. 23 | */ 24 | 25 | #ifndef MALLOC_280_H 26 | #define MALLOC_280_H 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | #include /* for size_t */ 33 | 34 | #if !ONLY_MSPACES 35 | 36 | #ifndef USE_DL_PREFIX 37 | #define dlcalloc calloc 38 | #define dlfree free 39 | #define dlmalloc malloc 40 | #define dlmemalign memalign 41 | #define dlrealloc realloc 42 | #define dlvalloc valloc 43 | #define dlpvalloc pvalloc 44 | #define dlmallinfo mallinfo 45 | #define dlmallopt mallopt 46 | #define dlmalloc_trim malloc_trim 47 | #define dlmalloc_stats malloc_stats 48 | #define dlmalloc_usable_size malloc_usable_size 49 | #define dlmalloc_footprint malloc_footprint 50 | #define dlindependent_calloc independent_calloc 51 | #define dlindependent_comalloc independent_comalloc 52 | #endif /* USE_DL_PREFIX */ 53 | 54 | 55 | /* 56 | malloc(size_t n) 57 | Returns a pointer to a newly allocated chunk of at least n bytes, or 58 | null if no space is available, in which case errno is set to ENOMEM 59 | on ANSI C systems. 60 | 61 | If n is zero, malloc returns a minimum-sized chunk. (The minimum 62 | size is 16 bytes on most 32bit systems, and 32 bytes on 64bit 63 | systems.) Note that size_t is an unsigned type, so calls with 64 | arguments that would be negative if signed are interpreted as 65 | requests for huge amounts of space, which will often fail. The 66 | maximum supported value of n differs across systems, but is in all 67 | cases less than the maximum representable value of a size_t. 68 | */ 69 | void* dlmalloc(size_t); 70 | 71 | /* 72 | free(void* p) 73 | Releases the chunk of memory pointed to by p, that had been previously 74 | allocated using malloc or a related routine such as realloc. 75 | It has no effect if p is null. If p was not malloced or already 76 | freed, free(p) will by default cuase the current program to abort. 77 | */ 78 | void dlfree(void*); 79 | 80 | /* 81 | calloc(size_t n_elements, size_t element_size); 82 | Returns a pointer to n_elements * element_size bytes, with all locations 83 | set to zero. 84 | */ 85 | void* dlcalloc(size_t, size_t); 86 | 87 | /* 88 | realloc(void* p, size_t n) 89 | Returns a pointer to a chunk of size n that contains the same data 90 | as does chunk p up to the minimum of (n, p's size) bytes, or null 91 | if no space is available. 92 | 93 | The returned pointer may or may not be the same as p. The algorithm 94 | prefers extending p in most cases when possible, otherwise it 95 | employs the equivalent of a malloc-copy-free sequence. 96 | 97 | If p is null, realloc is equivalent to malloc. 98 | 99 | If space is not available, realloc returns null, errno is set (if on 100 | ANSI) and p is NOT freed. 101 | 102 | if n is for fewer bytes than already held by p, the newly unused 103 | space is lopped off and freed if possible. realloc with a size 104 | argument of zero (re)allocates a minimum-sized chunk. 105 | 106 | The old unix realloc convention of allowing the last-free'd chunk 107 | to be used as an argument to realloc is not supported. 108 | */ 109 | 110 | void* dlrealloc(void*, size_t); 111 | 112 | /* 113 | memalign(size_t alignment, size_t n); 114 | Returns a pointer to a newly allocated chunk of n bytes, aligned 115 | in accord with the alignment argument. 116 | 117 | The alignment argument should be a power of two. If the argument is 118 | not a power of two, the nearest greater power is used. 119 | 8-byte alignment is guaranteed by normal malloc calls, so don't 120 | bother calling memalign with an argument of 8 or less. 121 | 122 | Overreliance on memalign is a sure way to fragment space. 123 | */ 124 | void* dlmemalign(size_t, size_t); 125 | 126 | /* 127 | valloc(size_t n); 128 | Equivalent to memalign(pagesize, n), where pagesize is the page 129 | size of the system. If the pagesize is unknown, 4096 is used. 130 | */ 131 | void* dlvalloc(size_t); 132 | 133 | /* 134 | mallopt(int parameter_number, int parameter_value) 135 | Sets tunable parameters The format is to provide a 136 | (parameter-number, parameter-value) pair. mallopt then sets the 137 | corresponding parameter to the argument value if it can (i.e., so 138 | long as the value is meaningful), and returns 1 if successful else 139 | 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, 140 | normally defined in malloc.h. None of these are use in this malloc, 141 | so setting them has no effect. But this malloc also supports other 142 | options in mallopt: 143 | 144 | Symbol param # default allowed param values 145 | M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) 146 | M_GRANULARITY -2 page size any power of 2 >= page size 147 | M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) 148 | */ 149 | int dlmallopt(int, int); 150 | 151 | #define M_TRIM_THRESHOLD (-1) 152 | #define M_GRANULARITY (-2) 153 | #define M_MMAP_THRESHOLD (-3) 154 | 155 | 156 | /* 157 | malloc_footprint(); 158 | Returns the number of bytes obtained from the system. The total 159 | number of bytes allocated by malloc, realloc etc., is less than this 160 | value. Unlike mallinfo, this function returns only a precomputed 161 | result, so can be called frequently to monitor memory consumption. 162 | Even if locks are otherwise defined, this function does not use them, 163 | so results might not be up to date. 164 | */ 165 | size_t dlmalloc_footprint(void); 166 | 167 | #if !NO_MALLINFO 168 | /* 169 | mallinfo() 170 | Returns (by copy) a struct containing various summary statistics: 171 | 172 | arena: current total non-mmapped bytes allocated from system 173 | ordblks: the number of free chunks 174 | smblks: always zero. 175 | hblks: current number of mmapped regions 176 | hblkhd: total bytes held in mmapped regions 177 | usmblks: the maximum total allocated space. This will be greater 178 | than current total if trimming has occurred. 179 | fsmblks: always zero 180 | uordblks: current total allocated space (normal or mmapped) 181 | fordblks: total free space 182 | keepcost: the maximum number of bytes that could ideally be released 183 | back to system via malloc_trim. ("ideally" means that 184 | it ignores page restrictions etc.) 185 | 186 | Because these fields are ints, but internal bookkeeping may 187 | be kept as longs, the reported values may wrap around zero and 188 | thus be inaccurate. 189 | */ 190 | #ifndef HAVE_USR_INCLUDE_MALLOC_H 191 | #ifndef _MALLOC_H 192 | #ifndef MALLINFO_FIELD_TYPE 193 | #define MALLINFO_FIELD_TYPE size_t 194 | #endif /* MALLINFO_FIELD_TYPE */ 195 | struct mallinfo { 196 | MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ 197 | MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ 198 | MALLINFO_FIELD_TYPE smblks; /* always 0 */ 199 | MALLINFO_FIELD_TYPE hblks; /* always 0 */ 200 | MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ 201 | MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ 202 | MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ 203 | MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ 204 | MALLINFO_FIELD_TYPE fordblks; /* total free space */ 205 | MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ 206 | }; 207 | #endif /* _MALLOC_H */ 208 | #endif /* HAVE_USR_INCLUDE_MALLOC_H */ 209 | 210 | struct mallinfo dlmallinfo(void); 211 | #endif /* NO_MALLINFO */ 212 | 213 | /* 214 | independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); 215 | 216 | independent_calloc is similar to calloc, but instead of returning a 217 | single cleared space, it returns an array of pointers to n_elements 218 | independent elements that can hold contents of size elem_size, each 219 | of which starts out cleared, and can be independently freed, 220 | realloc'ed etc. The elements are guaranteed to be adjacently 221 | allocated (this is not guaranteed to occur with multiple callocs or 222 | mallocs), which may also improve cache locality in some 223 | applications. 224 | 225 | The "chunks" argument is optional (i.e., may be null, which is 226 | probably the most typical usage). If it is null, the returned array 227 | is itself dynamically allocated and should also be freed when it is 228 | no longer needed. Otherwise, the chunks array must be of at least 229 | n_elements in length. It is filled in with the pointers to the 230 | chunks. 231 | 232 | In either case, independent_calloc returns this pointer array, or 233 | null if the allocation failed. If n_elements is zero and "chunks" 234 | is null, it returns a chunk representing an array with zero elements 235 | (which should be freed if not wanted). 236 | 237 | Each element must be individually freed when it is no longer 238 | needed. If you'd like to instead be able to free all at once, you 239 | should instead use regular calloc and assign pointers into this 240 | space to represent elements. (In this case though, you cannot 241 | independently free elements.) 242 | 243 | independent_calloc simplifies and speeds up implementations of many 244 | kinds of pools. It may also be useful when constructing large data 245 | structures that initially have a fixed number of fixed-sized nodes, 246 | but the number is not known at compile time, and some of the nodes 247 | may later need to be freed. For example: 248 | 249 | struct Node { int item; struct Node* next; }; 250 | 251 | struct Node* build_list() { 252 | struct Node** pool; 253 | int n = read_number_of_nodes_needed(); 254 | if (n <= 0) return 0; 255 | pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); 256 | if (pool == 0) die(); 257 | // organize into a linked list... 258 | struct Node* first = pool[0]; 259 | for (i = 0; i < n-1; ++i) 260 | pool[i]->next = pool[i+1]; 261 | free(pool); // Can now free the array (or not, if it is needed later) 262 | return first; 263 | } 264 | */ 265 | void** dlindependent_calloc(size_t, size_t, void**); 266 | 267 | /* 268 | independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); 269 | 270 | independent_comalloc allocates, all at once, a set of n_elements 271 | chunks with sizes indicated in the "sizes" array. It returns 272 | an array of pointers to these elements, each of which can be 273 | independently freed, realloc'ed etc. The elements are guaranteed to 274 | be adjacently allocated (this is not guaranteed to occur with 275 | multiple callocs or mallocs), which may also improve cache locality 276 | in some applications. 277 | 278 | The "chunks" argument is optional (i.e., may be null). If it is null 279 | the returned array is itself dynamically allocated and should also 280 | be freed when it is no longer needed. Otherwise, the chunks array 281 | must be of at least n_elements in length. It is filled in with the 282 | pointers to the chunks. 283 | 284 | In either case, independent_comalloc returns this pointer array, or 285 | null if the allocation failed. If n_elements is zero and chunks is 286 | null, it returns a chunk representing an array with zero elements 287 | (which should be freed if not wanted). 288 | 289 | Each element must be individually freed when it is no longer 290 | needed. If you'd like to instead be able to free all at once, you 291 | should instead use a single regular malloc, and assign pointers at 292 | particular offsets in the aggregate space. (In this case though, you 293 | cannot independently free elements.) 294 | 295 | independent_comallac differs from independent_calloc in that each 296 | element may have a different size, and also that it does not 297 | automatically clear elements. 298 | 299 | independent_comalloc can be used to speed up allocation in cases 300 | where several structs or objects must always be allocated at the 301 | same time. For example: 302 | 303 | struct Head { ... } 304 | struct Foot { ... } 305 | 306 | void send_message(char* msg) { 307 | int msglen = strlen(msg); 308 | size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; 309 | void* chunks[3]; 310 | if (independent_comalloc(3, sizes, chunks) == 0) 311 | die(); 312 | struct Head* head = (struct Head*)(chunks[0]); 313 | char* body = (char*)(chunks[1]); 314 | struct Foot* foot = (struct Foot*)(chunks[2]); 315 | // ... 316 | } 317 | 318 | In general though, independent_comalloc is worth using only for 319 | larger values of n_elements. For small values, you probably won't 320 | detect enough difference from series of malloc calls to bother. 321 | 322 | Overuse of independent_comalloc can increase overall memory usage, 323 | since it cannot reuse existing noncontiguous small chunks that 324 | might be available for some of the elements. 325 | */ 326 | void** dlindependent_comalloc(size_t, size_t*, void**); 327 | 328 | 329 | /* 330 | pvalloc(size_t n); 331 | Equivalent to valloc(minimum-page-that-holds(n)), that is, 332 | round up n to nearest pagesize. 333 | */ 334 | void* dlpvalloc(size_t); 335 | 336 | /* 337 | malloc_trim(size_t pad); 338 | 339 | If possible, gives memory back to the system (via negative arguments 340 | to sbrk) if there is unused memory at the `high' end of the malloc 341 | pool or in unused MMAP segments. You can call this after freeing 342 | large blocks of memory to potentially reduce the system-level memory 343 | requirements of a program. However, it cannot guarantee to reduce 344 | memory. Under some allocation patterns, some large free blocks of 345 | memory will be locked between two used chunks, so they cannot be 346 | given back to the system. 347 | 348 | The `pad' argument to malloc_trim represents the amount of free 349 | trailing space to leave untrimmed. If this argument is zero, only 350 | the minimum amount of memory to maintain internal data structures 351 | will be left. Non-zero arguments can be supplied to maintain enough 352 | trailing space to service future expected allocations without having 353 | to re-obtain memory from the system. 354 | 355 | Malloc_trim returns 1 if it actually released any memory, else 0. 356 | */ 357 | int dlmalloc_trim(size_t); 358 | 359 | /* 360 | malloc_usable_size(void* p); 361 | 362 | Returns the number of bytes you can actually use in 363 | an allocated chunk, which may be more than you requested (although 364 | often not) due to alignment and minimum size constraints. 365 | You can use this many bytes without worrying about 366 | overwriting other allocated objects. This is not a particularly great 367 | programming practice. malloc_usable_size can be more useful in 368 | debugging and assertions, for example: 369 | 370 | p = malloc(n); 371 | assert(malloc_usable_size(p) >= 256); 372 | */ 373 | size_t dlmalloc_usable_size(void*); 374 | 375 | /* 376 | malloc_stats(); 377 | Prints on stderr the amount of space obtained from the system (both 378 | via sbrk and mmap), the maximum amount (which may be more than 379 | current if malloc_trim and/or munmap got called), and the current 380 | number of bytes allocated via malloc (or realloc, etc) but not yet 381 | freed. Note that this is the number of bytes allocated, not the 382 | number requested. It will be larger than the number requested 383 | because of alignment and bookkeeping overhead. Because it includes 384 | alignment wastage as being in use, this figure may be greater than 385 | zero even when no user-level chunks are allocated. 386 | 387 | The reported current and maximum system memory can be inaccurate if 388 | a program makes other calls to system memory allocation functions 389 | (normally sbrk) outside of malloc. 390 | 391 | malloc_stats prints only the most commonly interesting statistics. 392 | More information can be obtained by calling mallinfo. 393 | */ 394 | void dlmalloc_stats(void); 395 | 396 | #endif /* !ONLY_MSPACES */ 397 | 398 | #if MSPACES 399 | 400 | /* 401 | mspace is an opaque type representing an independent 402 | region of space that supports mspace_malloc, etc. 403 | */ 404 | typedef void* mspace; 405 | 406 | /* 407 | create_mspace creates and returns a new independent space with the 408 | given initial capacity, or, if 0, the default granularity size. It 409 | returns null if there is no system memory available to create the 410 | space. If argument locked is non-zero, the space uses a separate 411 | lock to control access. The capacity of the space will grow 412 | dynamically as needed to service mspace_malloc requests. You can 413 | control the sizes of incremental increases of this space by 414 | compiling with a different DEFAULT_GRANULARITY or dynamically 415 | setting with mallopt(M_GRANULARITY, value). 416 | */ 417 | mspace create_mspace(size_t capacity, int locked); 418 | 419 | /* 420 | destroy_mspace destroys the given space, and attempts to return all 421 | of its memory back to the system, returning the total number of 422 | bytes freed. After destruction, the results of access to all memory 423 | used by the space become undefined. 424 | */ 425 | size_t destroy_mspace(mspace msp); 426 | 427 | /* 428 | create_mspace_with_base uses the memory supplied as the initial base 429 | of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this 430 | space is used for bookkeeping, so the capacity must be at least this 431 | large. (Otherwise 0 is returned.) When this initial space is 432 | exhausted, additional memory will be obtained from the system. 433 | Destroying this space will deallocate all additionally allocated 434 | space (if possible) but not the initial base. 435 | */ 436 | mspace create_mspace_with_base(void* base, size_t capacity, int locked); 437 | 438 | /* 439 | mspace_malloc behaves as malloc, but operates within 440 | the given space. 441 | */ 442 | void* mspace_malloc(mspace msp, size_t bytes); 443 | 444 | /* 445 | mspace_free behaves as free, but operates within 446 | the given space. 447 | 448 | If compiled with FOOTERS==1, mspace_free is not actually needed. 449 | free may be called instead of mspace_free because freed chunks from 450 | any space are handled by their originating spaces. 451 | */ 452 | void mspace_free(mspace msp, void* mem); 453 | 454 | /* 455 | mspace_realloc behaves as realloc, but operates within 456 | the given space. 457 | 458 | If compiled with FOOTERS==1, mspace_realloc is not actually 459 | needed. realloc may be called instead of mspace_realloc because 460 | realloced chunks from any space are handled by their originating 461 | spaces. 462 | */ 463 | void* mspace_realloc(mspace msp, void* mem, size_t newsize); 464 | 465 | /* 466 | mspace_calloc behaves as calloc, but operates within 467 | the given space. 468 | */ 469 | void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); 470 | 471 | /* 472 | mspace_memalign behaves as memalign, but operates within 473 | the given space. 474 | */ 475 | void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); 476 | 477 | /* 478 | mspace_independent_calloc behaves as independent_calloc, but 479 | operates within the given space. 480 | */ 481 | void** mspace_independent_calloc(mspace msp, size_t n_elements, 482 | size_t elem_size, void* chunks[]); 483 | 484 | /* 485 | mspace_independent_comalloc behaves as independent_comalloc, but 486 | operates within the given space. 487 | */ 488 | void** mspace_independent_comalloc(mspace msp, size_t n_elements, 489 | size_t sizes[], void* chunks[]); 490 | 491 | /* 492 | mspace_footprint() returns the number of bytes obtained from the 493 | system for this space. 494 | */ 495 | size_t mspace_footprint(mspace msp); 496 | 497 | 498 | #if !NO_MALLINFO 499 | /* 500 | mspace_mallinfo behaves as mallinfo, but reports properties of 501 | the given space. 502 | */ 503 | struct mallinfo mspace_mallinfo(mspace msp); 504 | #endif /* NO_MALLINFO */ 505 | 506 | /* 507 | mspace_malloc_stats behaves as malloc_stats, but reports 508 | properties of the given space. 509 | */ 510 | void mspace_malloc_stats(mspace msp); 511 | 512 | /* 513 | mspace_trim behaves as malloc_trim, but 514 | operates within the given space. 515 | */ 516 | int mspace_trim(mspace msp, size_t pad); 517 | 518 | /* 519 | An alias for malloc_usable_size. 520 | */ 521 | size_t mspace_usable_size(void *mem); 522 | 523 | /* 524 | An alias for mallopt. 525 | */ 526 | int mspace_mallopt(int, int); 527 | 528 | #endif /* MSPACES */ 529 | 530 | #ifdef __cplusplus 531 | }; /* end of extern "C" */ 532 | #endif 533 | 534 | #endif /* MALLOC_280_H */ 535 | -------------------------------------------------------------------------------- /ptmalloc3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: ptmalloc3.c,v 1.8 2006/03/31 15:57:28 wg Exp $ 3 | * 4 | 5 | ptmalloc3 -- wrapper for Doug Lea's malloc-2.8.3 with concurrent 6 | allocations 7 | 8 | Copyright (c) 2005, 2006 Wolfram Gloger 9 | 10 | Permission to use, copy, modify, distribute, and sell this software 11 | and its documentation for any purpose is hereby granted without fee, 12 | provided that (i) the above copyright notices and this permission 13 | notice appear in all copies of the software and related documentation, 14 | and (ii) the name of Wolfram Gloger may not be used in any advertising 15 | or publicity relating to the software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 19 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 20 | 21 | IN NO EVENT SHALL WOLFRAM GLOGER BE LIABLE FOR ANY SPECIAL, 22 | INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY 23 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 24 | WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY 25 | OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 26 | PERFORMANCE OF THIS SOFTWARE. 27 | 28 | */ 29 | 30 | /* 31 | * TODO: optimization / better integration with malloc.c (partly done) 32 | * malloc_{get,set}_state (probably hard to keep compatibility) 33 | * debugging hooks 34 | * better mstats 35 | */ 36 | 37 | #include /* For size_t */ 38 | #include /* for mmap */ 39 | #include 40 | #include 41 | #include 42 | #include /* for memset */ 43 | 44 | #include 45 | 46 | #include 47 | 48 | #include "malloc-2.8.3.h" 49 | 50 | /* ----------------------------------------------------------------------- */ 51 | 52 | /* The following section is replicated from malloc.c */ 53 | 54 | #include "malloc-private.h" 55 | 56 | /* end of definitions replicated from malloc.c */ 57 | 58 | #define munmap_chunk(mst, p) do { \ 59 | size_t prevsize = (p)->prev_foot & ~IS_MMAPPED_BIT; \ 60 | size_t psize = chunksize(p) + prevsize + MMAP_FOOT_PAD; \ 61 | if (CALL_MUNMAP((char*)(p) - prevsize, psize) == 0) \ 62 | ((struct malloc_state*)(mst))->footprint -= psize; \ 63 | } while (0) 64 | 65 | /* ---------------------------------------------------------------------- */ 66 | 67 | /* Minimum size for a newly created arena. */ 68 | #ifndef ARENA_SIZE_MIN 69 | # define ARENA_SIZE_MIN (128*1024) 70 | #endif 71 | #define HAVE_MEMCPY 1 72 | 73 | /* If THREAD_STATS is non-zero, some statistics on mutex locking are 74 | computed. */ 75 | #ifndef THREAD_STATS 76 | # define THREAD_STATS 0 77 | #endif 78 | 79 | #ifndef MALLOC_DEBUG 80 | # define MALLOC_DEBUG 0 81 | #endif 82 | 83 | #define my_powerof2(x) ((((x)-1)&(x))==0) 84 | 85 | /* Already initialized? */ 86 | static int __malloc_initialized = -1; 87 | 88 | static unsigned long fmalloc_chunk_size = 0; 89 | 90 | #ifndef RETURN_ADDRESS 91 | # define RETURN_ADDRESS(X_) (NULL) 92 | #endif 93 | 94 | #if THREAD_STATS 95 | # define THREAD_STAT(x) x 96 | #else 97 | # define THREAD_STAT(x) do ; while(0) 98 | #endif 99 | 100 | #ifdef _LIBC 101 | 102 | /* Special defines for the GNU C library. */ 103 | #define public_cALLOc __libc_calloc 104 | #define public_fREe __libc_free 105 | #define public_cFREe __libc_cfree 106 | #define public_mALLOc __libc_malloc 107 | #define public_mEMALIGn __libc_memalign 108 | #define public_rEALLOc __libc_realloc 109 | #define public_vALLOc __libc_valloc 110 | #define public_pVALLOc __libc_pvalloc 111 | #define public_pMEMALIGn __posix_memalign 112 | #define public_mALLINFo __libc_mallinfo 113 | #define public_mALLOPt __libc_mallopt 114 | #define public_mTRIm __malloc_trim 115 | #define public_mSTATs __malloc_stats 116 | #define public_mUSABLe __malloc_usable_size 117 | #define public_iCALLOc __libc_independent_calloc 118 | #define public_iCOMALLOc __libc_independent_comalloc 119 | #define public_gET_STATe __malloc_get_state 120 | #define public_sET_STATe __malloc_set_state 121 | #define malloc_getpagesize __getpagesize() 122 | #define open __open 123 | #define mmap __mmap 124 | #define munmap __munmap 125 | #define mremap __mremap 126 | #define mprotect __mprotect 127 | #define MORECORE (*__morecore) 128 | #define MORECORE_FAILURE 0 129 | 130 | void * __default_morecore (ptrdiff_t); 131 | void *(*__morecore)(ptrdiff_t) = __default_morecore; 132 | 133 | #else /* !_LIBC */ 134 | 135 | #define public_cALLOc dlcalloc 136 | #define public_fREe dlfree 137 | #define public_cFREe dlcfree 138 | #define public_mALLOc dlmalloc 139 | #define public_mEMALIGn dlmemalign 140 | #define public_rEALLOc dlrealloc 141 | #define public_vALLOc dlvalloc 142 | #define public_pVALLOc dlpvalloc 143 | #define public_pMEMALIGn dlposix_memalign 144 | #define public_mALLINFo dlmallinfo 145 | #define public_mALLOPt dlmallopt 146 | #define public_mTRIm dlmalloc_trim 147 | #define public_mSTATs dlmalloc_stats 148 | #define public_mUSABLe dlmalloc_usable_size 149 | #define public_iCALLOc dlindependent_calloc 150 | #define public_iCOMALLOc dlindependent_comalloc 151 | #define public_gET_STATe dlmalloc_get_state 152 | #define public_sET_STATe dlmalloc_set_state 153 | 154 | #endif /* _LIBC */ 155 | 156 | #if !defined _LIBC && (!defined __GNUC__ || __GNUC__<3) 157 | #define __builtin_expect(expr, val) (expr) 158 | #endif 159 | 160 | #if MALLOC_DEBUG 161 | #include 162 | #else 163 | #undef assert 164 | #define assert(x) ((void)0) 165 | #endif 166 | 167 | /* USE_STARTER determines if and when the special "starter" hook 168 | functions are used: not at all (0), during ptmalloc_init (first bit 169 | set), or from the beginning until an explicit call to ptmalloc_init 170 | (second bit set). This is necessary if thread-related 171 | initialization functions (e.g. pthread_key_create) require 172 | malloc() calls (set USE_STARTER=1), or if those functions initially 173 | cannot be used at all (set USE_STARTER=2 and perform an explicit 174 | ptmalloc_init() when the thread library is ready, typically at the 175 | start of main()). */ 176 | 177 | #ifndef USE_STARTER 178 | # ifndef _LIBC 179 | # define USE_STARTER 1 180 | # else 181 | # if USE___THREAD || (defined USE_TLS && !defined SHARED) 182 | /* These routines are never needed in this configuration. */ 183 | # define USE_STARTER 0 184 | # else 185 | # define USE_STARTER (USE_TLS ? 4 : 1) 186 | # endif 187 | # endif 188 | #endif 189 | 190 | /*----------------------------------------------------------------------*/ 191 | 192 | /* Arenas */ 193 | static tsd_key_t arena_key; 194 | static mutex_t list_lock; 195 | 196 | /* Arena structure */ 197 | struct malloc_arena { 198 | int __malloc_initialized; 199 | /* Serialize access. */ 200 | mutex_t mutex; 201 | 202 | /* Statistics for locking. Only used if THREAD_STATS is defined. */ 203 | long stat_lock_direct, stat_lock_loop, stat_lock_wait; 204 | long stat_starter; 205 | 206 | /* Linked list */ 207 | fm_ptr next; 208 | 209 | /* Space for mstate. The size is just the minimum such that 210 | create_mspace_with_base can be successfully called. */ 211 | char buf_[pad_request(sizeof(struct malloc_state)) + TOP_FOOT_SIZE + 212 | CHUNK_ALIGN_MASK + 1]; 213 | } __attribute__((packed)); 214 | #define MSPACE_OFFSET (((offsetof(struct malloc_arena, buf_) \ 215 | + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)) 216 | #define arena_to_mspace(a) ((void *)chunk2mem((char*)(a) + MSPACE_OFFSET)) 217 | 218 | /* check for chunk from non-main arena */ 219 | #define chunk_non_main_arena(p) ((p)->head & NON_MAIN_ARENA) 220 | 221 | static struct malloc_arena* _int_new_arena(size_t size); 222 | 223 | /* Buffer for the main arena. */ 224 | #define main_arena (*((struct malloc_arena *)(__fm_addr_base + FMALLOC_OFF))) 225 | 226 | /* For now, store arena in footer. This means typically 4bytes more 227 | overhead for each non-main-arena chunk, but is fast and easy to 228 | compute. Note that the pointer stored in the extra footer must be 229 | properly aligned, though. */ 230 | #define FOOTER_OVERHEAD \ 231 | (2*sizeof(fm_ptr) - SIZE_T_SIZE) 232 | 233 | #define arena_for_chunk(ptr) \ 234 | (chunk_non_main_arena(ptr) ? *(fm_ptr*) \ 235 | ((char*)(ptr) + chunksize(ptr) - (FOOTER_OVERHEAD - SIZE_T_SIZE)) \ 236 | : &main_arena) 237 | 238 | /* special because of extra overhead */ 239 | #define arena_for_mmap_chunk(ptr) \ 240 | (chunk_non_main_arena(ptr) ? *(fm_ptr*) \ 241 | ((char*)(ptr) + chunksize(ptr) - sizeof(fm_ptr)) \ 242 | : &main_arena) 243 | 244 | #define set_non_main_arena(mem, ar_ptr) do { \ 245 | mchunkptr P = mem2chunk(mem); \ 246 | size_t SZ = chunksize(P) - (is_mmapped(P) ? sizeof(fm_ptr) \ 247 | : (FOOTER_OVERHEAD - SIZE_T_SIZE)); \ 248 | assert((unsigned long)((char*)(P) + SZ)%sizeof(fm_ptr) == 0); \ 249 | *(fm_ptr*)((char*)(P) + SZ) = (ar_ptr); \ 250 | P->head |= NON_MAIN_ARENA; \ 251 | } while (0) 252 | 253 | /* arena_get() acquires an arena and locks the corresponding mutex. 254 | First, try the one last locked successfully by this thread. (This 255 | is the common case and handled with a macro for speed.) Then, loop 256 | once over the circularly linked list of arenas. If no arena is 257 | readily available, create a new one. In this latter case, `size' 258 | is just a hint as to how much memory will be required immediately 259 | in the new arena. */ 260 | 261 | #define arena_get(ptr, size) do { \ 262 | void *vptr = NULL; \ 263 | ptr = (struct malloc_arena*)tsd_getspecific(arena_key, vptr); \ 264 | if(ptr && !mutex_trylock(&ptr->mutex)) { \ 265 | THREAD_STAT(++(ptr->stat_lock_direct)); \ 266 | } else \ 267 | ptr = arena_get2(ptr, (size)); \ 268 | } while(0) 269 | 270 | static struct malloc_arena* 271 | arena_get2(struct malloc_arena* a_tsd, size_t size) 272 | { 273 | struct malloc_arena* a; 274 | int err; 275 | 276 | if(!a_tsd) 277 | a = a_tsd = &main_arena; 278 | else { 279 | a = a_tsd->next; 280 | if(!a) { 281 | /* This can only happen while initializing the new arena. */ 282 | (void)mutex_lock(&main_arena.mutex); 283 | THREAD_STAT(++(main_arena.stat_lock_wait)); 284 | return &main_arena; 285 | } 286 | } 287 | 288 | /* Check the global, circularly linked list for available arenas. */ 289 | repeat: 290 | do { 291 | if(!mutex_trylock(&a->mutex)) { 292 | THREAD_STAT(++(a->stat_lock_loop)); 293 | tsd_setspecific(arena_key, (void *)a); 294 | return a; 295 | } 296 | a = a->next; 297 | } while(a != a_tsd); 298 | 299 | /* If not even the list_lock can be obtained, try again. This can 300 | happen during `atfork', or for example on systems where thread 301 | creation makes it temporarily impossible to obtain _any_ 302 | locks. */ 303 | if(mutex_trylock(&list_lock)) { 304 | a = a_tsd; 305 | goto repeat; 306 | } 307 | (void)mutex_unlock(&list_lock); 308 | 309 | /* Nothing immediately available, so generate a new arena. */ 310 | a = _int_new_arena(size); 311 | if(!a) 312 | return 0; 313 | 314 | tsd_setspecific(arena_key, (void *)a); 315 | mutex_init(&a->mutex); 316 | err = mutex_lock(&a->mutex); /* remember result */ 317 | 318 | /* Add the new arena to the global list. */ 319 | (void)mutex_lock(&list_lock); 320 | a->next = main_arena.next; 321 | atomic_write_barrier (); 322 | main_arena.next = a; 323 | (void)mutex_unlock(&list_lock); 324 | 325 | if(err) /* locking failed; keep arena for further attempts later */ 326 | return 0; 327 | 328 | THREAD_STAT(++(a->stat_lock_loop)); 329 | return a; 330 | } 331 | 332 | /* Create a new arena with room for a chunk of size "size". */ 333 | 334 | static inline void FMALLOC_CALL_MUNMAP(void *mem) 335 | { 336 | struct fm_super *s; 337 | if (__malloc_initialized < 0) { 338 | fprintf(stderr, "malloc is not initialized, this should not happen\n"); 339 | return; 340 | } 341 | s = (struct fm_super *) (__fm_addr_base); 342 | if (s->magic != FMALLOC_MAGIC) { 343 | fprintf(stderr, "super block is not initialized, this should not happen\n"); 344 | return; 345 | } 346 | (void)mutex_lock(&main_arena.mutex); 347 | s->munmap_locked(mem); 348 | (void)mutex_unlock(&main_arena.mutex); 349 | } 350 | 351 | static inline void *FMALLOC_CALL_MMAP(void) 352 | { 353 | struct fm_super *s; 354 | void *ret; 355 | if (__malloc_initialized < 0) { 356 | fprintf(stderr, "malloc is not initialized, this should not happen\n"); 357 | return MAP_FAILED; 358 | } 359 | s = (struct fm_super *) (__fm_addr_base); 360 | if (s->magic != FMALLOC_MAGIC) { 361 | fprintf(stderr, "super block is not initialized, this should not happen\n"); 362 | return MAP_FAILED; 363 | } 364 | (void)mutex_lock(&main_arena.mutex); 365 | ret = s->mmap_locked(); 366 | (void)mutex_unlock(&main_arena.mutex); 367 | return ret; 368 | } 369 | 370 | static struct malloc_arena* 371 | _int_new_arena(size_t size) 372 | { 373 | struct malloc_arena* a; 374 | void *m; 375 | 376 | a = (struct malloc_arena *) FMALLOC_CALL_MMAP(); 377 | if ((char*)a == (char*)-1) 378 | return 0; 379 | 380 | m = create_mspace_with_base((char*)a + MSPACE_OFFSET, 381 | fmalloc_chunk_size - MSPACE_OFFSET, 382 | 0); 383 | 384 | if (!m) { 385 | FMALLOC_CALL_MUNMAP(a); 386 | a = 0; 387 | } else { 388 | /*a->next = NULL;*/ 389 | /*a->system_mem = a->max_system_mem = h->size;*/ 390 | } 391 | 392 | return a; 393 | } 394 | 395 | /*------------------------------------------------------------------------*/ 396 | 397 | /* Hook mechanism for proper initialization and atfork support. */ 398 | 399 | /* Define and initialize the hook variables. These weak definitions must 400 | appear before any use of the variables in a function. */ 401 | #ifndef weak_variable 402 | #ifndef _LIBC 403 | #define weak_variable /**/ 404 | #else 405 | /* In GNU libc we want the hook variables to be weak definitions to 406 | avoid a problem with Emacs. */ 407 | #define weak_variable weak_function 408 | #endif 409 | #endif 410 | 411 | #if !(USE_STARTER & 2) 412 | # define free_hook_ini NULL 413 | /* Forward declarations. */ 414 | static void* malloc_hook_ini (size_t sz, const void *caller); 415 | static void* realloc_hook_ini (void* ptr, size_t sz, const void* caller); 416 | static void* memalign_hook_ini (size_t alignment, size_t sz, 417 | const void* caller); 418 | #else 419 | # define free_hook_ini free_starter 420 | # define malloc_hook_ini malloc_starter 421 | # define realloc_hook_ini NULL 422 | # define memalign_hook_ini memalign_starter 423 | #endif 424 | 425 | static void weak_variable (*__malloc_initialize_hook) (void) = NULL; 426 | static void weak_variable (*__free_hook) (void * __ptr, const void *) 427 | = free_hook_ini; 428 | static void * weak_variable (*__malloc_hook) (size_t __size, const void *) 429 | = malloc_hook_ini; 430 | static void * weak_variable (*__realloc_hook) 431 | (void * __ptr, size_t __size, const void *) = realloc_hook_ini; 432 | static void * weak_variable (*__memalign_hook) 433 | (size_t __alignment, size_t __size, const void *) = memalign_hook_ini; 434 | /*void weak_variable (*__after_morecore_hook) (void) = NULL;*/ 435 | 436 | /* The initial hooks just call the initialization routine, then do the 437 | normal work. */ 438 | 439 | #if !(USE_STARTER & 2) 440 | static 441 | #endif 442 | void ptmalloc_init(void); 443 | 444 | #if !(USE_STARTER & 2) 445 | 446 | static void* 447 | malloc_hook_ini(size_t sz, const void * caller) 448 | { 449 | __malloc_hook = NULL; 450 | ptmalloc_init(); 451 | return public_mALLOc(sz); 452 | } 453 | 454 | static void * 455 | realloc_hook_ini(void *ptr, size_t sz, const void * caller) 456 | { 457 | __malloc_hook = NULL; 458 | __realloc_hook = NULL; 459 | ptmalloc_init(); 460 | return public_rEALLOc(ptr, sz); 461 | } 462 | 463 | static void* 464 | memalign_hook_ini(size_t alignment, size_t sz, const void * caller) 465 | { 466 | __memalign_hook = NULL; 467 | ptmalloc_init(); 468 | return public_mEMALIGn(alignment, sz); 469 | } 470 | 471 | #endif /* !(USE_STARTER & 2) */ 472 | 473 | /*----------------------------------------------------------------------*/ 474 | 475 | #if !defined NO_THREADS && USE_STARTER 476 | 477 | /* The following hooks are used when the global initialization in 478 | ptmalloc_init() hasn't completed yet. */ 479 | 480 | static void* 481 | malloc_starter(size_t sz, const void *caller) 482 | { 483 | void* victim; 484 | 485 | /*ptmalloc_init_minimal();*/ 486 | victim = mspace_malloc(arena_to_mspace(&main_arena), sz); 487 | THREAD_STAT(++main_arena.stat_starter); 488 | 489 | return victim; 490 | } 491 | 492 | static void* 493 | memalign_starter(size_t align, size_t sz, const void *caller) 494 | { 495 | void* victim; 496 | 497 | /*ptmalloc_init_minimal();*/ 498 | victim = mspace_memalign(arena_to_mspace(&main_arena), align, sz); 499 | THREAD_STAT(++main_arena.stat_starter); 500 | 501 | return victim; 502 | } 503 | 504 | static void 505 | free_starter(void* mem, const void *caller) 506 | { 507 | if (mem) { 508 | mchunkptr p = mem2chunk(mem); 509 | void *msp = arena_to_mspace(&main_arena); 510 | if (is_mmapped(p)) 511 | munmap_chunk(msp, p); 512 | else 513 | mspace_free(msp, mem); 514 | } 515 | THREAD_STAT(++main_arena.stat_starter); 516 | } 517 | 518 | #endif /* !defined NO_THREADS && USE_STARTER */ 519 | 520 | /*----------------------------------------------------------------------*/ 521 | 522 | #ifndef NO_THREADS 523 | 524 | /* atfork support. */ 525 | 526 | static void * (*save_malloc_hook) (size_t __size, const void *); 527 | # if !defined _LIBC || !defined USE_TLS || (defined SHARED && !USE___THREAD) 528 | static void * (*save_memalign_hook) (size_t __align, size_t __size, 529 | const void *); 530 | # endif 531 | static void (*save_free_hook) (void * __ptr, const void *); 532 | static void* save_arena; 533 | 534 | /* Magic value for the thread-specific arena pointer when 535 | malloc_atfork() is in use. */ 536 | 537 | #define ATFORK_ARENA_PTR ((void*)-1) 538 | 539 | /* The following hooks are used while the `atfork' handling mechanism 540 | is active. */ 541 | 542 | static void* 543 | malloc_atfork(size_t sz, const void *caller) 544 | { 545 | void *vptr = NULL; 546 | 547 | tsd_getspecific(arena_key, vptr); 548 | if(vptr == ATFORK_ARENA_PTR) { 549 | /* We are the only thread that may allocate at all. */ 550 | return mspace_malloc(arena_to_mspace(&main_arena), sz); 551 | } else { 552 | /* Suspend the thread until the `atfork' handlers have completed. 553 | By that time, the hooks will have been reset as well, so that 554 | mALLOc() can be used again. */ 555 | (void)mutex_lock(&list_lock); 556 | (void)mutex_unlock(&list_lock); 557 | return public_mALLOc(sz); 558 | } 559 | } 560 | 561 | static void 562 | free_atfork(void* mem, const void *caller) 563 | { 564 | void *vptr = NULL; 565 | struct malloc_arena *ar_ptr; 566 | mchunkptr p; /* chunk corresponding to mem */ 567 | 568 | if (mem == 0) /* free(0) has no effect */ 569 | return; 570 | 571 | p = mem2chunk(mem); 572 | 573 | if (is_mmapped(p)) { /* release mmapped memory. */ 574 | ar_ptr = arena_for_mmap_chunk(p); 575 | munmap_chunk(arena_to_mspace(ar_ptr), p); 576 | return; 577 | } 578 | 579 | ar_ptr = arena_for_chunk(p); 580 | tsd_getspecific(arena_key, vptr); 581 | if(vptr != ATFORK_ARENA_PTR) 582 | (void)mutex_lock(&ar_ptr->mutex); 583 | mspace_free(arena_to_mspace(ar_ptr), mem); 584 | if(vptr != ATFORK_ARENA_PTR) 585 | (void)mutex_unlock(&ar_ptr->mutex); 586 | } 587 | 588 | /* The following two functions are registered via thread_atfork() to 589 | make sure that the mutexes remain in a consistent state in the 590 | fork()ed version of a thread. Also adapt the malloc and free hooks 591 | temporarily, because the `atfork' handler mechanism may use 592 | malloc/free internally (e.g. in LinuxThreads). */ 593 | 594 | static void 595 | ptmalloc_lock_all (void) 596 | { 597 | struct malloc_arena* ar_ptr; 598 | 599 | if(__malloc_initialized < 1) 600 | return; 601 | (void)mutex_lock(&list_lock); 602 | for(ar_ptr = &main_arena;;) { 603 | (void)mutex_lock(&ar_ptr->mutex); 604 | ar_ptr = ar_ptr->next; 605 | if(ar_ptr == &main_arena) 606 | break; 607 | } 608 | save_malloc_hook = __malloc_hook; 609 | save_free_hook = __free_hook; 610 | __malloc_hook = malloc_atfork; 611 | __free_hook = free_atfork; 612 | /* Only the current thread may perform malloc/free calls now. */ 613 | tsd_getspecific(arena_key, save_arena); 614 | tsd_setspecific(arena_key, ATFORK_ARENA_PTR); 615 | } 616 | 617 | static void 618 | ptmalloc_unlock_all (void) 619 | { 620 | struct malloc_arena *ar_ptr; 621 | 622 | if(__malloc_initialized < 1) 623 | return; 624 | tsd_setspecific(arena_key, save_arena); 625 | __malloc_hook = save_malloc_hook; 626 | __free_hook = save_free_hook; 627 | for(ar_ptr = &main_arena;;) { 628 | (void)mutex_unlock(&ar_ptr->mutex); 629 | ar_ptr = ar_ptr->next; 630 | if(ar_ptr == &main_arena) break; 631 | } 632 | (void)mutex_unlock(&list_lock); 633 | } 634 | 635 | #ifdef __linux__ 636 | 637 | /* In LinuxThreads, unlocking a mutex in the child process after a 638 | fork() is currently unsafe, whereas re-initializing it is safe and 639 | does not leak resources. Therefore, a special atfork handler is 640 | installed for the child. */ 641 | 642 | static void 643 | ptmalloc_unlock_all2(void) 644 | { 645 | struct malloc_arena *ar_ptr; 646 | 647 | if(__malloc_initialized < 1) 648 | return; 649 | #if defined _LIBC || 1 /*defined MALLOC_HOOKS*/ 650 | tsd_setspecific(arena_key, save_arena); 651 | __malloc_hook = save_malloc_hook; 652 | __free_hook = save_free_hook; 653 | #endif 654 | for(ar_ptr = &main_arena;;) { 655 | (void)mutex_init(&ar_ptr->mutex); 656 | ar_ptr = ar_ptr->next; 657 | if(ar_ptr == &main_arena) break; 658 | } 659 | (void)mutex_init(&list_lock); 660 | } 661 | 662 | #else 663 | 664 | #define ptmalloc_unlock_all2 ptmalloc_unlock_all 665 | 666 | #endif 667 | 668 | #endif /* !defined NO_THREADS */ 669 | 670 | /*---------------------------------------------------------------------*/ 671 | extern int init_mparams(void); 672 | #if !(USE_STARTER & 2) 673 | static 674 | #endif 675 | void 676 | ptmalloc_init(void) 677 | { 678 | const char* s; 679 | int secure = 0; 680 | void *mspace; 681 | 682 | if(__malloc_initialized >= 0) return; 683 | __malloc_initialized = 0; 684 | 685 | /*if (mp_.pagesize == 0) 686 | ptmalloc_init_minimal();*/ 687 | 688 | #ifndef NO_THREADS 689 | # if USE_STARTER & 1 690 | /* With some threads implementations, creating thread-specific data 691 | or initializing a mutex may call malloc() itself. Provide a 692 | simple starter version (realloc() won't work). */ 693 | save_malloc_hook = __malloc_hook; 694 | save_memalign_hook = __memalign_hook; 695 | save_free_hook = __free_hook; 696 | __malloc_hook = malloc_starter; 697 | __memalign_hook = memalign_starter; 698 | __free_hook = free_starter; 699 | # ifdef _LIBC 700 | /* Initialize the pthreads interface. */ 701 | if (__pthread_initialize != NULL) 702 | __pthread_initialize(); 703 | # endif /* !defined _LIBC */ 704 | # endif /* USE_STARTER & 1 */ 705 | #endif /* !defined NO_THREADS */ 706 | if (main_arena.__malloc_initialized <= 0) { 707 | mutex_init(&main_arena.mutex); 708 | main_arena.next = &main_arena; 709 | mspace = create_mspace_with_base((char*)&main_arena + MSPACE_OFFSET, 710 | fmalloc_chunk_size - MSPACE_OFFSET, 711 | 0); 712 | assert(mspace == arena_to_mspace(&main_arena)); 713 | if (mspace != arena_to_mspace(&main_arena)) { 714 | fprintf(stderr, "invalid mspace creation\n"); 715 | exit(1); 716 | } 717 | } else 718 | init_mparams(); 719 | 720 | mutex_init(&list_lock); 721 | tsd_key_create(&arena_key, NULL); 722 | tsd_setspecific(arena_key, (void *)&main_arena); 723 | thread_atfork(ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2); 724 | #ifndef NO_THREADS 725 | # if USE_STARTER & 1 726 | __malloc_hook = save_malloc_hook; 727 | __memalign_hook = save_memalign_hook; 728 | __free_hook = save_free_hook; 729 | # endif 730 | # if USE_STARTER & 2 731 | __malloc_hook = 0; 732 | __memalign_hook = 0; 733 | __free_hook = 0; 734 | # endif 735 | #endif 736 | #ifdef _LIBC 737 | secure = __libc_enable_secure; 738 | #else 739 | if (! secure) { 740 | if ((s = getenv("MALLOC_TRIM_THRESHOLD_"))) 741 | public_mALLOPt(M_TRIM_THRESHOLD, atoi(s)); 742 | if ((s = getenv("MALLOC_TOP_PAD_")) || 743 | (s = getenv("MALLOC_GRANULARITY_"))) 744 | public_mALLOPt(M_GRANULARITY, atoi(s)); 745 | if ((s = getenv("MALLOC_MMAP_THRESHOLD_"))) 746 | public_mALLOPt(M_MMAP_THRESHOLD, atoi(s)); 747 | /*if ((s = getenv("MALLOC_MMAP_MAX_"))) this is no longer available 748 | public_mALLOPt(M_MMAP_MAX, atoi(s));*/ 749 | } 750 | s = getenv("MALLOC_CHECK_"); 751 | #endif 752 | if (s) { 753 | /*if(s[0]) mALLOPt(M_CHECK_ACTION, (int)(s[0] - '0')); 754 | __malloc_check_init();*/ 755 | } 756 | if (__malloc_initialize_hook != NULL) 757 | (*__malloc_initialize_hook)(); 758 | __malloc_initialized = 1; 759 | if (main_arena.__malloc_initialized != 1) 760 | main_arena.__malloc_initialized = 1; 761 | } 762 | 763 | /*------------------------ Public wrappers. --------------------------------*/ 764 | 765 | void* 766 | public_mALLOc(size_t bytes) 767 | { 768 | struct malloc_arena* ar_ptr; 769 | void *victim; 770 | 771 | void * (*hook) (size_t, const void *) = __malloc_hook; 772 | if (hook != NULL) 773 | return (*hook)(bytes, RETURN_ADDRESS (0)); 774 | 775 | arena_get(ar_ptr, bytes + FOOTER_OVERHEAD); 776 | if (!ar_ptr) 777 | return 0; 778 | if (ar_ptr != &main_arena) 779 | bytes += FOOTER_OVERHEAD; 780 | victim = mspace_malloc(arena_to_mspace(ar_ptr), bytes); 781 | if (victim && ar_ptr != &main_arena) 782 | set_non_main_arena(victim, ar_ptr); 783 | (void)mutex_unlock(&ar_ptr->mutex); 784 | assert(!victim || is_mmapped(mem2chunk(victim)) || 785 | ar_ptr == arena_for_chunk(mem2chunk(victim))); 786 | return victim; 787 | } 788 | #ifdef libc_hidden_def 789 | libc_hidden_def(public_mALLOc) 790 | #endif 791 | 792 | void 793 | public_fREe(void* mem) 794 | { 795 | struct malloc_arena* ar_ptr; 796 | mchunkptr p; /* chunk corresponding to mem */ 797 | 798 | void (*hook) (void *, const void *) = __free_hook; 799 | if (hook != NULL) { 800 | (*hook)(mem, RETURN_ADDRESS (0)); 801 | return; 802 | } 803 | 804 | if (mem == 0) /* free(0) has no effect */ 805 | return; 806 | 807 | p = mem2chunk(mem); 808 | 809 | if (is_mmapped(p)) { /* release mmapped memory. */ 810 | ar_ptr = arena_for_mmap_chunk(p); 811 | munmap_chunk(arena_to_mspace(ar_ptr), p); 812 | return; 813 | } 814 | 815 | ar_ptr = arena_for_chunk(p); 816 | #if THREAD_STATS 817 | if(!mutex_trylock(&ar_ptr->mutex)) 818 | ++(ar_ptr->stat_lock_direct); 819 | else { 820 | (void)mutex_lock(&ar_ptr->mutex); 821 | ++(ar_ptr->stat_lock_wait); 822 | } 823 | #else 824 | (void)mutex_lock(&ar_ptr->mutex); 825 | #endif 826 | mspace_free(arena_to_mspace(ar_ptr), mem); 827 | (void)mutex_unlock(&ar_ptr->mutex); 828 | } 829 | #ifdef libc_hidden_def 830 | libc_hidden_def (public_fREe) 831 | #endif 832 | 833 | void* 834 | public_rEALLOc(void* oldmem, size_t bytes) 835 | { 836 | struct malloc_arena* ar_ptr; 837 | 838 | mchunkptr oldp; /* chunk corresponding to oldmem */ 839 | 840 | void* newp; /* chunk to return */ 841 | 842 | void * (*hook) (void *, size_t, const void *) = __realloc_hook; 843 | if (hook != NULL) 844 | return (*hook)(oldmem, bytes, RETURN_ADDRESS (0)); 845 | 846 | #if REALLOC_ZERO_BYTES_FREES 847 | if (bytes == 0 && oldmem != NULL) { public_fREe(oldmem); return 0; } 848 | #endif 849 | 850 | /* realloc of null is supposed to be same as malloc */ 851 | if (oldmem == 0) 852 | return public_mALLOc(bytes); 853 | 854 | oldp = mem2chunk(oldmem); 855 | if (is_mmapped(oldp)) 856 | ar_ptr = arena_for_mmap_chunk(oldp); /* FIXME: use mmap_resize */ 857 | else 858 | ar_ptr = arena_for_chunk(oldp); 859 | #if THREAD_STATS 860 | if(!mutex_trylock(&ar_ptr->mutex)) 861 | ++(ar_ptr->stat_lock_direct); 862 | else { 863 | (void)mutex_lock(&ar_ptr->mutex); 864 | ++(ar_ptr->stat_lock_wait); 865 | } 866 | #else 867 | (void)mutex_lock(&ar_ptr->mutex); 868 | #endif 869 | 870 | #ifndef NO_THREADS 871 | /* As in malloc(), remember this arena for the next allocation. */ 872 | tsd_setspecific(arena_key, (void *)ar_ptr); 873 | #endif 874 | 875 | if (ar_ptr != &main_arena) 876 | bytes += FOOTER_OVERHEAD; 877 | newp = mspace_realloc(arena_to_mspace(ar_ptr), oldmem, bytes); 878 | 879 | if (newp && ar_ptr != &main_arena) 880 | set_non_main_arena(newp, ar_ptr); 881 | (void)mutex_unlock(&ar_ptr->mutex); 882 | 883 | assert(!newp || is_mmapped(mem2chunk(newp)) || 884 | ar_ptr == arena_for_chunk(mem2chunk(newp))); 885 | return newp; 886 | } 887 | #ifdef libc_hidden_def 888 | libc_hidden_def (public_rEALLOc) 889 | #endif 890 | 891 | void* 892 | public_mEMALIGn(size_t alignment, size_t bytes) 893 | { 894 | struct malloc_arena* ar_ptr; 895 | void *p; 896 | 897 | void * (*hook) (size_t, size_t, const void *) = __memalign_hook; 898 | if (hook != NULL) 899 | return (*hook)(alignment, bytes, RETURN_ADDRESS (0)); 900 | 901 | /* If need less alignment than we give anyway, just relay to malloc */ 902 | if (alignment <= MALLOC_ALIGNMENT) return public_mALLOc(bytes); 903 | 904 | /* Otherwise, ensure that it is at least a minimum chunk size */ 905 | if (alignment < MIN_CHUNK_SIZE) 906 | alignment = MIN_CHUNK_SIZE; 907 | 908 | arena_get(ar_ptr, 909 | bytes + FOOTER_OVERHEAD + alignment + MIN_CHUNK_SIZE); 910 | if(!ar_ptr) 911 | return 0; 912 | 913 | if (ar_ptr != &main_arena) 914 | bytes += FOOTER_OVERHEAD; 915 | p = mspace_memalign(arena_to_mspace(ar_ptr), alignment, bytes); 916 | 917 | if (p && ar_ptr != &main_arena) 918 | set_non_main_arena(p, ar_ptr); 919 | (void)mutex_unlock(&ar_ptr->mutex); 920 | 921 | assert(!p || is_mmapped(mem2chunk(p)) || 922 | ar_ptr == arena_for_chunk(mem2chunk(p))); 923 | return p; 924 | } 925 | #ifdef libc_hidden_def 926 | libc_hidden_def (public_mEMALIGn) 927 | #endif 928 | 929 | void* 930 | public_vALLOc(size_t bytes) 931 | { 932 | struct malloc_arena* ar_ptr; 933 | void *p; 934 | 935 | if(__malloc_initialized < 0) 936 | ptmalloc_init (); 937 | arena_get(ar_ptr, bytes + FOOTER_OVERHEAD + MIN_CHUNK_SIZE); 938 | if(!ar_ptr) 939 | return 0; 940 | if (ar_ptr != &main_arena) 941 | bytes += FOOTER_OVERHEAD; 942 | p = mspace_memalign(arena_to_mspace(ar_ptr), 4096, bytes); 943 | 944 | if (p && ar_ptr != &main_arena) 945 | set_non_main_arena(p, ar_ptr); 946 | (void)mutex_unlock(&ar_ptr->mutex); 947 | return p; 948 | } 949 | 950 | int 951 | public_pMEMALIGn (void **memptr, size_t alignment, size_t size) 952 | { 953 | void *mem; 954 | 955 | /* Test whether the SIZE argument is valid. It must be a power of 956 | two multiple of sizeof (void *). */ 957 | if (alignment % sizeof (void *) != 0 958 | || !my_powerof2 (alignment / sizeof (void *)) != 0 959 | || alignment == 0) 960 | return EINVAL; 961 | 962 | mem = public_mEMALIGn (alignment, size); 963 | 964 | if (mem != NULL) { 965 | *memptr = mem; 966 | return 0; 967 | } 968 | 969 | return ENOMEM; 970 | } 971 | 972 | void* 973 | public_cALLOc(size_t n_elements, size_t elem_size) 974 | { 975 | struct malloc_arena* ar_ptr; 976 | size_t bytes, sz; 977 | void* mem; 978 | void * (*hook) (size_t, const void *) = __malloc_hook; 979 | 980 | /* size_t is unsigned so the behavior on overflow is defined. */ 981 | bytes = n_elements * elem_size; 982 | #define HALF_INTERNAL_SIZE_T \ 983 | (((size_t) 1) << (8 * sizeof (size_t) / 2)) 984 | if (__builtin_expect ((n_elements | elem_size) >= HALF_INTERNAL_SIZE_T, 0)) { 985 | if (elem_size != 0 && bytes / elem_size != n_elements) { 986 | /*MALLOC_FAILURE_ACTION;*/ 987 | return 0; 988 | } 989 | } 990 | 991 | if (hook != NULL) { 992 | sz = bytes; 993 | mem = (*hook)(sz, RETURN_ADDRESS (0)); 994 | if(mem == 0) 995 | return 0; 996 | #ifdef HAVE_MEMCPY 997 | return memset(mem, 0, sz); 998 | #else 999 | while(sz > 0) ((char*)mem)[--sz] = 0; /* rather inefficient */ 1000 | return mem; 1001 | #endif 1002 | } 1003 | 1004 | arena_get(ar_ptr, bytes + FOOTER_OVERHEAD); 1005 | if(!ar_ptr) 1006 | return 0; 1007 | 1008 | if (ar_ptr != &main_arena) 1009 | bytes += FOOTER_OVERHEAD; 1010 | mem = mspace_calloc(arena_to_mspace(ar_ptr), bytes, 1); 1011 | 1012 | if (mem && ar_ptr != &main_arena) 1013 | set_non_main_arena(mem, ar_ptr); 1014 | (void)mutex_unlock(&ar_ptr->mutex); 1015 | 1016 | assert(!mem || is_mmapped(mem2chunk(mem)) || 1017 | ar_ptr == arena_for_chunk(mem2chunk(mem))); 1018 | 1019 | return mem; 1020 | } 1021 | 1022 | void** 1023 | public_iCALLOc(size_t n, size_t elem_size, void* chunks[]) 1024 | { 1025 | struct malloc_arena* ar_ptr; 1026 | void** m; 1027 | 1028 | arena_get(ar_ptr, n*(elem_size + FOOTER_OVERHEAD)); 1029 | if (!ar_ptr) 1030 | return 0; 1031 | 1032 | if (ar_ptr != &main_arena) 1033 | elem_size += FOOTER_OVERHEAD; 1034 | m = mspace_independent_calloc(arena_to_mspace(ar_ptr), n, elem_size, chunks); 1035 | 1036 | if (m && ar_ptr != &main_arena) { 1037 | while (n > 0) 1038 | set_non_main_arena(m[--n], ar_ptr); 1039 | } 1040 | (void)mutex_unlock(&ar_ptr->mutex); 1041 | return m; 1042 | } 1043 | 1044 | void** 1045 | public_iCOMALLOc(size_t n, size_t sizes[], void* chunks[]) 1046 | { 1047 | struct malloc_arena* ar_ptr; 1048 | size_t* m_sizes; 1049 | size_t i; 1050 | void** m; 1051 | 1052 | arena_get(ar_ptr, n*sizeof(size_t)); 1053 | if (!ar_ptr) 1054 | return 0; 1055 | 1056 | if (ar_ptr != &main_arena) { 1057 | /* Temporary m_sizes[] array is ugly but it would be surprising to 1058 | change the original sizes[]... */ 1059 | m_sizes = (size_t *) mspace_malloc(arena_to_mspace(ar_ptr), n*sizeof(size_t)); 1060 | if (!m_sizes) { 1061 | (void)mutex_unlock(&ar_ptr->mutex); 1062 | return 0; 1063 | } 1064 | for (i=0; imutex); 1072 | return 0; 1073 | } 1074 | set_non_main_arena(chunks, ar_ptr); 1075 | } 1076 | } else 1077 | m_sizes = sizes; 1078 | 1079 | m = mspace_independent_comalloc(arena_to_mspace(ar_ptr), n, m_sizes, chunks); 1080 | 1081 | if (ar_ptr != &main_arena) { 1082 | mspace_free(arena_to_mspace(ar_ptr), m_sizes); 1083 | if (m) 1084 | for (i=0; imutex); 1088 | return m; 1089 | } 1090 | 1091 | #if 0 && !defined _LIBC 1092 | 1093 | void 1094 | public_cFREe(void* m) 1095 | { 1096 | public_fREe(m); 1097 | } 1098 | 1099 | #endif /* _LIBC */ 1100 | 1101 | int 1102 | public_mTRIm(size_t s) 1103 | { 1104 | int result; 1105 | 1106 | (void)mutex_lock(&main_arena.mutex); 1107 | result = mspace_trim(arena_to_mspace(&main_arena), s); 1108 | (void)mutex_unlock(&main_arena.mutex); 1109 | return result; 1110 | } 1111 | 1112 | size_t 1113 | public_mUSABLe(void* mem) 1114 | { 1115 | if (mem != 0) { 1116 | mchunkptr p = mem2chunk(mem); 1117 | if (cinuse(p)) 1118 | return chunksize(p) - overhead_for(p); 1119 | } 1120 | return 0; 1121 | } 1122 | 1123 | int 1124 | public_mALLOPt(int p, int v) 1125 | { 1126 | int result; 1127 | result = mspace_mallopt(p, v); 1128 | return result; 1129 | } 1130 | 1131 | void 1132 | public_mSTATs(void) 1133 | { 1134 | int i; 1135 | struct malloc_arena* ar_ptr; 1136 | /*unsigned long in_use_b, system_b, avail_b;*/ 1137 | #if THREAD_STATS 1138 | long stat_lock_direct = 0, stat_lock_loop = 0, stat_lock_wait = 0; 1139 | #endif 1140 | if(__malloc_initialized < 0) 1141 | ptmalloc_init (); 1142 | for (i=0, ar_ptr = &main_arena;; ++i) { 1143 | struct malloc_state* msp = (struct malloc_state *) arena_to_mspace(ar_ptr); 1144 | 1145 | fprintf(stderr, "Arena %d:\n", i); 1146 | mspace_malloc_stats(msp); 1147 | #if THREAD_STATS 1148 | stat_lock_direct += ar_ptr->stat_lock_direct; 1149 | stat_lock_loop += ar_ptr->stat_lock_loop; 1150 | stat_lock_wait += ar_ptr->stat_lock_wait; 1151 | #endif 1152 | if (MALLOC_DEBUG > 1) { 1153 | struct malloc_segment* mseg = &msp->seg; 1154 | while (mseg) { 1155 | fprintf(stderr, " seg %08lx-%08lx\n", (unsigned long)mseg->base, 1156 | (unsigned long)(mseg->base + mseg->size)); 1157 | mseg = mseg->next; 1158 | } 1159 | } 1160 | ar_ptr = ar_ptr->next; 1161 | if (ar_ptr == &main_arena) 1162 | break; 1163 | } 1164 | #if THREAD_STATS 1165 | fprintf(stderr, "locked directly = %10ld\n", stat_lock_direct); 1166 | fprintf(stderr, "locked in loop = %10ld\n", stat_lock_loop); 1167 | fprintf(stderr, "locked waiting = %10ld\n", stat_lock_wait); 1168 | fprintf(stderr, "locked total = %10ld\n", 1169 | stat_lock_direct + stat_lock_loop + stat_lock_wait); 1170 | if (main_arena.stat_starter > 0) 1171 | fprintf(stderr, "starter hooks = %10ld\n", main_arena.stat_starter); 1172 | #endif 1173 | } 1174 | 1175 | void do_ptmalloc_init(unsigned long chunk_size) 1176 | { 1177 | fmalloc_chunk_size = chunk_size; 1178 | 1179 | ptmalloc_init (); 1180 | } 1181 | 1182 | /* 1183 | * Local variables: 1184 | * c-basic-offset: 2 1185 | * End: 1186 | */ 1187 | --------------------------------------------------------------------------------