From 6e73b6a4f5582ef4b13a40cdf6786cf1702d2cc3 Mon Sep 17 00:00:00 2001 From: Lew Rossman Date: Mon, 3 Sep 2018 10:29:41 -0400 Subject: [PATCH] Made mempool.c threadsafe (#234) --- src/mempool.c | 240 ++++++++++++++++++-------------------------------- src/mempool.h | 36 ++------ src/quality.c | 17 ++-- src/types.h | 5 +- 4 files changed, 100 insertions(+), 198 deletions(-) diff --git a/src/mempool.c b/src/mempool.c index ef7e645..f77d8ec 100755 --- a/src/mempool.c +++ b/src/mempool.c @@ -1,17 +1,9 @@ /* mempool.c ** -** A simple fast memory allocation package. +** A simple fast pooled memory allocation package. ** -** By Steve Hill in Graphics Gems III, David Kirk (ed.), -** Academic Press, Boston, MA, 1992 -** -** Modified by Lew Rossman, 8/13/94. -** -** AllocInit() - create an alloc pool, returns the old pool handle -** Alloc() - allocate memory -** AllocReset() - reset the current pool -** AllocSetPool() - set the current pool -** AllocFree() - free the memory used by the current pool. +** Based on code by Steve Hill in Graphics Gems III, +** David Kirk (ed.), Academic Press, Boston, MA, 1992 ** */ @@ -28,95 +20,80 @@ #define ALLOC_BLOCK_SIZE 64000 /*(62*1024)*/ -/* -** alloc_hdr_t - Header for each block of memory. -*/ - -typedef struct alloc_hdr_s +struct MemBlock { - struct alloc_hdr_s *next; /* Next Block */ - char *block, /* Start of block */ - *free, /* Next free in block */ - *end; /* block + block size */ -} alloc_hdr_t; + struct MemBlock *next; // Next block + char *block, // Start of block + *free, // Next free position in block + *end; // block + block size +}; -/* -** alloc_root_t - Header for the whole pool. -*/ - -typedef struct alloc_root_s +struct Mempool { - alloc_hdr_t *first, /* First header in pool */ - *current; /* Current header */ -} alloc_root_t; + struct MemBlock *first; + struct MemBlock *current; +}; -/* -** root - Pointer to the current pool. -*/ - -static alloc_root_t *root; - - -/* -** AllocHdr() -** -** Private routine to allocate a header and memory block. -*/ - -static alloc_hdr_t *AllocHdr(void); - -static alloc_hdr_t * AllocHdr() +static struct MemBlock* createMemBlock() { - alloc_hdr_t *hdr; - char *block; - - block = (char *) malloc(ALLOC_BLOCK_SIZE); - hdr = (alloc_hdr_t *) malloc(sizeof(alloc_hdr_t)); - - if (hdr == NULL || block == NULL) return(NULL); - hdr->block = block; - hdr->free = block; - hdr->next = NULL; - hdr->end = block + ALLOC_BLOCK_SIZE; - - return(hdr); + struct MemBlock* memBlock = malloc(sizeof(struct MemBlock)); + if (memBlock) + { + memBlock->block = malloc(ALLOC_BLOCK_SIZE * sizeof(char)); + if (memBlock->block == NULL) + { + free(memBlock); + return NULL; + } + memBlock->free = memBlock->block; + memBlock->next = NULL; + memBlock->end = memBlock->block + ALLOC_BLOCK_SIZE; + } + return memBlock; } -/* -** AllocInit() -** -** Create a new memory pool with one block. -** Returns pointer to the new pool. -*/ - -DLLEXPORT alloc_handle_t * AllocInit() +static void deleteMemBlock(struct MemBlock* memBlock) { - alloc_handle_t *newpool; - root = (alloc_root_t *) malloc(sizeof(alloc_root_t)); - if (root == NULL) { - return(NULL); - } - if ( (root->first = AllocHdr()) == NULL) { - return(NULL); - } - root->current = root->first; - newpool = (alloc_handle_t *) root; - return(newpool); + free(memBlock->block); + free(memBlock); } -/* -** Alloc() -** -** Use as a direct replacement for malloc(). Allocates -** memory from the current pool. -*/ - -DLLEXPORT char *Alloc(long size) +struct Mempool * mempool_create() { - alloc_hdr_t *hdr = root->current; - char *ptr; + struct Mempool *mempool; + mempool = (struct Mempool *)malloc(sizeof(struct Mempool)); + if (mempool == NULL) return NULL; + mempool->first = createMemBlock(); + mempool->current = mempool->first; + if (mempool->first == NULL) return NULL; + return mempool; +} + +void mempool_delete(struct Mempool *mempool) +{ + if (mempool == NULL) return; + while (mempool->first) + { + mempool->current = mempool->first->next; + deleteMemBlock(mempool->first); + mempool->first = mempool->current; + } + free(mempool); + mempool = NULL; +} + +void mempool_reset(struct Mempool *mempool) +{ + mempool->current = mempool->first; + mempool->current->free = mempool->current->block; +} + + +char * mempool_alloc(struct Mempool *mempool, size_t size) +{ + char* ptr; /* ** Align to 4 byte boundary - should be ok for most machines. @@ -124,86 +101,37 @@ DLLEXPORT char *Alloc(long size) */ size = (size + 3) & 0xfffffffc; - ptr = hdr->free; - hdr->free += size; + if (!mempool->current) return NULL; + ptr = mempool->current->free; + mempool->current->free += size; - /* Check if the current block is exhausted. */ + // Check if the current block is exhausted - if (hdr->free >= hdr->end) + if (mempool->current->free >= mempool->current->end) { - /* Is the next block already allocated? */ + // Is the next block already allocated? - if (hdr->next != NULL) + if (mempool->current->next) { - /* re-use block */ - hdr->next->free = hdr->next->block; - root->current = hdr->next; + // re-use block + mempool->current->next->free = mempool->current->next->block; + mempool->current = mempool->current->next; } else { - /* extend the pool with a new block */ - if ( (hdr->next = AllocHdr()) == NULL) return(NULL); - root->current = hdr->next; + // extend the pool with a new block + mempool->current->next = createMemBlock(); + if (!mempool->current->next) return NULL; + mempool->current = mempool->current->next; } - /* set ptr to the first location in the next block */ - ptr = root->current->free; - root->current->free += size; + // set ptr to the first location in the next block + + ptr = mempool->current->free; + mempool->current->free += size; } - /* Return pointer to allocated memory. */ + // Return pointer to allocated memory - return(ptr); -} - - -/* -** AllocSetPool() -** -** Change the current pool. Return the old pool. -*/ - -DLLEXPORT alloc_handle_t * AllocSetPool(alloc_handle_t *newpool) -{ - alloc_handle_t *old = (alloc_handle_t *) root; - root = (alloc_root_t *) newpool; - return(old); -} - - -/* -** AllocReset() -** -** Reset the current pool for re-use. No memory is freed, -** so this is very fast. -*/ - -DLLEXPORT void AllocReset() -{ - root->current = root->first; - root->current->free = root->current->block; -} - - -/* -** AllocFreePool() -** -** Free the memory used by the current pool. -** Don't use where AllocReset() could be used. -*/ - -DLLEXPORT void AllocFreePool() -{ - alloc_hdr_t *tmp, - *hdr = root->first; - - while (hdr != NULL) - { - tmp = hdr->next; - free((char *) hdr->block); - free((char *) hdr); - hdr = tmp; - } - free((char *) root); - root = NULL; + return ptr; } diff --git a/src/mempool.h b/src/mempool.h index 4fe3c0f..002272f 100755 --- a/src/mempool.h +++ b/src/mempool.h @@ -3,41 +3,17 @@ ** ** Header for mempool.c ** -** The type alloc_handle_t provides an opaque reference to the -** alloc pool - only the alloc routines know its structure. +** A simple pooled memory allocator */ #ifndef MEMPOOL_H #define MEMPOOL_H -#ifndef DLLEXPORT - #ifdef DLL - #ifdef __cplusplus - #define DLLEXPORT extern "C" __declspec(dllexport) - #else - #define DLLEXPORT __declspec(dllexport) __stdcall - #endif - #elif defined(CYGWIN) - #define DLLEXPORT __stdcall - #else - #ifdef __cplusplus - #define DLLEXPORT - #else - #define DLLEXPORT - #endif - #endif -#endif +struct Mempool; - -typedef struct -{ - long dummy; -} alloc_handle_t; - -alloc_handle_t *AllocInit(void); -char *Alloc(long); -alloc_handle_t *AllocSetPool(alloc_handle_t *); -void AllocReset(void); -void AllocFreePool(void); +struct Mempool * mempool_create(); +void mempool_delete(struct Mempool *mempool); +void mempool_reset(struct Mempool *mempool); +char * mempool_alloc(struct Mempool *mempool, size_t size); #endif \ No newline at end of file diff --git a/src/quality.c b/src/quality.c index a7286db..1a70beb 100644 --- a/src/quality.c +++ b/src/quality.c @@ -29,9 +29,9 @@ AUTHOR: L. Rossman closequal() -- called from ENcloseQ() in EPANET.C Calls are made to: - AllocInit() - Alloc() - AllocFree() + mempool_create() + mempool_alloc() + mempool_delete() in MEMPOOL.C to utilize a memory pool to prevent excessive malloc'ing when constantly creating and destroying pipe sub-segments during the water quality transport calculations. @@ -89,7 +89,7 @@ int openqual(EN_Project *pr) /* Allocate memory pool for WQ segments */ qu->OutOfMemory = FALSE; - qu->SegPool = AllocInit(); + qu->SegPool = mempool_create(); if (qu->SegPool == NULL) { errcode = 101; } @@ -193,8 +193,7 @@ void initqual(EN_Project *pr) /* Reset memory pool */ qu->FreeSeg = NULL; - AllocSetPool(qu->SegPool); - AllocReset(); + mempool_reset(qu->SegPool); } /* Initialize avg. reaction rates */ @@ -438,8 +437,7 @@ int closequal(EN_Project *pr) /* Free memory pool */ if (qu->SegPool) { - AllocSetPool(qu->SegPool); - AllocFreePool(); + mempool_delete(qu->SegPool); } free(qu->FirstSeg); @@ -573,7 +571,6 @@ void transport(EN_Project *pr, long tstep) /* Repeat until elapsed time equals hydraulic time step */ - AllocSetPool(qu->SegPool); qtime = 0; while (!qu->OutOfMemory && qtime < tstep) { /* Qstep is quality time step */ dt = MIN(qu->Qstep, tstep - qtime); /* Current time step */ @@ -793,7 +790,7 @@ void addseg(EN_Project *pr, int k, double v, double c) seg = qu->FreeSeg; qu->FreeSeg = seg->prev; } else { - seg = (struct Sseg *)Alloc(sizeof(struct Sseg)); + seg = (struct Sseg *) mempool_alloc(qu->SegPool, sizeof(struct Sseg)); if (seg == NULL) { qu->OutOfMemory = TRUE; return; diff --git a/src/types.h b/src/types.h index d887c96..0a2d26c 100755 --- a/src/types.h +++ b/src/types.h @@ -22,7 +22,6 @@ AUTHOR: L. Rossman #include "epanet2.h" #include "hash.h" -#include "mempool.h" #include "util/errormanager.h" @@ -538,6 +537,8 @@ typedef struct s_ActItem /* Action list item */ } ActItem; +// Forward declaration of the Mempool structure defined in mempool.h +struct Mempool; typedef struct { char @@ -578,7 +579,7 @@ typedef struct { Qtime; /// Current quality time (sec) char OutOfMemory; /* Out of memory indicator */ - alloc_handle_t *SegPool; // Memory pool for water quality segments + struct Mempool *SegPool; // Memory pool for water quality segments Pseg FreeSeg; /* Pointer to unused segment */ Pseg *FirstSeg, /* First (downstream) segment in each pipe */