os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPreserve.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
/* 
sl@0
     2
 * tclPreserve.c --
sl@0
     3
 *
sl@0
     4
 *	This file contains a collection of procedures that are used
sl@0
     5
 *	to make sure that widget records and other data structures
sl@0
     6
 *	aren't reallocated when there are nested procedures that
sl@0
     7
 *	depend on their existence.
sl@0
     8
 *
sl@0
     9
 * Copyright (c) 1991-1994 The Regents of the University of California.
sl@0
    10
 * Copyright (c) 1994-1998 Sun Microsystems, Inc.
sl@0
    11
 * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
sl@0
    12
 *
sl@0
    13
 * See the file "license.terms" for information on usage and redistribution
sl@0
    14
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    15
 *
sl@0
    16
 * RCS: @(#) $Id: tclPreserve.c,v 1.3.34.2 2005/06/24 18:21:41 kennykb Exp $
sl@0
    17
 */
sl@0
    18
sl@0
    19
#include "tclInt.h"
sl@0
    20
sl@0
    21
/*
sl@0
    22
 * The following data structure is used to keep track of all the
sl@0
    23
 * Tcl_Preserve calls that are still in effect.  It grows as needed
sl@0
    24
 * to accommodate any number of calls in effect.
sl@0
    25
 */
sl@0
    26
sl@0
    27
typedef struct {
sl@0
    28
    ClientData clientData;	/* Address of preserved block. */
sl@0
    29
    int refCount;		/* Number of Tcl_Preserve calls in effect
sl@0
    30
				 * for block. */
sl@0
    31
    int mustFree;		/* Non-zero means Tcl_EventuallyFree was
sl@0
    32
				 * called while a Tcl_Preserve call was in
sl@0
    33
				 * effect, so the structure must be freed
sl@0
    34
				 * when refCount becomes zero. */
sl@0
    35
    Tcl_FreeProc *freeProc;	/* Procedure to call to free. */
sl@0
    36
} Reference;
sl@0
    37
sl@0
    38
#if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
sl@0
    39
static Reference *refArray;	/* First in array of references. */
sl@0
    40
static int spaceAvl = 0;	/* Total number of structures available
sl@0
    41
				 * at *firstRefPtr. */
sl@0
    42
static int inUse = 0;		/* Count of structures currently in use
sl@0
    43
				 * in refArray. */
sl@0
    44
#else
sl@0
    45
#define refArray (*(Reference**)get_refArray())
sl@0
    46
#define spaceAvl (*(int *)get_spaceAvl())
sl@0
    47
#define inUse (*(int *)get_inUse())
sl@0
    48
#endif
sl@0
    49
#define INITIAL_SIZE 2
sl@0
    50
TCL_DECLARE_MUTEX(preserveMutex)/* To protect the above statics */
sl@0
    51
sl@0
    52
/*
sl@0
    53
 * The following data structure is used to keep track of whether an
sl@0
    54
 * arbitrary block of memory has been deleted.  This is used by the
sl@0
    55
 * TclHandle code to avoid the more time-expensive algorithm of
sl@0
    56
 * Tcl_Preserve().  This mechanism is mainly used when we have lots of
sl@0
    57
 * references to a few big, expensive objects that we don't want to live
sl@0
    58
 * any longer than necessary.
sl@0
    59
 */
sl@0
    60
sl@0
    61
typedef struct HandleStruct {
sl@0
    62
    VOID *ptr;			/* Pointer to the memory block being
sl@0
    63
				 * tracked.  This field will become NULL when
sl@0
    64
				 * the memory block is deleted.  This field
sl@0
    65
				 * must be the first in the structure. */
sl@0
    66
#ifdef TCL_MEM_DEBUG
sl@0
    67
    VOID *ptr2;			/* Backup copy of the abpve pointer used to
sl@0
    68
				 * ensure that the contents of the handle are
sl@0
    69
				 * not changed by anyone else. */
sl@0
    70
#endif
sl@0
    71
    int refCount;		/* Number of TclHandlePreserve() calls in
sl@0
    72
				 * effect on this handle. */
sl@0
    73
} HandleStruct;
sl@0
    74
sl@0
    75
sl@0
    76

sl@0
    77
/*
sl@0
    78
 *----------------------------------------------------------------------
sl@0
    79
 *
sl@0
    80
 * TclFinalizePreserve --
sl@0
    81
 *
sl@0
    82
 *	Called during exit processing to clean up the reference array.
sl@0
    83
 *
sl@0
    84
 * Results:
sl@0
    85
 *	None.
sl@0
    86
 *
sl@0
    87
 * Side effects:
sl@0
    88
 *	Frees the storage of the reference array.
sl@0
    89
 *
sl@0
    90
 *----------------------------------------------------------------------
sl@0
    91
 */
sl@0
    92
sl@0
    93
	/* ARGSUSED */
sl@0
    94
void
sl@0
    95
TclFinalizePreserve()
sl@0
    96
{
sl@0
    97
    Tcl_MutexLock(&preserveMutex);
sl@0
    98
    if (spaceAvl != 0) {
sl@0
    99
        ckfree((char *) refArray);
sl@0
   100
        refArray = (Reference *) NULL;
sl@0
   101
        inUse = 0;
sl@0
   102
        spaceAvl = 0;
sl@0
   103
    }
sl@0
   104
    Tcl_MutexUnlock(&preserveMutex);
sl@0
   105
}
sl@0
   106

sl@0
   107
/*
sl@0
   108
 *----------------------------------------------------------------------
sl@0
   109
 *
sl@0
   110
 * Tcl_Preserve --
sl@0
   111
 *
sl@0
   112
 *	This procedure is used by a procedure to declare its interest
sl@0
   113
 *	in a particular block of memory, so that the block will not be
sl@0
   114
 *	reallocated until a matching call to Tcl_Release has been made.
sl@0
   115
 *
sl@0
   116
 * Results:
sl@0
   117
 *	None.
sl@0
   118
 *
sl@0
   119
 * Side effects:
sl@0
   120
 *	Information is retained so that the block of memory will
sl@0
   121
 *	not be freed until at least the matching call to Tcl_Release.
sl@0
   122
 *
sl@0
   123
 *----------------------------------------------------------------------
sl@0
   124
 */
sl@0
   125
sl@0
   126
EXPORT_C void
sl@0
   127
Tcl_Preserve(clientData)
sl@0
   128
    ClientData clientData;	/* Pointer to malloc'ed block of memory. */
sl@0
   129
{
sl@0
   130
    Reference *refPtr;
sl@0
   131
    int i;
sl@0
   132
sl@0
   133
    /*
sl@0
   134
     * See if there is already a reference for this pointer.  If so,
sl@0
   135
     * just increment its reference count.
sl@0
   136
     */
sl@0
   137
sl@0
   138
    Tcl_MutexLock(&preserveMutex);
sl@0
   139
    for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
sl@0
   140
	if (refPtr->clientData == clientData) {
sl@0
   141
	    refPtr->refCount++;
sl@0
   142
	    Tcl_MutexUnlock(&preserveMutex);
sl@0
   143
	    return;
sl@0
   144
	}
sl@0
   145
    }
sl@0
   146
sl@0
   147
    /*
sl@0
   148
     * Make a reference array if it doesn't already exist, or make it
sl@0
   149
     * bigger if it is full.
sl@0
   150
     */
sl@0
   151
sl@0
   152
    if (inUse == spaceAvl) {
sl@0
   153
	if (spaceAvl == 0) {
sl@0
   154
	    refArray = (Reference *) ckalloc((unsigned)
sl@0
   155
		    (INITIAL_SIZE*sizeof(Reference)));
sl@0
   156
	    spaceAvl = INITIAL_SIZE;
sl@0
   157
	} else {
sl@0
   158
	    Reference *new;
sl@0
   159
sl@0
   160
	    new = (Reference *) ckalloc((unsigned)
sl@0
   161
		    (2*spaceAvl*sizeof(Reference)));
sl@0
   162
	    memcpy((VOID *) new, (VOID *) refArray,
sl@0
   163
                    spaceAvl*sizeof(Reference));
sl@0
   164
	    ckfree((char *) refArray);
sl@0
   165
	    refArray = new;
sl@0
   166
	    spaceAvl *= 2;
sl@0
   167
	}
sl@0
   168
    }
sl@0
   169
sl@0
   170
    /*
sl@0
   171
     * Make a new entry for the new reference.
sl@0
   172
     */
sl@0
   173
sl@0
   174
    refPtr = &refArray[inUse];
sl@0
   175
    refPtr->clientData = clientData;
sl@0
   176
    refPtr->refCount = 1;
sl@0
   177
    refPtr->mustFree = 0;
sl@0
   178
    refPtr->freeProc = TCL_STATIC;
sl@0
   179
    inUse += 1;
sl@0
   180
    Tcl_MutexUnlock(&preserveMutex);
sl@0
   181
}
sl@0
   182

sl@0
   183
/*
sl@0
   184
 *----------------------------------------------------------------------
sl@0
   185
 *
sl@0
   186
 * Tcl_Release --
sl@0
   187
 *
sl@0
   188
 *	This procedure is called to cancel a previous call to
sl@0
   189
 *	Tcl_Preserve, thereby allowing a block of memory to be
sl@0
   190
 *	freed (if no one else cares about it).
sl@0
   191
 *
sl@0
   192
 * Results:
sl@0
   193
 *	None.
sl@0
   194
 *
sl@0
   195
 * Side effects:
sl@0
   196
 *	If Tcl_EventuallyFree has been called for clientData, and if
sl@0
   197
 *	no other call to Tcl_Preserve is still in effect, the block of
sl@0
   198
 *	memory is freed.
sl@0
   199
 *
sl@0
   200
 *----------------------------------------------------------------------
sl@0
   201
 */
sl@0
   202
sl@0
   203
EXPORT_C void
sl@0
   204
Tcl_Release(clientData)
sl@0
   205
    ClientData clientData;	/* Pointer to malloc'ed block of memory. */
sl@0
   206
{
sl@0
   207
    Reference *refPtr;
sl@0
   208
    int mustFree;
sl@0
   209
    Tcl_FreeProc *freeProc;
sl@0
   210
    int i;
sl@0
   211
sl@0
   212
    Tcl_MutexLock(&preserveMutex);
sl@0
   213
    for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
sl@0
   214
	if (refPtr->clientData != clientData) {
sl@0
   215
	    continue;
sl@0
   216
	}
sl@0
   217
	refPtr->refCount--;
sl@0
   218
	if (refPtr->refCount == 0) {
sl@0
   219
sl@0
   220
            /*
sl@0
   221
             * Must remove information from the slot before calling freeProc
sl@0
   222
             * to avoid reentrancy problems if the freeProc calls Tcl_Preserve
sl@0
   223
             * on the same clientData. Copy down the last reference in the
sl@0
   224
             * array to overwrite the current slot.
sl@0
   225
             */
sl@0
   226
sl@0
   227
            freeProc = refPtr->freeProc;
sl@0
   228
            mustFree = refPtr->mustFree;
sl@0
   229
	    inUse--;
sl@0
   230
	    if (i < inUse) {
sl@0
   231
		refArray[i] = refArray[inUse];
sl@0
   232
	    }
sl@0
   233
	    if (mustFree) {
sl@0
   234
		if (freeProc == TCL_DYNAMIC) {
sl@0
   235
		    ckfree((char *) clientData);
sl@0
   236
		} else {
sl@0
   237
		    Tcl_MutexUnlock(&preserveMutex);
sl@0
   238
		    (*freeProc)((char *) clientData);
sl@0
   239
		    return;
sl@0
   240
		}
sl@0
   241
	    }
sl@0
   242
	}
sl@0
   243
	Tcl_MutexUnlock(&preserveMutex);
sl@0
   244
	return;
sl@0
   245
    }
sl@0
   246
    Tcl_MutexUnlock(&preserveMutex);
sl@0
   247
sl@0
   248
    /*
sl@0
   249
     * Reference not found.  This is a bug in the caller.
sl@0
   250
     */
sl@0
   251
sl@0
   252
    panic("Tcl_Release couldn't find reference for 0x%x", clientData);
sl@0
   253
}
sl@0
   254

sl@0
   255
/*
sl@0
   256
 *----------------------------------------------------------------------
sl@0
   257
 *
sl@0
   258
 * Tcl_EventuallyFree --
sl@0
   259
 *
sl@0
   260
 *	Free up a block of memory, unless a call to Tcl_Preserve is in
sl@0
   261
 *	effect for that block.  In this case, defer the free until all
sl@0
   262
 *	calls to Tcl_Preserve have been undone by matching calls to
sl@0
   263
 *	Tcl_Release.
sl@0
   264
 *
sl@0
   265
 * Results:
sl@0
   266
 *	None.
sl@0
   267
 *
sl@0
   268
 * Side effects:
sl@0
   269
 *	Ptr may be released by calling free().
sl@0
   270
 *
sl@0
   271
 *----------------------------------------------------------------------
sl@0
   272
 */
sl@0
   273
sl@0
   274
EXPORT_C void
sl@0
   275
Tcl_EventuallyFree(clientData, freeProc)
sl@0
   276
    ClientData clientData;	/* Pointer to malloc'ed block of memory. */
sl@0
   277
    Tcl_FreeProc *freeProc;	/* Procedure to actually do free. */
sl@0
   278
{
sl@0
   279
    Reference *refPtr;
sl@0
   280
    int i;
sl@0
   281
sl@0
   282
    /*
sl@0
   283
     * See if there is a reference for this pointer.  If so, set its
sl@0
   284
     * "mustFree" flag (the flag had better not be set already!).
sl@0
   285
     */
sl@0
   286
sl@0
   287
    Tcl_MutexLock(&preserveMutex);
sl@0
   288
    for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
sl@0
   289
	if (refPtr->clientData != clientData) {
sl@0
   290
	    continue;
sl@0
   291
	}
sl@0
   292
	if (refPtr->mustFree) {
sl@0
   293
	    panic("Tcl_EventuallyFree called twice for 0x%x\n", clientData);
sl@0
   294
        }
sl@0
   295
        refPtr->mustFree = 1;
sl@0
   296
	refPtr->freeProc = freeProc;
sl@0
   297
	Tcl_MutexUnlock(&preserveMutex);
sl@0
   298
        return;
sl@0
   299
    }
sl@0
   300
    Tcl_MutexUnlock(&preserveMutex);
sl@0
   301
sl@0
   302
    /*
sl@0
   303
     * No reference for this block.  Free it now.
sl@0
   304
     */
sl@0
   305
sl@0
   306
    if (freeProc == TCL_DYNAMIC) {
sl@0
   307
	ckfree((char *) clientData);
sl@0
   308
    } else {
sl@0
   309
	(*freeProc)((char *)clientData);
sl@0
   310
    }
sl@0
   311
}
sl@0
   312

sl@0
   313
/*
sl@0
   314
 *---------------------------------------------------------------------------
sl@0
   315
 *
sl@0
   316
 * TclHandleCreate --
sl@0
   317
 *
sl@0
   318
 *	Allocate a handle that contains enough information to determine
sl@0
   319
 *	if an arbitrary malloc'd block has been deleted.  This is 
sl@0
   320
 *	used to avoid the more time-expensive algorithm of Tcl_Preserve().
sl@0
   321
 *
sl@0
   322
 * Results:
sl@0
   323
 *	The return value is a TclHandle that refers to the given malloc'd
sl@0
   324
 *	block.  Doubly dereferencing the returned handle will give
sl@0
   325
 *	back the pointer to the block, or will give NULL if the block has
sl@0
   326
 *	been deleted.
sl@0
   327
 *
sl@0
   328
 * Side effects:
sl@0
   329
 *	The caller must keep track of this handle (generally by storing
sl@0
   330
 *	it in a field in the malloc'd block) and call TclHandleFree()
sl@0
   331
 *	on this handle when the block is deleted.  Everything else that
sl@0
   332
 *	wishes to keep track of whether the malloc'd block has been deleted
sl@0
   333
 *	should use calls to TclHandlePreserve() and TclHandleRelease()
sl@0
   334
 *	on the associated handle.
sl@0
   335
 *
sl@0
   336
 *---------------------------------------------------------------------------
sl@0
   337
 */
sl@0
   338
sl@0
   339
TclHandle
sl@0
   340
TclHandleCreate(ptr)
sl@0
   341
    VOID *ptr;			/* Pointer to an arbitrary block of memory
sl@0
   342
				 * to be tracked for deletion.  Must not be
sl@0
   343
				 * NULL. */
sl@0
   344
{
sl@0
   345
    HandleStruct *handlePtr;
sl@0
   346
sl@0
   347
    handlePtr = (HandleStruct *) ckalloc(sizeof(HandleStruct));
sl@0
   348
    handlePtr->ptr = ptr;
sl@0
   349
#ifdef TCL_MEM_DEBUG
sl@0
   350
    handlePtr->ptr2 = ptr;
sl@0
   351
#endif
sl@0
   352
    handlePtr->refCount = 0;
sl@0
   353
    return (TclHandle) handlePtr;
sl@0
   354
}
sl@0
   355

sl@0
   356
/*
sl@0
   357
 *---------------------------------------------------------------------------
sl@0
   358
 *
sl@0
   359
 * TclHandleFree --
sl@0
   360
 *
sl@0
   361
 *	Called when the arbitrary malloc'd block associated with the
sl@0
   362
 *	handle is being deleted.  Modifies the handle so that doubly
sl@0
   363
 *	dereferencing it will give NULL.  This informs any user of the
sl@0
   364
 *	handle that the block of memory formerly referenced by the
sl@0
   365
 *	handle has been freed.
sl@0
   366
 *
sl@0
   367
 * Results:
sl@0
   368
 *	None.
sl@0
   369
 *
sl@0
   370
 * Side effects:
sl@0
   371
 *	If nothing is referring to the handle, the handle will be reclaimed.
sl@0
   372
 *
sl@0
   373
 *---------------------------------------------------------------------------
sl@0
   374
 */
sl@0
   375
sl@0
   376
void
sl@0
   377
TclHandleFree(handle)
sl@0
   378
    TclHandle handle;		/* Previously created handle associated
sl@0
   379
				 * with a malloc'd block that is being
sl@0
   380
				 * deleted.  The handle is modified so that
sl@0
   381
				 * doubly dereferencing it will give NULL. */
sl@0
   382
{
sl@0
   383
    HandleStruct *handlePtr;
sl@0
   384
sl@0
   385
    handlePtr = (HandleStruct *) handle;
sl@0
   386
#ifdef TCL_MEM_DEBUG
sl@0
   387
    if (handlePtr->refCount == 0x61616161) {
sl@0
   388
	panic("using previously disposed TclHandle %x", handlePtr);
sl@0
   389
    }
sl@0
   390
    if (handlePtr->ptr2 != handlePtr->ptr) {
sl@0
   391
	panic("someone has changed the block referenced by the handle %x\nfrom %x to %x",
sl@0
   392
		handlePtr, handlePtr->ptr2, handlePtr->ptr);
sl@0
   393
    }
sl@0
   394
#endif
sl@0
   395
    handlePtr->ptr = NULL;
sl@0
   396
    if (handlePtr->refCount == 0) {
sl@0
   397
	ckfree((char *) handlePtr);
sl@0
   398
    }
sl@0
   399
}
sl@0
   400

sl@0
   401
/*
sl@0
   402
 *---------------------------------------------------------------------------
sl@0
   403
 *
sl@0
   404
 * TclHandlePreserve --
sl@0
   405
 *
sl@0
   406
 *	Declare an interest in the arbitrary malloc'd block associated
sl@0
   407
 *	with the handle.  
sl@0
   408
 *
sl@0
   409
 * Results:
sl@0
   410
 *	The return value is the handle argument, with its ref count
sl@0
   411
 *	incremented.
sl@0
   412
 *
sl@0
   413
 * Side effects:
sl@0
   414
 *	For each call to TclHandlePreserve(), there should be a matching
sl@0
   415
 *	call to TclHandleRelease() when the caller is no longer interested
sl@0
   416
 *	in the malloc'd block associated with the handle.
sl@0
   417
 *
sl@0
   418
 *---------------------------------------------------------------------------
sl@0
   419
 */
sl@0
   420
sl@0
   421
TclHandle
sl@0
   422
TclHandlePreserve(handle)
sl@0
   423
    TclHandle handle;		/* Declare an interest in the block of
sl@0
   424
				 * memory referenced by this handle. */
sl@0
   425
{
sl@0
   426
    HandleStruct *handlePtr;
sl@0
   427
sl@0
   428
    handlePtr = (HandleStruct *) handle;
sl@0
   429
#ifdef TCL_MEM_DEBUG
sl@0
   430
    if (handlePtr->refCount == 0x61616161) {
sl@0
   431
	panic("using previously disposed TclHandle %x", handlePtr);
sl@0
   432
    }
sl@0
   433
    if ((handlePtr->ptr != NULL)
sl@0
   434
	    && (handlePtr->ptr != handlePtr->ptr2)) {
sl@0
   435
	panic("someone has changed the block referenced by the handle %x\nfrom %x to %x",
sl@0
   436
		handlePtr, handlePtr->ptr2, handlePtr->ptr);
sl@0
   437
    }
sl@0
   438
#endif
sl@0
   439
    handlePtr->refCount++;
sl@0
   440
sl@0
   441
    return handle;
sl@0
   442
}
sl@0
   443

sl@0
   444
/*
sl@0
   445
 *---------------------------------------------------------------------------
sl@0
   446
 *
sl@0
   447
 * TclHandleRelease --
sl@0
   448
 *
sl@0
   449
 *	This procedure is called to release an interest in the malloc'd
sl@0
   450
 *	block associated with the handle.
sl@0
   451
 *
sl@0
   452
 * Results:
sl@0
   453
 *	None.
sl@0
   454
 *
sl@0
   455
 * Side effects:
sl@0
   456
 *	The ref count of the handle is decremented.  If the malloc'd block
sl@0
   457
 *	has been freed and if no one is using the handle any more, the
sl@0
   458
 *	handle will be reclaimed.
sl@0
   459
 *
sl@0
   460
 *---------------------------------------------------------------------------
sl@0
   461
 */
sl@0
   462
 
sl@0
   463
void
sl@0
   464
TclHandleRelease(handle)
sl@0
   465
    TclHandle handle;		/* Unregister interest in the block of
sl@0
   466
				 * memory referenced by this handle. */
sl@0
   467
{
sl@0
   468
    HandleStruct *handlePtr;
sl@0
   469
sl@0
   470
    handlePtr = (HandleStruct *) handle;
sl@0
   471
#ifdef TCL_MEM_DEBUG
sl@0
   472
    if (handlePtr->refCount == 0x61616161) {
sl@0
   473
	panic("using previously disposed TclHandle %x", handlePtr);
sl@0
   474
    }
sl@0
   475
    if ((handlePtr->ptr != NULL)
sl@0
   476
	    && (handlePtr->ptr != handlePtr->ptr2)) {
sl@0
   477
	panic("someone has changed the block referenced by the handle %x\nfrom %x to %x",
sl@0
   478
		handlePtr, handlePtr->ptr2, handlePtr->ptr);
sl@0
   479
    }
sl@0
   480
#endif
sl@0
   481
    handlePtr->refCount--;
sl@0
   482
    if ((handlePtr->refCount == 0) && (handlePtr->ptr == NULL)) {
sl@0
   483
	ckfree((char *) handlePtr);
sl@0
   484
    }
sl@0
   485
}
sl@0
   486