📄 mpralloc.c
字号:
/** * @file mprAlloc.c * @brief Memory Allocation * @overview *//* * @copy default * * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. * * This software is distributed under commercial and open source licenses. * You may use the GPL open source license described below or you may acquire * a commercial license from Mbedthis Software. You agree to be fully bound * by the terms of either license. Consult the LICENSE.TXT distributed with * this software for full details. * * This software is open source; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See the GNU General Public License for more * details at: http://www.mbedthis.com/downloads/gplLicense.html * * This program is distributed WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * This GPL license does NOT permit incorporating this software into * proprietary programs. If you are unable to comply with the GPL, you must * acquire a commercial license to use this software. Commercial licenses * for this software and support services are available from Mbedthis * Software at http://www.mbedthis.com * * @end *//********************************* Includes ***********************************/#define UNSAFE_FUNCTIONS_OK 1#include "mpr.h"/******************************* Local Defines ********************************//* * Set to 1 to disable slab based allocations */#define NO_SLAB 0/* * Validation mode is quite slow */#define VALIDATE_ALLOC 0#if VALIDATE_ALLOC#define VALIDATE_BLOCK(ptr) mprValidateBlock(ptr)#else#define VALIDATE_BLOCK(ptr) #endif/* * Align on 4 bytes if squeeze, Otherwize on 16 bytes. */#define HDR_SIZE MPR_BLK_HDR_SIZE#define APP_MAGIC 0xa571cb80#define ALLOC_MAGIC 0xe814ecc0/* * This must be at least one word to ensure that the smallest allocation is * 4 bytes. Slab allocations need at least one word to store their next ptr. */#define ALLOC_ALIGN(x) (((x)+3)&~3)#define GET_HDR(ptr) ((MprBlk*) (((char*) (ptr)) - HDR_SIZE))#define GET_PTR(bp) ((void*) (((char*) (bp)) + HDR_SIZE))#define VALID_HDR(bp) (((bp)->flags & ~0x3F) == ALLOC_MAGIC)#define VALID_BLK(ptr) (VALID_HDR(GET_HDR(ptr)))/* * In production releases, mprAssert will compile out (not be included) * but CHECK_HDR will remain even in production builds. */#define CHECK_HDR(bp) \ if (1) { if (! VALID_HDR(bp)) { mprAllocAbort(); } } else/* * Chunk the slabs into 32 byte increments. * This allows for allocations up to 512 bytes via slabs and maximizes * sharing of slab allocations. * * Index map: * 0 == 32 bytes * 1 == 64 bytes * 2 == 96 bytes * 3 == 128 bytes * 4 == 160 bytes * 5 == 192 bytes * 6 == 224 bytes * 7 == 256 bytes * 8 == 288 bytes * 9 == 320 bytes * 10 == 352 bytes * 11 == 384 bytes * 12 == 416 bytes * 13 == 448 bytes * 14 == 480 bytes * 15 == 512 bytes */#define SLAB_ALIGN(size) ((size + 31) & ~31)#define GET_SLAB(size) (size >> 6)/* * Block flags */#define ALLOC_FLAGS_FREE 0x1 /* Block is free */#define ALLOC_FLAGS_FREEING 0x2 /* Block is being freed */#define ALLOC_FLAGS_SLAB_BLOCK 0x4 /* Block was allocated from slab */#define ALLOC_FLAGS_REQUIRED 0x8 /* Block is required by alloc */#define ALLOC_FLAGS_KEEP 0x10 /* Keep block - don't mprFree */#define ALLOC_FLAGS_DONT_OS_FREE 0x20 /* Don't return mem to O/S */#define ALLOC_FLAGS_IS_SLAB 0x40 /* Block is a slab */#if BLD_DEBUG && !BREW/* * Set this address to break when this address is allocated or freed. This is * a block address (not a user ptr). */static MprBlk *stopAlloc;#endif#if !BREWstatic MprCtx rootCtx; /* Root context if none supplied */#endif/***************************** Forward Declarations ***************************/static int mprAllocException(MPR_LOC_DEC(ptr, loc), uint size, bool granted);static void slabFree(MprBlk *bp);static int growSlab(MPR_LOC_DEC(ctx, loc), MprSlab *slab, uint size, uint inc);/******************************************************************************//* * Put first in file so it is easy to locate in a debugger */void mprBreakpoint(const char *loc, const char *msg){}/******************************************************************************/#if (WIN || BREW_SIMULATOR) && BLD_DEBUGint crtReportHook(int type, char *msg, int *retval){ printf("%s\n", msg); *retval = 0; return TRUE;}#endif/******************************************************************************//* * Initialize the memory subsystem */MprApp *mprAllocInit(MprAllocCback cback){ MprAllocStats *stats; MprApp *app; MprSlab *slab; MprBlk *bp, *sp; int i; bp = malloc(sizeof(MprApp) + HDR_SIZE); mprAssert(bp); if (bp == 0) { if (cback) { (*cback)(0, sizeof(MprApp), 0, 0); } return 0; } memset(bp, 0, sizeof(MprApp) + HDR_SIZE); bp->parent = bp; bp->size = sizeof(MprApp); bp->flags = ALLOC_MAGIC; bp->next = bp->prev = bp;#if BLD_FEATURE_ALLOC_LEAK_TRACK bp->location = MPR_LOC;#endif app = (MprApp*) GET_PTR(bp); app->magic = APP_MAGIC; app->alloc.cback = cback; app->stackStart = (void*) &app; bp->app = app; app->alloc.slabs = mprAllocZeroedBlock(MPR_LOC_PASS(app, MPR_LOC), sizeof(MprSlab) * MPR_MAX_SLAB); if (app->alloc.slabs == 0) { mprFree(app); return 0; } /* * The slab control structures must not be freed. Set keep to safeguard * against accidents. */ sp = GET_HDR(app->alloc.slabs); sp->flags |= ALLOC_FLAGS_KEEP; for (i = 0; i < MPR_MAX_SLAB; i++) { /* * This is overriden by requestors calling slabAlloc */ slab = &app->alloc.slabs[i]; slab->preAllocateIncr = MPR_SLAB_DEFAULT_INC; } /* * Keep aggregated stats even in production code */ stats = &app->alloc.stats; stats->bytesAllocated += sizeof(MprApp); if (stats->bytesAllocated > stats->peakAllocated) { stats->peakAllocated = stats->bytesAllocated; } stats->allocCount++;#if !BREW rootCtx = app;#endif#if (WIN || BREW_SIMULATOR) && BLD_DEBUG _CrtSetReportHook(crtReportHook);#endif return app;}/******************************************************************************//* * Terminate the alloc module */void mprAllocTerm(MprApp *app){ MprSlab *slabs; MprBlk *appBlk, *slabBlk; /* * Must do a carefully ordered cleanup. Need to free all children blocks * before freeing the slab memory. Save a local pointer to the slabs. */ slabs = app->alloc.slabs; /* * Free the app and all children. Set DONT_OS_FREE to prevent free() being * called on app itself. We need that so we can free the slabs below. */ appBlk = GET_HDR(app); appBlk->flags |= ALLOC_FLAGS_DONT_OS_FREE; mprFree(app); /* * Slabs are initially marked don't free. We must preserve them while all * other blocks are freed. Then we clear the don't free flag and free. * Now we don't have an app structure which is used by mprFree. We must * fake it. */ slabBlk = GET_HDR(slabs); slabBlk->flags &= ~ALLOC_FLAGS_KEEP; mprFree(slabs); /* * Now we can finally free the memory for the app structure */ free(appBlk);}/******************************************************************************//* * Allocate a block */void *mprAllocBlock(MPR_LOC_DEC(ctx, loc), uint size){ MprAllocStats *stats; MprBlk *bp, *parent; MprApp *app; int diff; mprAssert(size > 0); if (ctx == 0) {#if BREW mprAssert(ctx); return 0;#else ctx = rootCtx;#endif } if (size == 0) { size = 1; } mprAssert(VALID_BLK(ctx)); parent = GET_HDR(ctx); mprAssert(VALID_HDR(parent)); CHECK_HDR(parent); size = ALLOC_ALIGN(size); app = parent->app; stats = &app->alloc.stats; mprLock(app->allocLock); stats->bytesAllocated += size + HDR_SIZE; if (stats->bytesAllocated > stats->peakAllocated) { stats->peakAllocated = stats->bytesAllocated; } /* * Prevent allocation if over the maximum */ if (stats->maxMemory && stats->bytesAllocated > stats->maxMemory) { stats->bytesAllocated -= (size + HDR_SIZE); mprUnlock(app->allocLock); if (mprAllocException(MPR_LOC_PASS(ctx, loc), size, 0) < 0) { return 0; } mprLock(app->allocLock); } if ((bp = malloc(size + HDR_SIZE)) == 0) { mprAssert(bp); stats->errors++; mprUnlock(app->allocLock); mprAllocException(MPR_LOC_PASS(ctx, loc), size, 0); return 0; }#if BLD_DEBUG memset(bp, 0xf7, size + HDR_SIZE);#endif#if BLD_DEBUG && !BREW if (bp == stopAlloc) { mprBreakpoint(MPR_LOC, "breakOnAddr"); }#endif /* * Warn if allocation puts us over the red line */ if (stats->redLine && stats->bytesAllocated > stats->redLine) { mprUnlock(app->allocLock); if (mprAllocException(MPR_LOC_PASS(ctx, loc), size, 1) < 0) { return 0; } mprLock(app->allocLock); } bp->size = size; bp->flags = ALLOC_MAGIC; bp->destructor = 0; bp->parent = parent; if (parent->children == 0) { parent->children = bp; bp->next = bp->prev = bp; } else { /* * Append to the end of the list. Preserve alloc order */ bp->next = parent->children; bp->prev = parent->children->prev; parent->children->prev->next = bp; parent->children->prev = bp; } bp->children = 0;#if BLD_FEATURE_ALLOC_LEAK_TRACK bp->location = loc;#endif bp->app = parent->app; VALIDATE_BLOCK(GET_PTR(bp)); stats->allocCount++; /* * Monitor stack usage */ diff = (int) bp->app->stackStart - (int) &stats; if (diff < 0) { app->maxStack -= diff; app->stackStart = (void*) &stats; diff = 0; } if ((uint) diff > app->maxStack) { app->maxStack = diff; } mprUnlock(app->allocLock); return GET_PTR(bp);}/******************************************************************************//* * Allocate and zero a block */void *mprAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size){ void *newBlock; MprBlk *bp; bp = GET_HDR(ctx); mprAssert(VALID_BLK(ctx)); newBlock = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size); if (newBlock) { memset(newBlock, 0, size); } return newBlock;}/******************************************************************************//* * Free a block of memory. Free all children recursively. */int mprFree(void *ptr){ MprAllocStats *stats; MprBlk *bp, *parent, *cp, *firstChild, *prev; MprApp *app; if (ptr == 0) { return 0; } mprAssert(VALID_BLK(ptr)); VALIDATE_BLOCK(ptr); bp = GET_HDR(ptr);#if BLD_DEBUG && !BREW if (bp == stopAlloc) { mprBreakpoint(MPR_LOC, "breakOnAddr"); }#endif mprAssert(bp); mprAssert(VALID_HDR(bp)); CHECK_HDR(bp); /* * Test if already freed */ mprAssert(! (bp->flags & ALLOC_FLAGS_FREE)); if (bp->flags & ALLOC_FLAGS_FREE) { return 0; } /* * Return if recursive freeing or this is a permanent block */ app = bp->app; mprLock(app->allocLock); if (bp->flags & (ALLOC_FLAGS_FREEING | ALLOC_FLAGS_KEEP)) { mprUnlock(app->allocLock); return 0; } bp->flags |= ALLOC_FLAGS_FREEING; /* * Call any destructors */ if (bp->destructor) { mprUnlock(app->allocLock); if ((bp->destructor)(ptr) < 0) { return -1; } mprLock(app->allocLock); bp->destructor = 0; } /* * Free the children. Free in reverse order so firstChild is preserved * during the list scan as an end of list marker. */ if ((firstChild = bp->children) != 0) { cp = firstChild->prev; while (cp != firstChild) { mprAssert(VALID_HDR(cp)); VALIDATE_BLOCK(GET_PTR(cp)); prev = cp->prev; /* * FUTURE - OPT. Make this inline */ mprFree(GET_PTR(cp)); cp = prev; } mprFree(GET_PTR(firstChild)); /* * Just for clarity */ bp->children = 0; } parent = bp->parent; mprAssert(VALID_HDR(parent)); /* * Unlink from the parent */ if (parent->children == bp) { if (bp->next == bp) { parent->children = 0; } else { parent->children = bp->next; } } /* * Remove from the sibling chain */ bp->prev->next = bp->next; bp->next->prev = bp->prev; bp->flags |= ALLOC_FLAGS_FREE; /* * Release the memory. If from a slab, return to the slab. Otherwise, * return to the O/S. */ if (bp->flags & ALLOC_FLAGS_SLAB_BLOCK) { slabFree(bp); } else { mprAssert(bp); /* * Update the stats */ stats = &bp->app->alloc.stats; stats->bytesAllocated -= (bp->size + HDR_SIZE); mprAssert(stats->bytesAllocated >= 0); stats->allocCount--; mprAssert(stats->allocCount >= 0);#if BLD_DEBUG && !BREW if (bp == stopAlloc) { mprBreakpoint(MPR_LOC, "breakOnAddr"); }#endif /* * Return to the O/S */ if (! (bp->flags & ALLOC_FLAGS_DONT_OS_FREE)) { free(bp); } } /* OPT */ if (app != ptr) { mprUnlock(app->allocLock); } return 0;}/******************************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -