diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c12bd7..3f8e99f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,12 @@ cmake_minimum_required(VERSION 3.5) set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Werror -std=c99") +add_library (allocation src/allocation.c) +target_include_directories (allocation PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + add_library (coroutine src/coroutine.c) target_include_directories (coroutine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries (coroutine LINK_PUBLIC allocation) add_executable (test-coroutine test/coroutine.c src/default_allocator.c) target_include_directories (coroutine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/allocation.c b/src/allocation.c new file mode 100644 index 0000000..4557646 --- /dev/null +++ b/src/allocation.c @@ -0,0 +1,42 @@ +/* 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 + * allocation interface implementation + * @author Justin R. Cutler + */ + +/* errno, ENOMEM */ +#include +/* size_t, NULL */ +#include + +/* ... */ +#include + + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t)-1) +#endif + +#define SQRT_SIZE_MAX_PLUS_1 ((size_t)1 << (sizeof(size_t) * 4)) + + +int allocation_realloc_array(allocation_t *allocation, size_t nmemb, + size_t size) +{ + /* test for multiplication overflow */ + if (((nmemb >= SQRT_SIZE_MAX_PLUS_1) || (size >= SQRT_SIZE_MAX_PLUS_1)) && + (nmemb != 0) && ((SIZE_MAX / nmemb) < size)) { + /* overflow detected */ + errno = ENOMEM; + return -1; + } + + size_t alloc_size = nmemb * size; + + /* perform allocator action */ + return allocation->allocator->allocate(allocation, alloc_size); +} diff --git a/src/coroutine.c b/src/coroutine.c index 9419112..ea35c02 100644 --- a/src/coroutine.c +++ b/src/coroutine.c @@ -19,6 +19,8 @@ /* ucontext_t, getcontext, makecontext, swapcontext */ #include +/* allocator_t allocation_t, allocation_init, allocation_realloc_array */ +#include /* ... */ #include @@ -29,11 +31,11 @@ enum { struct coroutine { - allocator_t *allocator; - ucontext_t context; - ucontext_t caller; - void *data; - int status; + allocation_t allocation; + ucontext_t context; + ucontext_t caller; + void *data; + int status; }; @@ -67,13 +69,15 @@ coroutine_t *coroutine_create(allocator_t *allocator, goto fail; } - coro = allocator_malloc(allocator, alloc_size); - if (NULL == coro) { + allocation_t allocation; + allocation_init(&allocation, allocator); + if (allocation_realloc_array(&allocation, 1, alloc_size)) { goto fail; } + coro = allocation.memory; memset(coro, 0, alloc_size); - coro->allocator = allocator; + coro->allocation = allocation; (void) getcontext(&coro->context); coro->context.uc_stack.ss_sp = coro + 1; coro->context.uc_stack.ss_size = stack_size; @@ -92,8 +96,9 @@ fail: void coroutine_destroy(coroutine_t *coro) { - if (NULL != coro && NULL != coro->allocator) { - allocator_free(coro->allocator, coro); + if (NULL != coro) { + allocation_t allocation = coro->allocation; + allocation_free(&allocation); } } diff --git a/src/default_allocator.c b/src/default_allocator.c index ea8ad9e..4000236 100644 --- a/src/default_allocator.c +++ b/src/default_allocator.c @@ -7,36 +7,31 @@ * default allocator implementation * @author Justin R. Cutler */ -/* errno, ENOMEM */ -#include + /* realloc */ #include /* ... */ -#include +#include -#ifndef SIZE_MAX -# define SIZE_MAX ((size_t)-1) -#endif - -#define SQRT_SIZE_MAX_PLUS_1 ((size_t)1 << (sizeof(size_t) * 4)) - - -static void *default_allocate(allocator_t *allocator, void *ptr, size_t nmemb, - size_t size) +static int default_allocate(allocation_t *allocation, size_t size) { - /* ignore allocator */ - (void) allocator; - /* test for multiplication overflow */ - if (((nmemb >= SQRT_SIZE_MAX_PLUS_1) || (size >= SQRT_SIZE_MAX_PLUS_1)) && - (nmemb != 0) && ((SIZE_MAX / nmemb) < size)) { - /* overflow detected */ - errno = ENOMEM; - return NULL; - } + void *new_memory; + /* perform allocator action */ - return realloc(ptr, size * nmemb); + new_memory = realloc(allocation->memory, size); + + if ((NULL == new_memory) && (size != 0)) { + /* realloc failed */ + return -1; + } + + /* update allocation */ + allocation->memory = new_memory; + allocation->size = size; + + return 0; } diff --git a/test/coroutine.c b/test/coroutine.c index 005faf0..2893fb8 100644 --- a/test/coroutine.c +++ b/test/coroutine.c @@ -14,7 +14,7 @@ #include /* allocator_t, allocator_get_default, allocator_destroy */ -#include +#include /* ... */ #include diff --git a/threadless/allocation.h b/threadless/allocation.h new file mode 100644 index 0000000..d0ac1b2 --- /dev/null +++ b/threadless/allocation.h @@ -0,0 +1,119 @@ +/* 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 + * allocation interface definition + * @author Justin R. Cutler + */ +#ifndef THREADLESS_ALLOCATION_H +#define THREADLESS_ALLOCATION_H + +/* size_t, NULL */ +#include + +/** Memory allocator instance */ +typedef struct allocator allocator_t; + +/** Memory allocation handle */ +typedef struct { + /** allocator used to manage this memory */ + allocator_t *allocator; + /** pointer to allocated memory */ + void *memory; + /** current size of allocated memory */ + size_t size; +} allocation_t; + +/** Memory allocator function + * @param[in,out] allocation memory allocation handle + * @param size minimum new size of memory (or 0 to free) + * @retval 0 success + * @retval -1 error + * @pre @p allocation must be initialized + * @pre If @p allocation->memory is @c NULL and @p allocation->size is + * non-zero, function shall allocate new memory + * @pre If @p size is 0, function shall free allocated memory + * @post Upon success, @p allocation has been updated to reflect changes + * @post Upon failure, @p allocation has not been changed + */ +typedef int (allocator_function_t)(allocation_t *allocation, size_t size); + +/** Memory allocator destructor function + * @param[in,out] allocator allocator instance + * @post @p allocator may no longer be used + */ +typedef void (allocator_destroy_function_t)(allocator_t *allocator); + +/** Memory allocator instance structure */ +struct allocator { + /** Memory (re)allocator function */ + allocator_function_t *const allocate; + /** Memory allocator instance destructor */ + allocator_destroy_function_t *const destroy; +}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Initialize an allocation handle + * @param[out] allocation allocation + * @param allocator allocator instance + * @pre @p allocation must be initialized + * @post @p allocation->allocator has been set to @p allocator, + * @p allocation->memory has been set to @c NULL, and @p allocation->size + * has been set to 0 + */ +static inline void allocation_init(allocation_t *allocation, + allocator_t *allocator) +{ + allocation->allocator = allocator; + allocation->memory = NULL; + allocation->size = 0; +} + +/** @c realloc_array() helper function + * @param[in,out] allocation memory allocation handle + * @param nmemb number of elements to allocate (or 0 to free) + * @param size size of each element to allocate (or 0 to free) + * @retval 0 success + * @retval -1 error + * @pre @p allocation must point to an initialized, valid allocation + * @pre If @p allocation->memory is @c NULL, function shall allocate new + * memory + * @pre If @p nmemb or @p size is 0, function shall free allocated memory + * @post Upon success, @p allocation->memory and @p allocation->size have been + * updated to reflect changes + * @post Upon failure, @p allocation has not been changed + * @note Upon detection of integer overflow, this function shall return + * @c NULL (like @c calloc(3) and FreeBSD's @c realloc_array(3)) + */ +int allocation_realloc_array(allocation_t *allocation, size_t nmemb, + size_t size); + +/** @c free() helper function + * @param[in,out] allocation memory allocation handle + * @pre @p allocation must point to an initialized, valid allocation + * @post @p allocation->memory is set to @c NULL and @p allocation->size is set + * to 0 + */ +static inline void allocation_free(allocation_t *allocation) +{ + (void) allocation_realloc_array(allocation, 0, 0); +} + +/** Memory allocator destructor helper function + * @copydetails allocator_destroy_function_t + */ +static inline void allocator_destroy(allocator_t *allocator) +{ + allocator->destroy(allocator); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* THREADLESS_ALLOCATOR_H */ diff --git a/threadless/allocator.h b/threadless/allocator.h deleted file mode 100644 index 60790e4..0000000 --- a/threadless/allocator.h +++ /dev/null @@ -1,96 +0,0 @@ -/* 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 - * allocator interface definition - * @author Justin R. Cutler - */ -#ifndef THREADLESS_ALLOCATOR_H -#define THREADLESS_ALLOCATOR_H - -/* size_t, NULL */ -#include - -/** Memory allocator instance */ -typedef struct allocator allocator_t; - -/** Memory allocator function - * @param[in,out] allocator allocator instance - * @param[in,out] ptr memory to reallocate/free (or @c NULL to allocate) - * @param nmemb number of elements to allocate (or 0 to free) - * @param size size of each element to allocate (or 0 to free) - * @retval non-NULL successful allocation/reallocation - * @retval NULL successful free (@p nmemb or @p size was 0) or error - * @note Upon detection of integer overflow, this function shall return - * @c NULL (like @c calloc(3) and FreeBSD's @c realloc_array(3)) - */ -typedef void *(allocator_function_t)(allocator_t *allocator, void *ptr, - size_t nmemb, size_t size); - -/** Memory allocator destructor function - * @param[in,out] allocator allocator instance - * @post @p allocator may no longer be used - */ -typedef void (allocator_destroy_function_t)(allocator_t *allocator); - -/** Memory allocator instance structure */ -struct allocator { - /** Memory allocator function */ - allocator_function_t *const allocate; - /** Memory allocator instance destructor */ - allocator_destroy_function_t *const destroy; -}; - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** Get default allocator instance - * @returns default allocator - */ -allocator_t *allocator_get_default(void); - -/** @c malloc() helper function - * @param[in,out] allocator allocator - * @param size size to allocate - * @retval Non-NULL allocated memory - * @retval NULL error - */ -static inline void *allocator_malloc(allocator_t *allocator, size_t size) -{ - return allocator->allocate(allocator, NULL, 1, size); -} - -/** @c realloc_array() helper function - * @copydetails allocator_function_t - */ -static inline void *allocator_realloc_array(allocator_t *allocator, void *ptr, - size_t nmemb, size_t size) -{ - return allocator->allocate(allocator, ptr, nmemb, size); -} - -/** @c free() helper function - * @param[in,out] allocator allocator - * @param ptr memory to free - */ -static inline void allocator_free(allocator_t *allocator, void *ptr) -{ - (void) allocator->allocate(allocator, ptr, 0, 0); -} - -/** Memory allocator destructor helper function - * @copydetails allocator_destroy_function_t - */ -static inline void allocator_destroy(allocator_t *allocator) -{ - allocator->destroy(allocator); -} - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* THREADLESS_ALLOCATOR_H */ diff --git a/threadless/coroutine.h b/threadless/coroutine.h index 5f38b4e..b018a58 100644 --- a/threadless/coroutine.h +++ b/threadless/coroutine.h @@ -16,7 +16,7 @@ #include /* allocator_t */ -#include +#include /** Opaque coroutine type */ typedef struct coroutine coroutine_t; diff --git a/threadless/default_allocator.h b/threadless/default_allocator.h new file mode 100644 index 0000000..00b8211 --- /dev/null +++ b/threadless/default_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 + * allocator interface definition + * @author Justin R. Cutler + */ +#ifndef THREADLESS_DEFAULT_ALLOCATOR_H +#define THREADLESS_DEFAULT_ALLOCATOR_H + +/* allocator_t */ +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Get default allocator instance + * @returns default allocator + */ +allocator_t *allocator_get_default(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* THREADLESS_DEFAULT_ALLOCATOR_H */