diff --git a/include/my_sys.h b/include/my_sys.h index ec760c7bd1968..606e89b3a2d34 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -173,6 +173,8 @@ extern void *my_multi_malloc(PSI_memory_key key, myf MyFlags, ...); extern void *my_multi_malloc_large(PSI_memory_key key, myf MyFlags, ...); extern void *my_realloc(PSI_memory_key key, void *ptr, size_t size, myf MyFlags); extern void my_free(void *ptr); +extern void *my_mmap_alloc(PSI_memory_key key, size_t size, myf MyFlags); +extern int my_mmap_free(void *ptr); extern void *my_memdup(PSI_memory_key key, const void *from,size_t length,myf MyFlags); extern char *my_strdup(PSI_memory_key key, const char *from,myf MyFlags); extern char *my_strndup(PSI_memory_key key, const char *from, size_t length, myf MyFlags); diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c index 2509f8f63e2a2..ae507c58f9506 100644 --- a/mysys/my_malloc.c +++ b/mysys/my_malloc.c @@ -255,3 +255,136 @@ char *my_strndup(PSI_memory_key key, const char *from, size_t length, myf my_fla DBUG_RETURN(ptr); } + +#if defined(HAVE_MMAP) && !defined(_WIN32) +/* Solaris for example has only MAP_ANON, FreeBSD has MAP_ANONYMOUS and +MAP_ANON but MAP_ANONYMOUS is marked "for compatibility" */ +#if defined(MAP_ANONYMOUS) +#define OS_MAP_ANON MAP_ANONYMOUS +#elif defined(MAP_ANON) +#define OS_MAP_ANON MAP_ANON +#else +#error unsupported mmap - no MAP_ANON{YMOUS} +#endif +#endif /* HAVE_MMAP && !_WIN32 */ + + +/** + Allocate a block of memory using mmap + + @param key Key for memory instrumentation + @param size The size of memory requested in bytes + @param my_flags myf flags + + @return A pointer to mapped area; MAP_FAILED (i.e (void *) -1) on failure + */ + +void *my_mmap_alloc(PSI_memory_key key, size_t size, myf my_flags) +{ + my_memory_header *mh; + void *ptr; + DBUG_ENTER("my_mmap_alloc"); + +#if !defined(_WIN32) && !defined(HAVE_MMAP) + DBUG_RETURN(my_malloc(key, size, my_flags)); +#else + if (!(my_flags & (MY_WME | MY_FAE))) + my_flags|= my_global_flags; + + if (!size) + size= 1; + + size= ALIGN_SIZE(size); + if ((size + HEADER_SIZE) > SIZE_T_MAX - 1024L*1024L*16L) /* Wrong call */ + DBUG_RETURN(0); + + //TODO: simulate out of memory +#ifdef _WIN32 + DBUG_PRINT("VirtualAlloc",("size: %zu flags: %lu", size, my_flags)); + mh= (my_memory_header*) VirtualAlloc(NULL, size + HEADER_SIZE, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + if (mh == NULL) + { + my_errno= GetLastError(); +#elif defined(HAVE_MMAP) && !defined(_WIN32) + DBUG_PRINT("mmap",("size: %zu flags: %lu", size, my_flags)); + mh= (my_memory_header*) mmap(NULL, size + HEADER_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | OS_MAP_ANON, + -1, 0); + if (mh == MAP_FAILED) + { + my_errno= errno; +#endif + if (my_flags & MY_FAE) + error_handler_hook= fatal_error_handler_hook; + if (my_flags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_ERROR_LOG+ME_FATAL),size); + if (my_flags & MY_FAE) + abort(); + ptr= NULL; + } + else + { + int flag= MY_TEST(my_flags & MY_THREAD_SPECIFIC); + mh->m_size= size | flag; + mh->m_key= PSI_CALL_memory_alloc(key, size, &mh->m_owner); + update_malloc_size(size + HEADER_SIZE, flag); + ptr= HEADER_TO_USER(mh); + // mmap/VirtualAlloc is zero-filled (with MAP_ANONYMOUS), bzero not required + if (!(my_flags & MY_ZEROFILL)) + TRASH_ALLOC(ptr, size); + } + + DBUG_PRINT("exit",("ptr: %p", ptr)); + DBUG_RETURN(ptr); +#endif /* !defined(_WIN32) && !defined(HAVE_MMAP) */ +} + + +/** + De-allocate/Unmap a block of memory + + @param ptr pointer to mapped area + + @return 0 - on succes + -1 - on error, errno is set to indicate error + */ +int my_mmap_free(void *ptr) +{ + my_memory_header *mh; + size_t size; + my_bool flags; + DBUG_ENTER("my_mmap_free"); + + if (ptr == NULL) + DBUG_RETURN(0); + +#if !defined(_WIN32) && !defined(HAVE_MMAP) + my_free(ptr); +#else + mh= USER_TO_HEADER(ptr); + size= mh->m_size & ~1; + flags= mh->m_size & 1; + PSI_CALL_memory_free(mh->m_key, size, mh->m_owner); + update_malloc_size(-(longlong) size - HEADER_SIZE, flags); + +#ifdef _WIN32 + DBUG_PRINT("VirtualFree",("ptr: %p", ptr)); + if (!VirtualFree(mh, 0, MEM_RELEASE)) + { + my_errno= GetLastError(); + DBUG_RETURN(-1); + } +#elif defined(HAVE_MMAP) && !defined(_WIN32) + DBUG_PRINT("munmap",("ptr: %p", ptr)); + if (munmap(mh, size + HEADER_SIZE) != 0) + { + my_errno= errno; + DBUG_RETURN(-1); + } +#endif +#endif /* !defined(_WIN32) && !defined(HAVE_MMAP) */ + DBUG_RETURN(0); +} diff --git a/storage/heap/hp_block.c b/storage/heap/hp_block.c index 44bca36374615..fd6579d760cea 100644 --- a/storage/heap/hp_block.c +++ b/storage/heap/hp_block.c @@ -80,10 +80,10 @@ int hp_get_new_block(HP_SHARE *info, HP_BLOCK *block, size_t *alloc_length) (ulonglong)block->records_in_block * block->recbuffer); /* Alloc in blocks of powers of 2 */ *alloc_length= MY_MAX(*alloc_length, block->alloc_size); - if (!(root=(HP_PTRS*) my_malloc(hp_key_memory_HP_PTRS, *alloc_length, - MYF(MY_WME | - (info->internal ? - MY_THREAD_SPECIFIC : 0))))) + if (!(root=(HP_PTRS*) my_mmap_alloc(hp_key_memory_HP_PTRS, *alloc_length, + MYF(MY_WME | + (info->internal ? + MY_THREAD_SPECIFIC : 0))))) return 1; if (i == 0) @@ -150,7 +150,7 @@ uchar *hp_free_level(HP_BLOCK *block, uint level, HP_PTRS *pos, uchar *last_pos) } if ((uchar*) pos != last_pos) { - my_free(pos); + my_mmap_free(pos); return last_pos; } return next_ptr; /* next memory position */