summaryrefslogtreecommitdiffstats
path: root/generic/tclInt.h
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclInt.h')
-rw-r--r--generic/tclInt.h320
1 files changed, 224 insertions, 96 deletions
diff --git a/generic/tclInt.h b/generic/tclInt.h
index a22348f..53e4323 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -10,7 +10,7 @@
* Copyright (c) 2001, 2002 by Kevin B. Kenny. All rights reserved.
* Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net>
* Copyright (c) 2006-2008 by Joe Mistachkin. All rights reserved.
- * Copyright (c) 2008-2011 by Miguel Sofer. All rights reserved.
+ * Copyright (c) 2008 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.
@@ -1390,6 +1390,13 @@ MODULE_SCOPE void TclThreadDataKeySet(Tcl_ThreadDataKey *keyPtr,
(ThreadSpecificData *)Tcl_GetThreadData((keyPtr), sizeof(ThreadSpecificData))
/*
+ *----------------------------------------------------------------
+ * Data structures related to bytecode compilation and execution. These are
+ * used primarily in tclCompile.c, tclExecute.c, and tclBasic.c.
+ *----------------------------------------------------------------
+ */
+
+/*
* Forward declaration to prevent errors when the forward references to
* Tcl_Parse and CompileEnv are encountered in the procedure type CompileProc
* declared below.
@@ -1431,6 +1438,19 @@ typedef int (CompileHookProc)(Tcl_Interp *interp,
struct CompileEnv *compEnvPtr, ClientData clientData);
/*
+ * The data structure for a (linked list of) execution stacks.
+ */
+
+typedef struct ExecStack {
+ struct ExecStack *prevPtr;
+ struct ExecStack *nextPtr;
+ Tcl_Obj **markerPtr;
+ Tcl_Obj **endPtr;
+ Tcl_Obj **tosPtr;
+ Tcl_Obj *stackWords[1];
+} ExecStack;
+
+/*
* The data structure defining the execution environment for ByteCode's.
* There is one ExecEnv structure per Tcl interpreter. It holds the evaluation
* stack that holds command operands and results. The stack grows towards
@@ -1467,6 +1487,8 @@ typedef struct CoroutineData {
} CoroutineData;
typedef struct ExecEnv {
+ ExecStack *execStackPtr; /* Points to the first item in the evaluation
+ * stack on the heap. */
Tcl_Obj *constants[2]; /* Pointers to constant "0" and "1" objs. */
struct Tcl_Interp *interp;
struct NRE_callback *callbackPtr;
@@ -1747,6 +1769,24 @@ enum PkgPreferOptions {
/*
*----------------------------------------------------------------
+ * This structure shadows the first few fields of the memory cache for the
+ * allocator defined in tclThreadAlloc.c; it has to be kept in sync with the
+ * definition there.
+ * Some macros require knowledge of some fields in the struct in order to
+ * avoid hitting the TSD unnecessarily. In order to facilitate this, a pointer
+ * to the relevant fields is kept in the objCache field in struct Interp.
+ *----------------------------------------------------------------
+ */
+
+typedef struct AllocCache {
+ struct Cache *nextPtr; /* Linked list of cache entries. */
+ Tcl_ThreadId owner; /* Which thread's cache is this? */
+ Tcl_Obj *firstObjPtr; /* List of free objects for thread. */
+ int numObjects; /* Number of objects for thread. */
+} AllocCache;
+
+/*
+ *----------------------------------------------------------------
* This structure defines an interpreter, which is a collection of commands
* plus other state information related to interpreting commands, such as
* variable storage. Primary responsibility for this data structure is in
@@ -2078,6 +2118,7 @@ typedef struct Interp {
* They are used by the macros defined below.
*/
+ AllocCache *allocCache;
void *pendingObjDataPtr; /* Pointer to the Cache and PendingObjData
* structs for this interp's thread; see
* tclObj.c and tclThreadAlloc.c */
@@ -2310,6 +2351,17 @@ struct LimitHandler {
#define UCHAR(c) ((unsigned char) (c))
/*
+ * This macro is used to properly align the memory allocated by Tcl, giving
+ * the same alignment as the native malloc.
+ */
+
+#if defined(__APPLE__)
+#define TCL_ALLOCALIGN 16
+#else
+#define TCL_ALLOCALIGN (2*sizeof(void *))
+#endif
+
+/*
* This macro is used to determine the offset needed to safely allocate any
* data structure in memory. Given a starting offset or size, it "rounds up"
* or "aligns" the offset to the next 8-byte boundary so that any data
@@ -2668,6 +2720,13 @@ MODULE_SCOPE const Tcl_HashKeyType tclOneWordHashKeyType;
MODULE_SCOPE const Tcl_HashKeyType tclStringHashKeyType;
MODULE_SCOPE const Tcl_HashKeyType tclObjHashKeyType;
+/*
+ * The head of the list of free Tcl objects, and the total number of Tcl
+ * objects ever allocated and freed.
+ */
+
+MODULE_SCOPE Tcl_Obj * tclFreeObjList;
+
#ifdef TCL_COMPILE_STATS
MODULE_SCOPE long tclObjsAlloced;
MODULE_SCOPE long tclObjsFreed;
@@ -2843,6 +2902,7 @@ MODULE_SCOPE void TclCreateLateExitHandler(Tcl_ExitProc *proc,
ClientData clientData);
MODULE_SCOPE void TclDeleteLateExitHandler(Tcl_ExitProc *proc,
ClientData clientData);
+MODULE_SCOPE void TclFinalizeAllocSubsystem(void);
MODULE_SCOPE void TclFinalizeAsync(void);
MODULE_SCOPE void TclFinalizeDoubleConversion(void);
MODULE_SCOPE void TclFinalizeEncodingSubsystem(void);
@@ -2859,6 +2919,7 @@ MODULE_SCOPE void TclFinalizeNotifier(void);
MODULE_SCOPE void TclFinalizeObjects(void);
MODULE_SCOPE void TclFinalizePreserve(void);
MODULE_SCOPE void TclFinalizeSynchronization(void);
+MODULE_SCOPE void TclFinalizeThreadAlloc(void);
MODULE_SCOPE void TclFinalizeThreadData(void);
MODULE_SCOPE void TclFinalizeThreadObjects(void);
MODULE_SCOPE double TclFloor(const mp_int *a);
@@ -3036,6 +3097,8 @@ MODULE_SCOPE void TclSetDuplicateObj(Tcl_Obj *dupPtr, Tcl_Obj *objPtr);
MODULE_SCOPE void TclSetProcessGlobalValue(ProcessGlobalValue *pgvPtr,
Tcl_Obj *newValue, Tcl_Encoding encoding);
MODULE_SCOPE void TclSignalExitThread(Tcl_ThreadId id, int result);
+MODULE_SCOPE void * TclStackRealloc(Tcl_Interp *interp, void *ptr,
+ int numBytes);
MODULE_SCOPE int TclStringMatch(const char *str, int strLen,
const char *pattern, int ptnLen, int flags);
MODULE_SCOPE int TclStringMatchObj(Tcl_Obj *stringObj,
@@ -3745,10 +3808,10 @@ typedef const char *TclDTraceStr;
#endif /* TCL_COMPILE_STATS */
# define TclAllocObjStorage(objPtr) \
- (objPtr) = TclSmallAlloc()
+ TclAllocObjStorageEx(NULL, (objPtr))
# define TclFreeObjStorage(objPtr) \
- TclSmallFree(objPtr)
+ TclFreeObjStorageEx(NULL, (objPtr))
#ifndef TCL_MEM_DEBUG
# define TclNewObj(objPtr) \
@@ -3783,125 +3846,128 @@ typedef const char *TclDTraceStr;
} \
}
-#else /* TCL_MEM_DEBUG */
-MODULE_SCOPE void TclDbInitNewObj(Tcl_Obj *objPtr, const char *file,
- int line);
+#if defined(PURIFY)
-# define TclDbNewObj(objPtr, file, line) \
- do { \
- TclIncrObjsAllocated(); \
- (objPtr) = (Tcl_Obj *) \
- Tcl_DbCkalloc(sizeof(Tcl_Obj), (file), (line)); \
- TclDbInitNewObj((objPtr), (file), (line)); \
- TCL_DTRACE_OBJ_CREATE(objPtr); \
- } while (0)
-
-# define TclNewObj(objPtr) \
- TclDbNewObj(objPtr, __FILE__, __LINE__);
-
-# define TclDecrRefCount(objPtr) \
- Tcl_DbDecrRefCount(objPtr, __FILE__, __LINE__)
+/*
+ * The PURIFY mode is like the regular mode, but instead of doing block
+ * Tcl_Obj allocation and keeping a freed list for efficiency, it always
+ * allocates and frees a single Tcl_Obj so that tools like Purify can better
+ * track memory leaks.
+ */
-# define TclNewListObjDirect(objc, objv) \
- TclDbNewListObjDirect(objc, objv, __FILE__, __LINE__)
+# define TclAllocObjStorageEx(interp, objPtr) \
+ (objPtr) = (Tcl_Obj *) Tcl_Alloc(sizeof(Tcl_Obj))
-#endif /* TCL_MEM_DEBUG */
+# define TclFreeObjStorageEx(interp, objPtr) \
+ ckfree((char *) (objPtr))
-/*
- * Macros that drive the allocator behaviour
- */
+#undef USE_THREAD_ALLOC
+#elif defined(TCL_THREADS) && defined(USE_THREAD_ALLOC)
-#if defined(TCL_THREADS)
/*
* The TCL_THREADS mode is like the regular mode but allocates Tcl_Obj's from
* per-thread caches.
*/
-MODULE_SCOPE void TclpFreeAllocCache(void *);
+
+MODULE_SCOPE Tcl_Obj * TclThreadAllocObj(void);
+MODULE_SCOPE void TclThreadFreeObj(Tcl_Obj *);
+MODULE_SCOPE Tcl_Mutex *TclpNewAllocMutex(void);
+MODULE_SCOPE void TclFreeAllocCache(void *);
MODULE_SCOPE void * TclpGetAllocCache(void);
MODULE_SCOPE void TclpSetAllocCache(void *);
-MODULE_SCOPE void TclFreeAllocCache(void *);
MODULE_SCOPE void TclpFreeAllocMutex(Tcl_Mutex *mutex);
-MODULE_SCOPE Tcl_Mutex *TclpNewAllocMutex(void);
-#endif
+MODULE_SCOPE void TclpFreeAllocCache(void *);
/*
- * List of valid allocators. Have to respect the following convention:
- * - allocators that shunt TclpAlloc to malloc are below aNONE
- * - allocators that use zippy are above aNONE
+ * These macros need to be kept in sync with the code of TclThreadAllocObj()
+ * and TclThreadFreeObj().
+ *
+ * Note that the optimiser should resolve the case (interp==NULL) at compile
+ * time.
*/
-#define aNATIVE 0
-#define aPURIFY 1
-#define aNONE 2
-#define aZIPPY 3
-#define aMULTI 4
+# define ALLOC_NOBJHIGH 1200
-#if defined(TCL_ALLOCATOR) && ((TCL_ALLOCATOR < 0) || (TCL_ALLOCATOR > aMULTI))
-#undef TCL_ALLOCATOR
-#endif
+# define TclAllocObjStorageEx(interp, objPtr) \
+ do { \
+ AllocCache *cachePtr; \
+ if (((interp) == NULL) || \
+ ((cachePtr = ((Interp *)(interp))->allocCache), \
+ (cachePtr->numObjects == 0))) { \
+ (objPtr) = TclThreadAllocObj(); \
+ } else { \
+ (objPtr) = cachePtr->firstObjPtr; \
+ cachePtr->firstObjPtr = (objPtr)->internalRep.otherValuePtr; \
+ --cachePtr->numObjects; \
+ } \
+ } while (0)
-#ifdef PURIFY
-# undef TCL_ALLOCATOR
-# define TCL_ALLOCATOR aPURIFY
-#endif
+# define TclFreeObjStorageEx(interp, objPtr) \
+ do { \
+ AllocCache *cachePtr; \
+ if (((interp) == NULL) || \
+ ((cachePtr = ((Interp *)(interp))->allocCache), \
+ (cachePtr->numObjects >= ALLOC_NOBJHIGH))) { \
+ TclThreadFreeObj(objPtr); \
+ } else { \
+ (objPtr)->internalRep.otherValuePtr = cachePtr->firstObjPtr; \
+ cachePtr->firstObjPtr = objPtr; \
+ ++cachePtr->numObjects; \
+ } \
+ } while (0)
-#if !defined(TCL_ALLOCATOR)
-# if defined(USE_THREAD_ALLOC) || defined(USE_TCLALLOC)
-# define TCL_ALLOCATOR aZIPPY
-# else
-# define TCL_ALLOCATOR aNATIVE
-# endif
-#endif
+#else /* not PURIFY or USE_THREAD_ALLOC */
-#if TCL_ALLOCATOR < aNONE /* native or purify */
-# define TclpAlloc(size) malloc(size)
-# define TclpRealloc(ptr, size) realloc((ptr),(size))
-# define TclpFree(size) free(size)
-# define TclAllocMaximize(ptr) UINT_MAX
-#else
- MODULE_SCOPE char * TclpAlloc(unsigned int size);
- MODULE_SCOPE char * TclpRealloc(char * ptr, unsigned int size);
- MODULE_SCOPE void TclpFree(char * ptr);
- MODULE_SCOPE unsigned int TclAllocMaximize(void *ptr);
+#ifdef TCL_THREADS
+/* declared in tclObj.c */
+MODULE_SCOPE Tcl_Mutex tclObjMutex;
#endif
-#if TCL_ALLOCATOR == aPURIFY
-# define TclSmallAlloc() ckalloc(sizeof(Tcl_Obj))
-# define TclSmallFree(ptr) ckfree(ptr)
-# define TclInitAlloc()
-# define TclFinalizeAlloc()
-# define TclFreeAllocCache(ptr)
-#else
- MODULE_SCOPE void * TclSmallAlloc();
- MODULE_SCOPE void TclSmallFree(void *ptr);
- MODULE_SCOPE void TclInitAlloc(void);
- MODULE_SCOPE void TclFinalizeAlloc(void);
+# define TclAllocObjStorageEx(interp, objPtr) \
+ do { \
+ Tcl_MutexLock(&tclObjMutex); \
+ if (tclFreeObjList == NULL) { \
+ TclAllocateFreeObjects(); \
+ } \
+ (objPtr) = tclFreeObjList; \
+ tclFreeObjList = (Tcl_Obj *) \
+ tclFreeObjList->internalRep.otherValuePtr; \
+ Tcl_MutexUnlock(&tclObjMutex); \
+ } while (0)
+
+# define TclFreeObjStorageEx(interp, objPtr) \
+ do { \
+ Tcl_MutexLock(&tclObjMutex); \
+ (objPtr)->internalRep.otherValuePtr = (void *) tclFreeObjList; \
+ tclFreeObjList = (objPtr); \
+ Tcl_MutexUnlock(&tclObjMutex); \
+ } while (0)
#endif
-#define TclCkSmallAlloc(nbytes, memPtr) \
- do { \
- TCL_CT_ASSERT((nbytes)<=sizeof(Tcl_Obj)); \
- memPtr = TclSmallAlloc(); \
+#else /* TCL_MEM_DEBUG */
+MODULE_SCOPE void TclDbInitNewObj(Tcl_Obj *objPtr, const char *file,
+ int line);
+
+# define TclDbNewObj(objPtr, file, line) \
+ do { \
+ TclIncrObjsAllocated(); \
+ (objPtr) = (Tcl_Obj *) \
+ Tcl_DbCkalloc(sizeof(Tcl_Obj), (file), (line)); \
+ TclDbInitNewObj((objPtr), (file), (line)); \
+ TCL_DTRACE_OBJ_CREATE(objPtr); \
} while (0)
-/*
- * Support for Clang Static Analyzer <http://clang-analyzer.llvm.org>
- */
+# define TclNewObj(objPtr) \
+ TclDbNewObj(objPtr, __FILE__, __LINE__);
-#if (TCL_ALLOCATOR == aPURIFY) && defined(__clang__)
-#if __has_feature(attribute_analyzer_noreturn) && \
- !defined(Tcl_Panic) && defined(Tcl_Panic_TCL_DECLARED)
-void Tcl_Panic(const char *, ...) __attribute__((analyzer_noreturn));
-#endif
-#if !defined(CLANG_ASSERT)
-#include <assert.h>
-#define CLANG_ASSERT(x) assert(x)
-#endif
-#elif !defined(CLANG_ASSERT)
- #define CLANG_ASSERT(x)
-#endif /* PURIFY && __clang__ */
+# define TclDecrRefCount(objPtr) \
+ Tcl_DbDecrRefCount(objPtr, __FILE__, __LINE__)
+# define TclNewListObjDirect(objc, objv) \
+ TclDbNewListObjDirect(objc, objv, __FILE__, __LINE__)
+#undef USE_THREAD_ALLOC
+#endif /* TCL_MEM_DEBUG */
/*
*----------------------------------------------------------------
@@ -4405,11 +4471,73 @@ MODULE_SCOPE Tcl_PackageInitProc Procbodytest_SafeInit;
{enum { ct_assert_value = 1/(!!(e)) };}
/*
+ *----------------------------------------------------------------
+ * Allocator for small structs (<=sizeof(Tcl_Obj)) using the Tcl_Obj pool.
+ * Only checked at compile time.
+ *
+ * ONLY USE FOR CONSTANT nBytes.
+ *
+ * DO NOT LET THEM CROSS THREAD BOUNDARIES
+ *----------------------------------------------------------------
+ */
+
+#define TclSmallAlloc(nbytes, memPtr) \
+ TclSmallAllocEx(NULL, (nbytes), (memPtr))
+
+#define TclSmallFree(memPtr) \
+ TclSmallFreeEx(NULL, (memPtr))
+
+#ifndef TCL_MEM_DEBUG
+#define TclSmallAllocEx(interp, nbytes, memPtr) \
+ do { \
+ Tcl_Obj *objPtr; \
+ TCL_CT_ASSERT((nbytes)<=sizeof(Tcl_Obj)); \
+ TclIncrObjsAllocated(); \
+ TclAllocObjStorageEx((interp), (objPtr)); \
+ memPtr = (ClientData) (objPtr); \
+ } while (0)
+
+#define TclSmallFreeEx(interp, memPtr) \
+ do { \
+ TclFreeObjStorageEx((interp), (Tcl_Obj *) (memPtr)); \
+ TclIncrObjsFreed(); \
+ } while (0)
+
+#else /* TCL_MEM_DEBUG */
+#define TclSmallAllocEx(interp, nbytes, memPtr) \
+ do { \
+ Tcl_Obj *objPtr; \
+ TCL_CT_ASSERT((nbytes)<=sizeof(Tcl_Obj)); \
+ TclNewObj(objPtr); \
+ memPtr = (ClientData) objPtr; \
+ } while (0)
+
+#define TclSmallFreeEx(interp, memPtr) \
+ do { \
+ Tcl_Obj *objPtr = (Tcl_Obj *) memPtr; \
+ objPtr->bytes = NULL; \
+ objPtr->typePtr = NULL; \
+ objPtr->refCount = 1; \
+ TclDecrRefCount(objPtr); \
+ } while (0)
+#endif /* TCL_MEM_DEBUG */
+
+/*
* Support for Clang Static Analyzer <http://clang-analyzer.llvm.org>
*/
+#if defined(PURIFY) && defined(__clang__)
+#if __has_feature(attribute_analyzer_noreturn) && \
+ !defined(Tcl_Panic) && defined(Tcl_Panic_TCL_DECLARED)
+void Tcl_Panic(const char *, ...) __attribute__((analyzer_noreturn));
+#endif
+#if !defined(CLANG_ASSERT)
+#include <assert.h>
+#define CLANG_ASSERT(x) assert(x)
+#endif
+#elif !defined(CLANG_ASSERT)
#define CLANG_ASSERT(x)
-
+#endif /* PURIFY && __clang__ */
/*
*----------------------------------------------------------------
@@ -4482,8 +4610,8 @@ typedef struct NRE_callback {
#if NRE_USE_SMALL_ALLOC
#define TCLNR_ALLOC(interp, ptr) \
- TclCkSmallAlloc(sizeof(NRE_callback), (ptr))
-#define TCLNR_FREE(interp, ptr) TclSmallFree(ptr)
+ TclSmallAllocEx(interp, sizeof(NRE_callback), (ptr))
+#define TCLNR_FREE(interp, ptr) TclSmallFreeEx((interp), (ptr))
#else
#define TCLNR_ALLOC(interp, ptr) \
(ptr = ((ClientData) ckalloc(sizeof(NRE_callback))))