summaryrefslogtreecommitdiffstats
path: root/generic/tclAlloc.c
diff options
context:
space:
mode:
authormig <mig>2011-03-23 23:42:14 (GMT)
committermig <mig>2011-03-23 23:42:14 (GMT)
commita72bb809ac762d8ceb58a44c3d38a64062bb327b (patch)
tree204d89b3d0d39d9b01583898d50e66d8d5d73bf4 /generic/tclAlloc.c
parent645587123aa26590e35a5c0e5ce7ec43ff1a6963 (diff)
downloadtcl-a72bb809ac762d8ceb58a44c3d38a64062bb327b.zip
tcl-a72bb809ac762d8ceb58a44c3d38a64062bb327b.tar.gz
tcl-a72bb809ac762d8ceb58a44c3d38a64062bb327b.tar.bz2
completely new Tcl_Preserve implementation under Zippy, should be *much* faster. Cost is loss of some mem usage stats for zippy.
Diffstat (limited to 'generic/tclAlloc.c')
-rw-r--r--generic/tclAlloc.c334
1 files changed, 250 insertions, 84 deletions
diff --git a/generic/tclAlloc.c b/generic/tclAlloc.c
index 0aaa7f8..d1c3f8c 100644
--- a/generic/tclAlloc.c
+++ b/generic/tclAlloc.c
@@ -130,11 +130,13 @@ typedef struct Block {
struct {
unsigned char magic1; /* First magic number. */
unsigned char bucket; /* Bucket block allocated from. */
- unsigned char unused; /* Padding. */
+ unsigned char inUse; /* Block memory currently in use, as
+ * a fraction of the block size, in
+ * 255ths*/
unsigned char magic2; /* Second magic number. */
} s;
} u;
- size_t reqSize; /* Requested allocation size. */
+ long refCount;
} Block;
#define OFFSET ALIGN(sizeof(Block))
@@ -143,7 +145,7 @@ typedef struct Block {
#define sourceBucket u.s.bucket
#define magicNum1 u.s.magic1
#define magicNum2 u.s.magic2
-#define blockReqSize reqSize
+#define used u.s.inUse
#define MAGIC 0xEF
/*
@@ -180,7 +182,6 @@ typedef struct Bucket {
long numInserts; /* Number of inserts into bucket */
long numWaits; /* Number of waits to acquire a lock */
long numLocks; /* Number of locks acquired */
- long totalAssigned; /* Total space assigned to bucket */
#endif
} Bucket;
@@ -215,9 +216,6 @@ typedef struct Cache {
#if defined(TCL_THREADS)
Tcl_ThreadId owner; /* Which thread's cache is this? */
#endif
-#if defined(ZIPPY_STATS)
- int totalAssigned; /* Total space assigned to thread */
-#endif
Bucket buckets[NBUCKETS]; /* The buckets for this thread */
#endif /* USE_ZIPPY, ie TCL_ALLOCATOR != aNATIVE */
} Cache;
@@ -693,14 +691,6 @@ MoveObjs(
* the magic number at the end of the requested memory.
*/
-#ifndef RCHECK
-# ifdef NDEBUG
-# define RCHECK 0
-# else
-# define RCHECK 1
-# endif
-#endif
-
/*
*----------------------------------------------------------------------
@@ -725,14 +715,23 @@ Block2Ptr(
unsigned int reqSize)
{
register void *ptr;
-
+
blockPtr->magicNum1 = blockPtr->magicNum2 = MAGIC;
+ if (bucket == NBUCKETS) {
+ blockPtr->used = 255;
+ } else {
+ /*
+ * This relies on not overflowing, which will be the case if nobody
+ * makes NBUCKETS way too large.
+ */
+
+ size_t maxSize = bucketInfo[bucket].blockSize - OFFSET;
+
+ blockPtr->used = ((maxSize -1 + reqSize*255)/maxSize);
+ }
blockPtr->sourceBucket = bucket;
- blockPtr->reqSize = reqSize;
ptr = (void *) (((char *)blockPtr) + OFFSET);
-#if RCHECK
- ((unsigned char *)(ptr))[reqSize] = MAGIC;
-#endif
+ blockPtr->refCount = 0;
return (char *) ptr;
}
@@ -747,13 +746,6 @@ Ptr2Block(
Tcl_Panic("alloc: invalid block: %p: %x %x",
blockPtr, blockPtr->magicNum1, blockPtr->magicNum2);
}
-#if RCHECK
- if (((unsigned char *) ptr)[blockPtr->reqSize] != MAGIC) {
- Tcl_Panic("alloc: invalid block: %p: %x %x %x",
- blockPtr, blockPtr->magicNum1, blockPtr->magicNum2,
- ((unsigned char *) ptr)[blockPtr->reqSize]);
- }
-#endif
return blockPtr;
}
@@ -796,7 +788,7 @@ TclpAlloc(
const size_t zero = 0;
const size_t max = ~zero;
- if (((size_t) reqSize) > max - OFFSET - RCHECK) {
+ if (((size_t) reqSize) > max - OFFSET) {
/* Requested allocation exceeds memory */
return NULL;
}
@@ -811,17 +803,9 @@ TclpAlloc(
*/
size = reqSize + OFFSET;
-#if RCHECK
- size++;
-#endif
if (size > MAXALLOC) {
bucket = NBUCKETS;
blockPtr = malloc(size);
-#ifdef ZIPPY_STATS
- if (blockPtr != NULL) {
- cachePtr->totalAssigned += reqSize;
- }
-#endif
} else {
blockPtr = NULL;
bucket = 0;
@@ -834,7 +818,6 @@ TclpAlloc(
cachePtr->buckets[bucket].numFree--;
#ifdef ZIPPY_STATS
cachePtr->buckets[bucket].numRemoves++;
- cachePtr->buckets[bucket].totalAssigned += reqSize;
#endif
}
if (blockPtr == NULL) {
@@ -878,33 +861,25 @@ TclpFree(
return;
}
-#ifdef ZIPPY_STATS
- GETCACHE(cachePtr);
-#endif
-
+ blockPtr = Ptr2Block(ptr);
+ if (blockPtr->refCount != 0) {
+ /* Tcl_Panic("trying to free a preserved pointer");*/
+ }
+
/*
* Get the block back from the user pointer and call system free directly
* for large blocks. Otherwise, push the block back on the bucket and move
* blocks to the shared cache if there are now too many free.
*/
- blockPtr = Ptr2Block(ptr);
bucket = blockPtr->sourceBucket;
if (bucket == NBUCKETS) {
-#ifdef ZIPPY_STATS
- cachePtr->totalAssigned -= blockPtr->reqSize;
-#endif
free(blockPtr);
return;
}
-#ifndef ZIPPY_STATS
GETCACHE(cachePtr);
-#endif
-#ifdef ZIPPY_STATS
- cachePtr->buckets[bucket].totalAssigned -= blockPtr->reqSize;
-#endif
blockPtr->nextBlock = cachePtr->buckets[bucket].firstPtr;
cachePtr->buckets[bucket].firstPtr = blockPtr;
cachePtr->buckets[bucket].numFree++;
@@ -940,7 +915,6 @@ TclpRealloc(
char *ptr,
unsigned int reqSize)
{
- Cache *cachePtr;
Block *blockPtr;
void *newPtr;
size_t size, min;
@@ -952,8 +926,6 @@ TclpRealloc(
}
#endif
- GETCACHE(cachePtr);
-
if (ptr == NULL) {
return TclpAlloc(reqSize);
}
@@ -964,7 +936,7 @@ TclpRealloc(
const size_t zero = 0;
const size_t max = ~zero;
- if (((size_t) reqSize) > max - OFFSET - RCHECK) {
+ if (((size_t) reqSize) > max - OFFSET) {
/* Requested allocation exceeds memory */
return NULL;
}
@@ -978,10 +950,11 @@ TclpRealloc(
*/
blockPtr = Ptr2Block(ptr);
+ if (blockPtr->refCount != 0) {
+ Tcl_Panic("trying to realloc a preserved pointer");
+ }
+
size = reqSize + OFFSET;
-#if RCHECK
- size++;
-#endif
bucket = blockPtr->sourceBucket;
if (bucket != NBUCKETS) {
if (bucket > 0) {
@@ -990,17 +963,9 @@ TclpRealloc(
min = 0;
}
if (size > min && size <= bucketInfo[bucket].blockSize) {
-#ifdef ZIPPY_STATS
- cachePtr->buckets[bucket].totalAssigned -= blockPtr->reqSize;
- cachePtr->buckets[bucket].totalAssigned += reqSize;
-#endif
return Block2Ptr(blockPtr, bucket, reqSize);
}
} else if (size > MAXALLOC) {
-#ifdef ZIPPY_STATS
- cachePtr->totalAssigned -= blockPtr->reqSize;
- cachePtr->totalAssigned += reqSize;
-#endif
blockPtr = realloc(blockPtr, size);
if (blockPtr == NULL) {
return NULL;
@@ -1014,10 +979,14 @@ TclpRealloc(
newPtr = TclpAlloc(reqSize);
if (newPtr != NULL) {
- if (reqSize > blockPtr->reqSize) {
- reqSize = blockPtr->reqSize;
+ size_t maxSize = bucketInfo[bucket].blockSize - OFFSET;
+ size_t toCopy = ((blockPtr->used) * maxSize)/255;
+
+ if (toCopy > reqSize) {
+ toCopy = reqSize;
}
- memcpy(newPtr, ptr, reqSize);
+
+ memcpy(newPtr, ptr, toCopy);
TclpFree(ptr);
}
return newPtr;
@@ -1043,8 +1012,9 @@ TclpRealloc(
{
Block *blockPtr;
int bucket;
- size_t oldSize, newSize;
+ size_t size;
+ return UINT_MAX;
#if TCL_ALLOCATOR == aMULTI
if (allocator < aNONE) {
/*
@@ -1066,20 +1036,9 @@ TclpRealloc(
return UINT_MAX;
}
- oldSize = blockPtr->reqSize;
- newSize = bucketInfo[bucket].blockSize - OFFSET - RCHECK;
- blockPtr->reqSize = newSize;
-#if RCHECK
- ((unsigned char *)(ptr))[newSize] = MAGIC;
-#endif
-#ifdef ZIPPY_STATS
- {
- Cache *cachePtr;
- GETCACHE(cachePtr);
- cachePtr->buckets[bucket].totalAssigned += (newSize - oldSize);
- }
-#endif
- return newSize;
+ size = bucketInfo[bucket].blockSize - OFFSET;
+ blockPtr->used = 255;
+ return size;
}
#ifdef ZIPPY_STATS
@@ -1128,7 +1087,6 @@ Tcl_GetMemoryInfo(
cachePtr->buckets[n].numFree,
cachePtr->buckets[n].numRemoves,
cachePtr->buckets[n].numInserts,
- cachePtr->buckets[n].totalAssigned,
cachePtr->buckets[n].numLocks,
cachePtr->buckets[n].numWaits);
Tcl_DStringAppendElement(dsPtr, buf);
@@ -1390,6 +1348,214 @@ ChooseAllocator()
}
}
#endif
+
+#if USE_NEW_PRESERVE
+
+typedef struct PreserveData {
+ struct PreserveData *nextPtr;
+ char *ptr;
+ Tcl_FreeProc *freeProc; /* Function to call to free. */
+} PreserveData;
+
+static PreserveData *pdataPtr = NULL;
+TCL_DECLARE_MUTEX(preserveMutex) /* To protect the above statics */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclFinalizePreserve --
+ *
+ * Called during exit processing to clean up the reference array.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees the storage of the reference array.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+TclFinalizePreserve(void)
+{
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_Preserve --
+ *
+ * This function is used by a function to declare its interest in a
+ * particular block of memory, so that the block will not be reallocated
+ * until a matching call to Tcl_Release has been made.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information is retained so that the block of memory will not be freed
+ * until at least the matching call to Tcl_Release.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_Preserve(
+ ClientData clientData) /* Pointer to malloc'ed block of memory. */
+{
+ char *ptr = (char *) clientData;
+ Block *blockPtr;
+ long refCount;
+
+ blockPtr = Ptr2Block(ptr);
+ refCount = blockPtr->refCount;
+
+ if (refCount > 0) {
+ ++blockPtr->refCount;
+ } else if (refCount < 0) {
+ /*
+ * TclEventuallyFree has already been called on this with
+ * (freeProc != TCL_DYNAMIC)
+ */
+
+ --blockPtr->refCount;
+ } else {
+ /*
+ * First preserve call: add one refcount for use by EventuallyFree
+ */
+
+ blockPtr->refCount = 2;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_Release --
+ *
+ * This function is called to cancel a previous call to Tcl_Preserve,
+ * thereby allowing a block of memory to be freed (if no one else cares
+ * about it).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If Tcl_EventuallyFree has been called for clientData, and if no other
+ * call to Tcl_Preserve is still in effect, the block of memory is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_Release(
+ ClientData clientData) /* Pointer to malloc'ed block of memory. */
+{
+ char *ptr = (char *) clientData;
+ Block *blockPtr = Ptr2Block(ptr);
+ long refCount = blockPtr->refCount;
+ int hasFreeProc = (refCount < 0);
+
+ if (refCount > 0) {
+ refCount = --blockPtr->refCount;
+ } else if (refCount < 0) {
+ refCount = ++blockPtr->refCount;
+ } else {
+ Tcl_Panic("Tcl_Release couldn't find reference for %p", clientData);
+ }
+
+ if (refCount == 0) {
+ if (!hasFreeProc) {
+ TclpFree(ptr);
+ } else {
+ PreserveData *thisPtr = pdataPtr, *lastPtr = NULL;
+ Tcl_MutexLock(&preserveMutex);
+ while (thisPtr && (thisPtr->ptr != ptr)) {
+ lastPtr = thisPtr;
+ thisPtr = thisPtr->nextPtr;
+ }
+ if (!thisPtr) {
+ Tcl_Panic("Tcl_Release couldn't find reference for %p", clientData);
+ }
+ if (lastPtr) {
+ lastPtr->nextPtr = thisPtr->nextPtr;
+ } else {
+ pdataPtr = thisPtr->nextPtr;
+ }
+ Tcl_MutexUnlock(&preserveMutex);
+
+ pdataPtr->freeProc(clientData);
+ TclSmallFree(pdataPtr);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_EventuallyFree --
+ *
+ * Free up a block of memory, unless a call to Tcl_Preserve is in effect
+ * for that block. In this case, defer the free until all calls to
+ * Tcl_Preserve have been undone by matching calls to Tcl_Release.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Ptr may be released by calling free().
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_EventuallyFree(
+ ClientData clientData, /* Pointer to malloc'ed block of memory. */
+ Tcl_FreeProc *freeProc) /* Function to actually do free. */
+{
+ char *ptr = (char *) clientData;
+ Block *blockPtr = Ptr2Block(ptr);
+ PreserveData *thisPtr;
+ long refCount = blockPtr->refCount;
+ int hasFreeProc = (refCount < 0);
+
+ if (hasFreeProc) {
+ Tcl_Panic("Tcl_EventuallyFree called twice for %p", clientData);
+ }
+
+ if (refCount < 2) {
+ /*
+ * No other reference for this block. Free it now.
+ */
+
+ blockPtr->refCount = 0;
+ if (freeProc == TCL_DYNAMIC) {
+ ckfree(clientData);
+ } else {
+ freeProc(clientData);
+ }
+ return;
+ }
+
+ --blockPtr->refCount;
+
+ if (freeProc != TCL_DYNAMIC) {
+ blockPtr->refCount = -blockPtr->refCount;
+ TclCkSmallAlloc(sizeof(PreserveData), thisPtr);
+
+ thisPtr->ptr = ptr;
+ thisPtr->freeProc = freeProc;
+ Tcl_MutexLock(&preserveMutex);
+ thisPtr->nextPtr = pdataPtr;
+ pdataPtr = thisPtr;
+ Tcl_MutexUnlock(&preserveMutex);
+ }
+}
+#endif /* USE_NEW_PRESERVE */
+
#endif /* USE_ZIPPY */
/*