kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

pool.c (4853B)



      1 /*
      2  * Copyright (c) 2013-2022 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/types.h>
     18 #include <sys/mman.h>
     19 #include <sys/queue.h>
     20 
     21 #include <stdint.h>
     22 
     23 #include "kore.h"
     24 
     25 #define POOL_MIN_ELEMENTS		16
     26 
     27 #define POOL_ELEMENT_BUSY		0
     28 #define POOL_ELEMENT_FREE		1
     29 
     30 #if defined(KORE_USE_TASKS)
     31 static void		pool_lock(struct kore_pool *);
     32 static void		pool_unlock(struct kore_pool *);
     33 #endif
     34 
     35 static void		pool_region_create(struct kore_pool *, size_t);
     36 static void		pool_region_destroy(struct kore_pool *);
     37 
     38 void
     39 kore_pool_init(struct kore_pool *pool, const char *name,
     40     size_t len, size_t elm)
     41 {
     42 	kore_debug("kore_pool_init(%p, %s, %zu, %zu)", pool, name, len, elm);
     43 
     44 	if (elm < POOL_MIN_ELEMENTS)
     45 		elm = POOL_MIN_ELEMENTS;
     46 
     47 	if ((pool->name = strdup(name)) == NULL)
     48 		fatal("kore_pool_init: strdup %s", errno_s);
     49 
     50 	len = (len + (8 - 1)) & ~(8 - 1);
     51 
     52 	pool->lock = 0;
     53 	pool->elms = 0;
     54 	pool->inuse = 0;
     55 	pool->elen = len;
     56 	pool->growth = elm * 0.25f;
     57 	pool->slen = pool->elen + sizeof(struct kore_pool_entry);
     58 
     59 	LIST_INIT(&(pool->regions));
     60 	LIST_INIT(&(pool->freelist));
     61 
     62 	pool_region_create(pool, elm);
     63 }
     64 
     65 void
     66 kore_pool_cleanup(struct kore_pool *pool)
     67 {
     68 	pool->lock = 0;
     69 	pool->elms = 0;
     70 	pool->inuse = 0;
     71 	pool->elen = 0;
     72 	pool->slen = 0;
     73 
     74 	free(pool->name);
     75 	pool->name = NULL;
     76 
     77 	pool_region_destroy(pool);
     78 }
     79 
     80 void *
     81 kore_pool_get(struct kore_pool *pool)
     82 {
     83 	u_int8_t			*ptr;
     84 	struct kore_pool_entry		*entry;
     85 
     86 #if defined(KORE_USE_TASKS)
     87 	pool_lock(pool);
     88 #endif
     89 
     90 	if (LIST_EMPTY(&(pool->freelist)))
     91 		pool_region_create(pool, pool->growth);
     92 
     93 	entry = LIST_FIRST(&(pool->freelist));
     94 	if (entry->state != POOL_ELEMENT_FREE)
     95 		fatal("%s: element %p was not free", pool->name, entry);
     96 	LIST_REMOVE(entry, list);
     97 
     98 	entry->state = POOL_ELEMENT_BUSY;
     99 	ptr = (u_int8_t *)entry + sizeof(struct kore_pool_entry);
    100 
    101 	pool->inuse++;
    102 
    103 #if defined(KORE_USE_TASKS)
    104 	pool_unlock(pool);
    105 #endif
    106 
    107 	return (ptr);
    108 }
    109 
    110 void
    111 kore_pool_put(struct kore_pool *pool, void *ptr)
    112 {
    113 	struct kore_pool_entry		*entry;
    114 
    115 #if defined(KORE_USE_TASKS)
    116 	pool_lock(pool);
    117 #endif
    118 
    119 	entry = (struct kore_pool_entry *)
    120 	    ((u_int8_t *)ptr - sizeof(struct kore_pool_entry));
    121 
    122 	if (entry->state != POOL_ELEMENT_BUSY)
    123 		fatal("%s: element %p was not busy", pool->name, ptr);
    124 
    125 	entry->state = POOL_ELEMENT_FREE;
    126 	LIST_INSERT_HEAD(&(pool->freelist), entry, list);
    127 
    128 	pool->inuse--;
    129 
    130 #if defined(KORE_USE_TASKS)
    131 	pool_unlock(pool);
    132 #endif
    133 }
    134 
    135 static void
    136 pool_region_create(struct kore_pool *pool, size_t elms)
    137 {
    138 	size_t				i;
    139 	u_int8_t			*p;
    140 	struct kore_pool_region		*reg;
    141 	struct kore_pool_entry		*entry;
    142 
    143 	kore_debug("pool_region_create(%p, %zu)", pool, elms);
    144 
    145 	if ((reg = calloc(1, sizeof(struct kore_pool_region))) == NULL)
    146 		fatal("pool_region_create: calloc: %s", errno_s);
    147 
    148 	LIST_INSERT_HEAD(&(pool->regions), reg, list);
    149 
    150 	if (SIZE_MAX / elms < pool->slen)
    151 		fatal("pool_region_create: overflow");
    152 
    153 	reg->length = elms * pool->slen;
    154 	reg->start = mmap(NULL, reg->length, PROT_READ | PROT_WRITE,
    155 	    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    156 	if (reg->start == MAP_FAILED)
    157 		fatal("mmap: %s", errno_s);
    158 
    159 	p = (u_int8_t *)reg->start;
    160 
    161 	for (i = 0; i < elms; i++) {
    162 		entry = (struct kore_pool_entry *)p;
    163 		entry->region = reg;
    164 		entry->state = POOL_ELEMENT_FREE;
    165 		LIST_INSERT_HEAD(&(pool->freelist), entry, list);
    166 
    167 		p = p + pool->slen;
    168 	}
    169 
    170 	pool->elms += elms;
    171 }
    172 
    173 static void
    174 pool_region_destroy(struct kore_pool *pool)
    175 {
    176 	struct kore_pool_region		*reg;
    177 
    178 	kore_debug("pool_region_destroy(%p)", pool);
    179 
    180 	/* Take care iterating when modifying list contents */
    181 	while (!LIST_EMPTY(&pool->regions)) {
    182 		reg = LIST_FIRST(&pool->regions);
    183 		LIST_REMOVE(reg, list);
    184 		(void)munmap(reg->start, reg->length);
    185 		free(reg);
    186 	}
    187 
    188 	/* Freelist references into the regions memory allocations */
    189 	LIST_INIT(&pool->freelist);
    190 	pool->elms = 0;
    191 }
    192 
    193 #if defined(KORE_USE_TASKS)
    194 static void
    195 pool_lock(struct kore_pool *pool)
    196 {
    197 	for (;;) {
    198 		if (__sync_bool_compare_and_swap(&pool->lock, 0, 1))
    199 			break;
    200 	}
    201 }
    202 
    203 static void
    204 pool_unlock(struct kore_pool *pool)
    205 {
    206 	if (!__sync_bool_compare_and_swap(&pool->lock, 1, 0))
    207 		fatal("pool_unlock: failed to release %s", pool->name);
    208 }
    209 #endif