Artifact [9ec5857b82]
Not logged in

Artifact 9ec5857b824546cf513fe09ca1c11e7637678998:


/*
 * tclAlloc.c --
 *
 *      This is the generic part of the Tcl allocator. It handles the
 *      freeObjLists and defines which main allocator will be used.
 *
 * Copyright (c) 2013 by Miguel Sofer. All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#include "tclAlloc.h"

/*
 * Parameters for the per-thread Tcl_Obj cache:
 *   - if >NOBJHIGH free objects, move some to the shared cache
 *   - if no objects are available, create NOBJALLOC of them
 */

#define NOBJHIGH	1200
#define NOBJALLOC	((NOBJHIGH*2)/3)


/*
 * The Tcl_Obj per-thread cache.
 */

typedef struct Cache {
    Tcl_Obj *firstObjPtr;	/* List of free objects for thread */
    int numObjects;		/* Number of objects for thread */
    void *allocCachePtr;
} Cache;

static Cache sharedCache;
#define sharedPtr (&sharedCache)

#if defined(TCL_THREADS)
static Tcl_Mutex *objLockPtr;

static Cache *	GetCache(void);
static void	MoveObjs(Cache *fromPtr, Cache *toPtr, int numMove);

#if defined(HAVE_FAST_TSD)
static __thread Cache *tcachePtr;

# define GETCACHE(cachePtr)			\
    do {					\
	if (!tcachePtr) {			\
	    tcachePtr = GetCache();		\
	}					\
	(cachePtr) = tcachePtr;			\
    } while (0)

#else /* THREADS, not HAVE_FAST_TSD */
# define GETCACHE(cachePtr)			\
    do {					\
	(cachePtr) = TclpGetAllocCache();	\
	if ((cachePtr) == NULL) {		\
	    (cachePtr) = GetCache();		\
	}					\
    } while (0)
#endif /* FAST TSD */

#else /* NOT THREADS */
#define GETCACHE(cachePtr)			\
    (cachePtr) = (&sharedCache)
#endif /* THREADS */


/*
 *----------------------------------------------------------------------
 *
 * GetCache ---
 *
 *	Gets per-thread memory cache, allocating it if necessary.
 *
 * Results:
 *	Pointer to cache.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#if defined(TCL_THREADS)
static Cache *
GetCache(void)
{
    Cache *cachePtr;

    /*
     * Get this thread's cache, allocating if necessary.
     */

    cachePtr = TclpGetAllocCache();
    if (cachePtr == NULL) {
	cachePtr = calloc(1, sizeof(Cache));
	if (cachePtr == NULL) {
	    Tcl_Panic("alloc: could not allocate new cache");
	}
	cachePtr->allocCachePtr= NULL;
	TclpSetAllocCache(cachePtr);
    }
    return cachePtr;
}
#endif

/*
 * TclSetSharedAllocCache, TclSetAllocCache, TclGetAllocCache
 *
 * These are utility functions for the loadable allocator.
 */

void
TclSetSharedAllocCache(
    void *allocCachePtr)
{
    sharedPtr->allocCachePtr = allocCachePtr;
}

void
TclSetAllocCache(
    void *allocCachePtr)
{
    Cache *cachePtr;

    GETCACHE(cachePtr);
    cachePtr->allocCachePtr = allocCachePtr;
}

void *
TclGetAllocCache(void)
{
    Cache *cachePtr;

    GETCACHE(cachePtr);
    return cachePtr->allocCachePtr;
}

  
/*
 *-------------------------------------------------------------------------
 *
 * TclInitAlloc --
 *
 *	Initialize the memory system.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Initialize the mutex used to serialize obj allocations.
 *      Call the allocator-specific initialization.
 *
 *-------------------------------------------------------------------------
 */

void
TclInitAlloc(void)
{
    /*
     * Set the params for the correct allocator
     */

#if defined(TCL_THREADS)
    Tcl_Mutex *initLockPtr;

    TCL_THREADED = 1;
    initLockPtr = Tcl_GetAllocMutex();
    Tcl_MutexLock(initLockPtr);
    objLockPtr = TclpNewAllocMutex();
    TclXpInitAlloc();
    Tcl_MutexUnlock(initLockPtr);
#else
    TCL_THREADED = 0;
    TclXpInitAlloc();
#endif /* THREADS */

#ifdef PURIFY
    TCL_PURIFY = 1;
#else
    TCL_PURIFY   = (getenv("TCL_PURIFY") != NULL);
#endif
}

/*
 *----------------------------------------------------------------------
 *
 * TclFinalizeAlloc --
 *
 *	This procedure is used to destroy all private resources used in this
 *	file.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *      Call the allocator-specific finalization.
 *
 *----------------------------------------------------------------------
 */

void
TclFinalizeAlloc(void)
{
#if defined(TCL_THREADS)

    TclpFreeAllocMutex(objLockPtr);
    objLockPtr = NULL;

    TclpFreeAllocCache(NULL);
#endif
    TclXpFinalizeAlloc();
}

/*
 *----------------------------------------------------------------------
 *
 * TclFreeAllocCache --
 *
 *	Flush and delete a cache, removing from list of caches.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#if defined(TCL_THREADS)
void
TclFreeAllocCache(
    void *arg)
{
    Cache *cachePtr = arg;
    
    /*
     * Flush objs.
     */

    if (cachePtr->numObjects > 0) {
	Tcl_MutexLock(objLockPtr);
	MoveObjs(cachePtr, sharedPtr, cachePtr->numObjects);
	Tcl_MutexUnlock(objLockPtr);
    }

    /*
     * Flush the external allocator cache
     */

    TclXpFreeAllocCache(cachePtr->allocCachePtr);
}
#endif

/*
 *----------------------------------------------------------------------
 *
 * TclSmallAlloc --
 *
 *	Allocate a Tcl_Obj sized block from the per-thread cache.
 *
 * Results:
 *	Pointer to uninitialized memory.
 *
 * Side effects:
 *	May move blocks from shared cached or allocate new blocks if
 *	list is empty.
 *
 *----------------------------------------------------------------------
 */

void *
TclSmallAlloc(void)
{
    register Cache *cachePtr;
    register Tcl_Obj *objPtr;
    int numMove;
    Tcl_Obj *newObjsPtr;
   
    GETCACHE(cachePtr);

    /*
     * Pop the first object.
     */

    if(cachePtr->firstObjPtr) {
	haveObj:
	objPtr = cachePtr->firstObjPtr;
	cachePtr->firstObjPtr = objPtr->internalRep.otherValuePtr;
	cachePtr->numObjects--;
	return objPtr;
    }

    /*
     * Do it AFTER looking at the queue, so that it doesn't slow down
     * non-purify small allocs.
     */
    
    if (TCL_PURIFY) {
	Tcl_Obj *objPtr = (Tcl_Obj *) TclpAlloc(sizeof(Tcl_Obj));
	if (objPtr == NULL) {
	    Tcl_Panic("alloc: could not allocate a new object");
	}
	return objPtr;
    }
    
    /*
     * Get this thread's obj list structure and move or allocate new objs if
     * necessary.
     */

#if defined(TCL_THREADS)
    Tcl_MutexLock(objLockPtr);
    numMove = sharedPtr->numObjects;
    if (numMove > 0) {
	if (numMove > NOBJALLOC) {
	    numMove = NOBJALLOC;
	}
	MoveObjs(sharedPtr, cachePtr, numMove);
    }
    Tcl_MutexUnlock(objLockPtr);
    if (cachePtr->firstObjPtr) {
	goto haveObj;
    }
#endif	
    cachePtr->numObjects = numMove = NOBJALLOC;
    newObjsPtr = malloc(sizeof(Tcl_Obj) * numMove);
    if (newObjsPtr == NULL) {
	Tcl_Panic("alloc: could not allocate %d new objects", numMove);
    }
    while (--numMove >= 0) {
	objPtr = &newObjsPtr[numMove];
	objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr;
	cachePtr->firstObjPtr = objPtr;
    }
    goto haveObj;
}


/*
 *----------------------------------------------------------------------
 *
 * TclSmallFree --
 *
 *	Return a free Tcl_Obj-sized block to the per-thread cache.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May move free blocks to shared list upon hitting high water mark.
 *
 *----------------------------------------------------------------------
 */

void
TclSmallFree(
    void *ptr)
{
    Cache *cachePtr;
    Tcl_Obj *objPtr = ptr;
    
    if (TCL_PURIFY) {
	TclpFree((char *) ptr);
	return;
    }
    
    GETCACHE(cachePtr);

    /*
     * Get this thread's list and push on the free Tcl_Obj.
     */

    objPtr->internalRep.otherValuePtr = cachePtr->firstObjPtr;
    cachePtr->firstObjPtr = objPtr;
    cachePtr->numObjects++;

#if defined(TCL_THREADS)
    /*
     * If the number of free objects has exceeded the high water mark, move
     * some blocks to the shared list.
     */

    if (cachePtr->numObjects > NOBJHIGH) {
	Tcl_MutexLock(objLockPtr);
	MoveObjs(cachePtr, sharedPtr, NOBJALLOC);
	Tcl_MutexUnlock(objLockPtr);
    }
#endif
}

/*
 *----------------------------------------------------------------------
 *
 * MoveObjs --
 *
 *	Move Tcl_Obj's between caches.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#if defined(TCL_THREADS)
static void
MoveObjs(
    Cache *fromPtr,
    Cache *toPtr,
    int numMove)
{
    register Tcl_Obj *objPtr = fromPtr->firstObjPtr;
    Tcl_Obj *fromFirstObjPtr = objPtr;

    toPtr->numObjects += numMove;
    fromPtr->numObjects -= numMove;

    /*
     * Find the last object to be moved; set the next one (the first one not
     * to be moved) as the first object in the 'from' cache.
     */

    while (--numMove) {
	objPtr = objPtr->internalRep.otherValuePtr;
    }
    fromPtr->firstObjPtr = objPtr->internalRep.otherValuePtr;

    /*
     * Move all objects as a block - they are already linked to each other, we
     * just have to update the first and last.
     */

    objPtr->internalRep.otherValuePtr = toPtr->firstObjPtr;
    toPtr->firstObjPtr = fromFirstObjPtr;
}
#endif

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */