diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c34b80..9a80679 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,20 @@ cmake_minimum_required(VERSION 3.5) +include (CheckFunctionExists) +include (CheckSymbolExists) + +check_function_exists(mmap HAVE_MMAP) +if(HAVE_MMAP) + check_symbol_exists(MAP_ANONYMOUS sys/mman.h HAVE_MAP_ANONYMOUS) + if(NOT HAVE_MAP_ANONYMOUS) + check_symbol_exists(MAP_ANON sys/mman.h HAVE_MAP_ANON) + endif() + check_function_exists(mremap HAVE_MREMAP) +endif() +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Werror -std=c99") -INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) add_library (allocation src/allocation.c) @@ -10,6 +22,12 @@ add_library (coroutine src/coroutine.c) target_link_libraries (coroutine LINK_PUBLIC allocation) add_library (default_allocator src/default_allocator.c) +set(ALLOCATORS default_allocator) + +if(HAVE_MMAP) + add_library (mmap_allocator src/mmap_allocator.c) + set(ALLOCATORS ${ALLOCATORS} mmap_allocator) +endif() add_executable (test-coroutine test/coroutine.c) -target_link_libraries (test-coroutine LINK_PUBLIC coroutine default_allocator) +target_link_libraries (test-coroutine LINK_PUBLIC coroutine ${ALLOCATORS}) diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..1909349 --- /dev/null +++ b/config.h.in @@ -0,0 +1,4 @@ +#cmakedefine HAVE_MMAP +#cmakedefine HAVE_MREMAP +#cmakedefine HAVE_MAP_ANONYMOUS +#cmakedefine HAVE_MAP_ANON diff --git a/src/mmap_allocator.c b/src/mmap_allocator.c new file mode 100644 index 0000000..b142391 --- /dev/null +++ b/src/mmap_allocator.c @@ -0,0 +1,139 @@ +/* threadless.io + * Copyright (c) 2016 Justin R. Cutler + * Licensed under the MIT License. See LICENSE file in the project root for + * full license information. + */ +/** @file + * @c mmap(3) allocator implementation + * @author Justin R. Cutler + */ + +#define _GNU_SOURCE + +/* HAVE_* */ +#include "config.h" + +/* errno, ENOSYS */ +#include +/* size_t, NULL */ +#include +#ifndef HAVE_MREMAP +/* memcpy */ +# include +#endif + +/* mmap, munmap, PROT_*, MAP_*, mremap(optional), MREMAP_*(optional) */ +#include +/* sysconf, _SC_PAGESIZE */ +#include + +/* ... */ +#include + + +#ifndef HAVE_MMAP +# error Need mmap() +#endif + +#if defined(HAVE_MAP_ANONYMOUS) || defined(HAVE_MAP_ANON) +# ifndef HAVE_MAP_ANONYMOUS +# define MAP_ANONYMOUS MAP_ANON +# endif +#else +# error Need MAP_ANONYMOUS or MAP_ANON +#endif + + +static void *do_mmap(size_t size) +{ + return mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, + -1, 0); +} + + +static void *do_mremap(void *memory, size_t old_size, size_t new_size) +{ + void *new_memory; +#ifdef HAVE_MREMAP + new_memory = mremap(memory, old_size, new_size, MREMAP_MAYMOVE); +#else + /* slow remap: mmap + copy + munmap */ + size_t copy_size = (new_size < old_size) ? new_size : old_size; + new_memory = do_mmap(new_size); + if (MAP_FAILED != new_memory) { + memcpy(new_memory, memory, copy_size); + (void) munmap(memory, old_size); + } +#endif + return new_memory; +} + + +static int mmap_allocate(allocation_t *allocation, size_t size) +{ + static size_t page_size = 0; + static size_t page_mask = 0; + void *new_memory = MAP_FAILED; + + if (!page_size) { + /* get page size */ + long result = sysconf(_SC_PAGESIZE); + if (result > 0 && !(result & (result - 1))) { + page_size = (size_t) result; + page_mask = page_size - 1; + } else { + /* not a power of 2 */ + errno = ENOSYS; + return -1; + } + } + + /* round size up to multiple of page_size */ + size = (size + page_size - 1) & ~page_mask; + + /* no action required */ + if (size == allocation->size) { + return 0; + } + + if (0 == size) { + if (NULL != allocation->memory && 0 != allocation->size) { + (void)munmap(allocation->memory, allocation->size); + } + new_memory = NULL; + } else if (0 == allocation->size) { + new_memory = do_mmap(size); + } else { + new_memory = do_mremap(allocation->memory, allocation->size, size); + } + + if (MAP_FAILED == new_memory) { + /* operation failed */ + return -1; + } + + /* update allocation */ + allocation->memory = new_memory; + allocation->size = size; + + return 0; +} + + +static void mmap_destroy(allocator_t *allocator) +{ + /* ignore allocator */ + (void) allocator; +} + + +static allocator_t mmap_allocator = { + .allocate = mmap_allocate, + .destroy = mmap_destroy, +}; + + +allocator_t *allocator_get_mmap(void) +{ + return &mmap_allocator; +} diff --git a/test/coroutine.c b/test/coroutine.c index 2893fb8..05ddc4d 100644 --- a/test/coroutine.c +++ b/test/coroutine.c @@ -8,6 +8,9 @@ * @author Justin R. Cutler */ +/* HAVE_* */ +#include "config.h" + /* printf, perror */ #include /* EXIT_SUCCESS, EXIT_FAILURE */ @@ -15,6 +18,10 @@ /* allocator_t, allocator_get_default, allocator_destroy */ #include +#ifdef HAVE_MMAP +/* allocator_get_mmap */ +# include +#endif /* ... */ #include @@ -93,14 +100,24 @@ fail: int main(int argc, char *argv[]) { int error; - allocator_t *allocator = allocator_get_default(); + allocator_t *allocator; (void) argc; (void) argv; + printf("default allocator:\n"); + allocator = allocator_get_default(); error = run(allocator); - allocator_destroy(allocator); +#ifdef HAVE_MMAP + if (!error) { + printf("mmap allocator:\n"); + allocator = allocator_get_mmap(); + error = run(allocator); + allocator_destroy(allocator); + } +#endif + return !error ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/threadless/mmap_allocator.h b/threadless/mmap_allocator.h new file mode 100644 index 0000000..28c2366 --- /dev/null +++ b/threadless/mmap_allocator.h @@ -0,0 +1,29 @@ +/* threadless.io + * Copyright (c) 2016 Justin R. Cutler + * Licensed under the MIT License. See LICENSE file in the project root for + * full license information. + */ +/** @file + * @c mmap(3) allocator interface definition + * @author Justin R. Cutler + */ +#ifndef THREADLESS_MMAP_ALLOCATOR_H +#define THREADLESS_MMAP_ALLOCATOR_H + +/* allocator_t */ +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Get @c mmap(3) allocator instance + * @returns default allocator + */ +allocator_t *allocator_get_mmap(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* THREADLESS_MMAP_ALLOCATOR_H */