rem: Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS);
add: Tcl_InitHashTable(tablePtr, TCL_ONE_WORD_KEYS);
context: static Ns_TlsCleanup FreeTable;static Ns_Callback CheckPool;static Ns_ArgProc CheckArgProc;/* * Static variables defined in this file */static Tcl_HashTable poolsTable;static Tcl_HashTable serversTable;static Ns_Tls tls;/* *---------------------------------------------------------------------- * * Ns_DbPoolDescription -- * *	Return the pool's description string. * * Results: *	Configured description string or NULL. * * Side effects: *	None. * *---------------------------------------------------------------------- */char *Ns_DbPoolDescription(char *pool){    Pool         *poolPtr;    poolPtr = GetPool(pool);    if (poolPtr == NULL) {        return NULL;    }    return poolPtr->desc;}/* *---------------------------------------------------------------------- * * Ns_DbPoolDefault -- * *	Return the default pool. * * Results: *	String name of default pool or NULL if no default is defined. * * Side effects: *	None. * *---------------------------------------------------------------------- */char *Ns_DbPoolDefault(char *server){    ServData *sdataPtr = GetServer(server);    return (sdataPtr ? sdataPtr->defpool : NULL);}/* *---------------------------------------------------------------------- * * Ns_DbPoolList -- * *	Return the list of all pools. * * Results: *	Double-null terminated list of pool names. * * Side effects: *	None. * *---------------------------------------------------------------------- */char *Ns_DbPoolList(char *server){    ServData *sdataPtr = GetServer(server);    return (sdataPtr ? sdataPtr->allowed : NULL);}/* *---------------------------------------------------------------------- * * Ns_DbPoolAllowable -- * *	Check that access is allowed to a pool. * * Results: *	NS_TRUE if allowed, NS_FALSE otherwise. * * Side effects: *	None. * *---------------------------------------------------------------------- */intNs_DbPoolAllowable(char *server, char *pool){    register char *p;    p = Ns_DbPoolList(server);    if (p != NULL) {        while (*p != '\0') {            if (STREQ(pool, p)) {                return NS_TRUE;            }            p = p + strlen(p) + 1;        }    }    return NS_FALSE;}/* *---------------------------------------------------------------------- * * Ns_DbPoolPutHandle -- * *	Cleanup and then return a handle to its pool. * * Results: *	None. * * Side effects: *	Handle is flushed, reset, and possibly closed as required. * *---------------------------------------------------------------------- */voidNs_DbPoolPutHandle(Ns_DbHandle *handle){    Handle	*handlePtr;    Pool	*poolPtr;    time_t	 now;    handlePtr = (Handle *) handle;    poolPtr = handlePtr->poolPtr;    /*     * Cleanup the handle.     */    Ns_DbFlush(handle);    Ns_DbResetHandle(handle);    Ns_DStringFree(&handle->dsExceptionMsg);    handle->cExceptionCode[0] = '\0';    /*     * Close the handle if it's stale, otherwise update     * the last access time.     */    time(&now);    if (IsStale(handlePtr, now)) {        NsDbDisconnect(handle);    } else {        handlePtr->atime = now;    }    IncrCount(poolPtr, -1);    Ns_MutexLock(&poolPtr->lock);    ReturnHandle(handlePtr);    if (poolPtr->waiting) {	Ns_CondSignal(&poolPtr->getCond);    }    Ns_MutexUnlock(&poolPtr->lock);}/* *---------------------------------------------------------------------- * * Ns_DbPoolTimedGetHandle -- * *	Return a single handle from a pool within the given number of *	seconds. * * Results: *	Pointer to Ns_DbHandle or NULL on error or timeout. * * Side effects: *	Database may be opened if needed. * *---------------------------------------------------------------------- */Ns_DbHandle *Ns_DbPoolTimedGetHandle(char *pool, int wait){    Ns_DbHandle       *handle;    if (Ns_DbPoolTimedGetMultipleHandles(&handle, pool, 1, wait) != NS_OK) {        return NULL;    }    return handle;}/* *---------------------------------------------------------------------- * * Ns_DbPoolGetHandle -- * *	Return a single handle from a pool. * * Results: *	Pointer to Ns_DbHandle or NULL on error. * * Side effects: *	Database may be opened if needed. * *---------------------------------------------------------------------- */Ns_DbHandle *Ns_DbPoolGetHandle(char *pool){    return Ns_DbPoolTimedGetHandle(pool, 0);}/* *---------------------------------------------------------------------- * * Ns_DbPoolGetMultipleHandles -- * *	Return 1 or more handles from a pool. * * Results: *	NS_OK if handles were allocated, NS_ERROR otherwise. * * Side effects: *	Given array of handles is updated with pointers to allocated *	handles.  Also, database may be opened if needed. * *---------------------------------------------------------------------- */intNs_DbPoolGetMultipleHandles(Ns_DbHandle **handles, char *pool, int nwant){    return Ns_DbPoolTimedGetMultipleHandles(handles, pool, nwant, 0);}/* *---------------------------------------------------------------------- * * Ns_DbPoolTimedGetMultipleHandles -- * *	Return 1 or more handles from a pool within the given number *	of seconds. * * Results: *	NS_OK if the handlers where allocated, NS_TIMEOUT if the *	thread could not wait long enough for the handles, NS_ERROR *	otherwise. * * Side effects: *	Given array of handles is updated with pointers to allocated *	handles.  Also, database may be opened if needed. * *---------------------------------------------------------------------- */intNs_DbPoolTimedGetMultipleHandles(Ns_DbHandle **handles, char *pool,     				 int nwant, int wait){    Handle    *handlePtr;    Handle   **handlesPtrPtr = (Handle **) handles;    Pool      *poolPtr;    Ns_Time    timeout, *timePtr;    int        i, ngot, status;    /*     * Verify the pool, the number of available handles in the pool,     * and that the calling thread does not already own handles from     * this pool.     */         poolPtr = GetPool(pool);    if (poolPtr == NULL) {	Ns_Log(Error, "dbinit: no such pool '%s'", pool);	return NS_ERROR;    }    if (poolPtr->nhandles < nwant) {	Ns_Log(Error, "dbinit: "	       "failed to get %d handles from a db pool of only %d handles: '%s'",	       nwant, poolPtr->nhandles, pool);	return NS_ERROR;    }    ngot = IncrCount(poolPtr, nwant);    if (ngot > 0) {	Ns_Log(Error, "dbinit: db handle limit exceeded: "	       "thread already owns %d handle%s from pool '%s'",	       ngot, ngot == 1 ? "" : "s", pool);	IncrCount(poolPtr, -nwant);	return NS_ERROR;    }        /*     * Wait until this thread can be the exclusive thread aquireing     * handles and then wait until all requested handles are available,     * watching for timeout in either of these waits.     */         if (wait <= 0) {	timePtr = NULL;    } else {    	Ns_GetTime(&timeout);    	Ns_IncrTime(&timeout, wait, 0);	timePtr = &timeout;    }    status = NS_OK;    Ns_MutexLock(&poolPtr->lock);    while (status == NS_OK && poolPtr->waiting) {	status = Ns_CondTimedWait(&poolPtr->waitCond, &poolPtr->lock, timePtr);    }    if (status == NS_OK) {    	poolPtr->waiting = 1;    	while (status == NS_OK && ngot < nwant) {	    while (status == NS_OK && poolPtr->firstPtr == NULL) {	    	status = Ns_CondTimedWait(&poolPtr->getCond, &poolPtr->lock,					  timePtr);	    }	    if (poolPtr->firstPtr != NULL) {		handlePtr = poolPtr->firstPtr;		poolPtr->firstPtr = handlePtr->nextPtr;		handlePtr->nextPtr = NULL;		if (poolPtr->lastPtr == handlePtr) {		    poolPtr->lastPtr = NULL;		}		handlesPtrPtr[ngot++] = handlePtr;	    }	}	poolPtr->waiting = 0;    	Ns_CondSignal(&poolPtr->waitCond);    }    Ns_MutexUnlock(&poolPtr->lock);    /*     * Handle special race condition where the final requested handle     * arrived just as the condition wait was timing out.     */    if (status == NS_TIMEOUT && ngot == nwant) {	status = NS_OK;    }    /*     * If status is still ok, connect any handles not already connected,     * otherwise return any allocated handles back to the pool, then     * update the final number of handles owned by this thread.     */    for (i = 0; status == NS_OK && i < ngot; ++i) {	handlePtr = handlesPtrPtr[i];	if (handlePtr->connected == NS_FALSE) {	    status = Connect(handlePtr);	}    }    if (status != NS_OK) {	Ns_MutexLock(&poolPtr->lock);	while (ngot > 0) {	    ReturnHandle(handlesPtrPtr[--ngot]);	}	if (poolPtr->waiting) {	    Ns_CondSignal(&poolPtr->getCond);	}	Ns_MutexUnlock(&poolPtr->lock);	IncrCount(poolPtr, -nwant);    }    return status;}/* *---------------------------------------------------------------------- * * Ns_DbBouncePool -- * *	Close all handles in the pool. * * Results: *	NS_OK if pool was bounce, NS_ERROR otherwise. * * Side effects: *	Handles are all marked stale and then closed by CheckPool. * *---------------------------------------------------------------------- */intNs_DbBouncePool(char *pool){    Pool	*poolPtr;    Handle	*handlePtr;        poolPtr = GetPool(pool);    if (poolPtr == NULL) {	return NS_ERROR;    }    Ns_MutexLock(&poolPtr->lock);    poolPtr->stale_on_close++;    handlePtr = poolPtr->firstPtr;    while (handlePtr != NULL) {	if (handlePtr->connected) {	    handlePtr->stale = 1;	}	handlePtr->stale_on_close = poolPtr->stale_on_close;	handlePtr = handlePtr->nextPtr;    }    Ns_MutexUnlock(&poolPtr->lock);    CheckPool(poolPtr);    return NS_OK;}/* *---------------------------------------------------------------------- * * NsDbInitPools -- * *	Initialize the database pools at startup. * * Results: *	None. * * Side effects: *	Pools may be created as configured. * *---------------------------------------------------------------------- */voidNsDbInitPools(void){    Tcl_HashEntry  *hPtr;    Pool           *poolPtr;    Ns_Set         *pools;    char           *path, *pool, *driver;    int		    new, i;    Ns_TlsAlloc(&tls, FreeTable);    /*     * Attempt to create each database pool.     */    Tcl_InitHashTable(&serversTable, TCL_STRING_KEYS);    Tcl_InitHashTable(&poolsTable, TCL_STRING_KEYS);    pools = Ns_ConfigGetSection("ns/db/pools");    for (i = 0; pools != NULL && i < Ns_SetSize(pools); ++i) {	pool = Ns_SetKey(pools, i);	hPtr = Tcl_CreateHashEntry(&poolsTable, pool, &new);	if (!new) {	    Ns_Log(Error, "dbinit: duplicate pool: %s", pool);	    continue;		}	path = Ns_ConfigGetPath(NULL, NULL, "db", "pool", pool, NULL);	driver = Ns_ConfigGetValue(path, "driver");	poolPtr = CreatePool(pool, path, driver);	if (poolPtr == NULL) {	    Tcl_DeleteHashEntry(hPtr);	} else {	    Tcl_SetHashValue(hPtr, poolPtr);	}    }    Ns_RegisterProcInfo(CheckPool, "nsdb:check", CheckArgProc);}/* *---------------------------------------------------------------------- * * NsDbInitServer -- * *	Initialize a virtual server allowed and default options. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */voidNsDbInitServer(char *server){    Pool	   *poolPtr;    ServData	   *sdataPtr;    Tcl_HashEntry  *hPtr;    Tcl_HashSearch  search;    char           *path, *pool, *p;    Ns_DString	    ds;    int		    new;    path = Ns_ConfigGetPath(server, NULL, "db", NULL);    /*     * Verify the default pool exists, if any.     */    sdataPtr = ns_malloc(sizeof(ServData));    hPtr = Tcl_CreateHashEntry(&serversTable, server, &new);    Tcl_SetHashValue(hPtr, sdataPtr);    sdataPtr->defpool = Ns_ConfigGetValue(path, "defaultpool");    if (sdataPtr->defpool != NULL &&	(Tcl_FindHashEntry(&poolsTable, sdataPtr->defpool) == NULL)) {	Ns_Log(Error, "dbinit: no such default pool '%s'", sdataPtr->defpool);	sdataPtr->defpool = NULL;    }    /*     * Construct the allowed list and call the server-specific init.     */    sdataPtr->allowed = "";    pool = Ns_ConfigGetValue(path, "pools");    if (pool != NULL && poolsTable.numEntries > 0) {	Ns_DStringInit(&ds);    	if (STREQ(pool, "*")) {	    hPtr = Tcl_FirstHashEntry(&poolsTable, &search);	    while (hPtr != NULL) {	    	poolPtr = Tcl_GetHashValue(hPtr);	    	NsDbDriverInit(server, poolPtr->driverPtr);	    	Ns_DStringAppendArg(&ds, poolPtr->name);		hPtr = Tcl_NextHashEntry(&search);	    }	} else {	    p = pool;	    while (p != NULL && *p != '\0') {		p = strchr(pool, ',');		if (p != NULL) {		    *p = '\0';		}		hPtr = Tcl_FindHashEntry(&poolsTable, pool);		if (hPtr != NULL) {		    poolPtr = Tcl_GetHashValue(hPtr);	    	    NsDbDriverInit(server, poolPtr->driverPtr);	    	    Ns_DStringAppendArg(&ds, poolPtr->name);		}		if (p != NULL) {		    *p++ = ',';		}		pool = p;	    }	}    	sdataPtr->allowed = ns_malloc((size_t)(ds.length + 1));    	memcpy(sdataPtr->allowed, ds.string, (size_t)(ds.length + 1));    	Ns_DStringFree(&ds);    }}/* *---------------------------------------------------------------------- * * NsDbDisconnect -- * *	Disconnect a handle by closing the database if needed. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */voidNsDbDisconnect(Ns_DbHandle *handle){    Handle *handlePtr = (Handle *) handle;    NsDbClose(handle);    handlePtr->connected = NS_FALSE;    handlePtr->atime = handlePtr->otime = 0;    handlePtr->stale = NS_FALSE;}/* *---------------------------------------------------------------------- * * NsDbLogSql -- * *	Log a SQL statement depending on the verbose state of the *	handle. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */voidNsDbLogSql(Ns_DbHandle *handle, char *sql){    Handle *handlePtr = (Handle *) handle;    if (handle->dsExceptionMsg.length > 0) {        if (handlePtr->poolPtr->fVerboseError || handle->verbose) {	                Ns_Log(Error, "dbinit: error(%s,%s): '%s'",		   handle->datasource, handle->dsExceptionMsg.string, sql);        }    } else if (handle->verbose) {        Ns_Log(Notice, "dbinit: sql(%s): '%s'", handle->datasource, sql);    }}/* *---------------------------------------------------------------------- * * NsDbGetDriver -- * *	Return a pointer to the driver structure for a handle. * * Results: *	Pointer to driver or NULL on error. * * Side effects: *	None. * *---------------------------------------------------------------------- */struct DbDriver *NsDbGetDriver(Ns_DbHandle *handle){    Handle *handlePtr = (Handle *) handle;    if (handlePtr != NULL && handlePtr->poolPtr != NULL) {	return handlePtr->poolPtr->driverPtr;    }    return NULL;}/* *---------------------------------------------------------------------- * * GetPool -- * *	Return the Pool structure for the given pool name. * * Results: *	Pointer to Pool structure or NULL if pool does not exist. * * Side effects: *	None. * *---------------------------------------------------------------------- */static Pool *GetPool(char *pool){    Tcl_HashEntry   *hPtr;    hPtr = Tcl_FindHashEntry(&poolsTable, pool);    if (hPtr == NULL) {	return NULL;    }    return (Pool *) Tcl_GetHashValue(hPtr);}/* *---------------------------------------------------------------------- * * ReturnHandle -- * *	Return a handle to its pool.  Connected handles are pushed on *	the front of the list, disconnected handles are appened to *	the end. * * Results: *	None. * * Side effects: *	Handle is returned to the pool.  Note:  The pool lock must be *	held by the caller and this function does not signal a thread *	waiting for handles. * *---------------------------------------------------------------------- */static voidReturnHandle(Handle *handlePtr){    Pool         *poolPtr;    poolPtr = handlePtr->poolPtr;    if (poolPtr->firstPtr == NULL) {	poolPtr->firstPtr = poolPtr->lastPtr = handlePtr;    	handlePtr->nextPtr = NULL;    } else if (handlePtr->connected) {	handlePtr->nextPtr = poolPtr->firstPtr;	poolPtr->firstPtr = handlePtr;    } else {	poolPtr->lastPtr->nextPtr = handlePtr;	poolPtr->lastPtr = handlePtr;    	handlePtr->nextPtr = NULL;    }}/* *---------------------------------------------------------------------- * * IsStale -- * *	Check to see if a handle is stale. * * Results: *	NS_TRUE if handle stale, NS_FALSE otherwise. * * Side effects: *	None. * *---------------------------------------------------------------------- */static intIsStale(Handle *handlePtr, time_t now){    time_t    minAccess, minOpen;        if (handlePtr->connected) {	minAccess = now - handlePtr->poolPtr->maxidle;	minOpen = now - handlePtr->poolPtr->maxopen;	if ((handlePtr->poolPtr->maxidle && handlePtr->atime < minAccess) || 	    (handlePtr->poolPtr->maxopen && (handlePtr->otime < minOpen)) ||	    (handlePtr->stale == NS_TRUE) ||	    (handlePtr->poolPtr->stale_on_close > handlePtr->stale_on_close)) {	    if (handlePtr->poolPtr->fVerbose) {		Ns_Log(Notice, "dbinit: closing %s handle in pool '%s'",		       handlePtr->atime < minAccess ? "idle" : "old",		       handlePtr->poolname);	    }	    return NS_TRUE;	}    }    return NS_FALSE;}/* *---------------------------------------------------------------------- * * CheckArgProc -- * *	Ns_ArgProc callback for the pool checker. * * Results: *	None. * * Side effects: *	Copies name of pool to given dstring. * *---------------------------------------------------------------------- */static voidCheckArgProc(Tcl_DString *dsPtr, void *arg){    Pool *poolPtr = arg;    Tcl_DStringAppendElement(dsPtr, poolPtr->name);}/* *---------------------------------------------------------------------- * * CheckPool -- * *	Verify all handles in a pool are not stale. * * Results: *	None. * * Side effects: *	Stale handles, if any, are closed. * *---------------------------------------------------------------------- */static voidCheckPool(void *arg){    Pool 	 *poolPtr = arg;    Handle       *handlePtr, *nextPtr;    Handle       *checkedPtr;    time_t	  now;    time(&now);    checkedPtr = NULL;    /*     * Grab the entire list of handles from the pool.     */    Ns_MutexLock(&poolPtr->lock);    handlePtr = poolPtr->firstPtr;    poolPtr->firstPtr = poolPtr->lastPtr = NULL;    Ns_MutexUnlock(&poolPtr->lock);    /*     * Run through the list of handles, closing any     * which have gone stale, and then return them     * all to the pool.     */    if (handlePtr != NULL) {    	while (handlePtr != NULL) {	    nextPtr = handlePtr->nextPtr;	    if (IsStale(handlePtr, now)) {                NsDbDisconnect((Ns_DbHandle *) handlePtr);	    }	    handlePtr->nextPtr = checkedPtr;	    checkedPtr = handlePtr;	    handlePtr = nextPtr;    	}	Ns_MutexLock(&poolPtr->lock);	handlePtr = checkedPtr;	while (handlePtr != NULL) {	    nextPtr = handlePtr->nextPtr;	    ReturnHandle(handlePtr);	    handlePtr = nextPtr;	}	if (poolPtr->waiting) {	    Ns_CondSignal(&poolPtr->getCond);	}	Ns_MutexUnlock(&poolPtr->lock);    }}/* *---------------------------------------------------------------------- * * CreatePool -- * *	Create a new pool using the given driver. * * Results: *	Pointer to newly allocated Pool structure. * * Side effects: *	None. * *---------------------------------------------------------------------- */static Pool  *CreatePool(char *pool, char *path, char *driver){    Pool            *poolPtr;    Handle          *handlePtr;    struct DbDriver *driverPtr;    int              i;    char	    *source;    if (driver == NULL) {	Ns_Log(Error, "dbinit: no driver for pool '%s'", pool);	return NULL;    }    driverPtr = NsDbLoadDriver(driver);    if (driverPtr == NULL) {	return NULL;    }    source = Ns_ConfigGetValue(path, "datasource");    if (source == NULL) {	Ns_Log(Error, "dbinit: missing datasource for pool '%s'", pool);	return NULL;    }    poolPtr = ns_malloc(sizeof(Pool));    poolPtr->driver = driver;    poolPtr->driverPtr = driverPtr;    Ns_MutexInit(&poolPtr->lock);    Ns_MutexSetName2(&poolPtr->lock, "nsdb", pool);    Ns_CondInit(&poolPtr->waitCond);    Ns_CondInit(&poolPtr->getCond);    poolPtr->source = source;    poolPtr->name = pool;    poolPtr->waiting = 0;    poolPtr->user = Ns_ConfigGetValue(path, "user");    poolPtr->pass = Ns_ConfigGetValue(path, "password");    poolPtr->desc = Ns_ConfigGetValue("ns/db/pools", pool);    poolPtr->stale_on_close = 0;    if (!Ns_ConfigGetBool(path, "verbose", &poolPtr->fVerbose)) {        poolPtr->fVerbose = 0;    }     if (!Ns_ConfigGetBool(path, "logsqlerrors", &poolPtr->fVerboseError)) {	poolPtr->fVerboseError = 0;    }    if (!Ns_ConfigGetInt(path, "connections", &poolPtr->nhandles)	|| poolPtr->nhandles <= 0) {        poolPtr->nhandles = 2;    }    if (Ns_ConfigGetInt(path, "MaxIdle", &i) == NS_FALSE || i < 0) {        i = 600;                    /* 10 minutes */    }    poolPtr->maxidle = i;    if (Ns_ConfigGetInt(path, "MaxOpen", &i) == NS_FALSE || i < 0) {        i = 3600;                   /* 1 hour */    }    poolPtr->maxopen = i;    poolPtr->firstPtr = poolPtr->lastPtr = NULL;    for (i = 0; i < poolPtr->nhandles; ++i) {    	handlePtr = ns_malloc(sizeof(Handle));    	Ns_DStringInit(&handlePtr->dsExceptionMsg);    	handlePtr->poolPtr = poolPtr;    	handlePtr->connection = NULL;    	handlePtr->connected = NS_FALSE;    	handlePtr->fetchingRows = 0;    	handlePtr->row = Ns_SetCreate(NULL);    	handlePtr->cExceptionCode[0] = '\0';    	handlePtr->otime = handlePtr->atime = 0;    	handlePtr->stale = NS_FALSE;    	handlePtr->stale_on_close = 0;	/*	 * The following elements of the Handle structure could	 * be obtained by dereferencing the poolPtr.  They're	 * only needed to maintain the original Ns_DbHandle	 * structure definition which was designed to allow	 * handles outside of pools, a feature no longer supported.	 */	handlePtr->driver = driver;	handlePtr->datasource = poolPtr->source;	handlePtr->user = poolPtr->user;	handlePtr->password = poolPtr->pass;	handlePtr->verbose = poolPtr->fVerbose;	handlePtr->poolname = pool;	ReturnHandle(handlePtr);    }    if (!Ns_ConfigGetInt(path, "checkinterval", &i) || i < 0) {	i = 600;	/* 10 minutes. */    }    Ns_ScheduleProc(CheckPool, poolPtr, 0, i);    return poolPtr;}/* *---------------------------------------------------------------------- * * Connect -- * *	Connect a handle by opening the database. * * Results: *	NS_OK if connect ok, NS_ERROR otherwise. * * Side effects: *	None. * *---------------------------------------------------------------------- */static intConnect(Handle *handlePtr){    int status;    status = NsDbOpen((Ns_DbHandle *) handlePtr);    if (status != NS_OK) {    	handlePtr->connected = NS_FALSE;    	handlePtr->atime = handlePtr->otime = 0;	handlePtr->stale = NS_FALSE;    } else {    	handlePtr->connected = NS_TRUE;    	handlePtr->atime = handlePtr->otime = time(NULL);    }    return status;}/* *---------------------------------------------------------------------- * * IncrCount -- * *	Update per-thread count of allocated handles. * * Results: *	Previous count of allocated handles. * * Side effects: *	None. * *---------------------------------------------------------------------- */static intIncrCount(Pool *poolPtr, int incr){    Tcl_HashTable *tablePtr;    Tcl_HashEntry *hPtr;    int prev, count, new;    tablePtr = Ns_TlsGet(&tls);    if (tablePtr == NULL) {	tablePtr = ns_malloc(sizeof(Tcl_HashTable));	Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS);	Ns_TlsSet(&tls, tablePtr);    }    hPtr = Tcl_CreateHashEntry(tablePtr, (char *) poolPtr, &new);    if (new) {	prev = 0;    } else {	prev = (int) Tcl_GetHashValue(hPtr);    }    count = prev + incr;    if (count == 0) {	Tcl_DeleteHashEntry(hPtr);    } else {	Tcl_SetHashValue(hPtr, (ClientData) count);    }    return prev;}/* *---------------------------------------------------------------------- * * GetServer -- * *	Get per-server data. * * Results: *	Pointer to per-server data. * * Side effects: *	None. * *---------------------------------------------------------------------- */static ServData *GetServer(char *server){    Tcl_HashEntry *hPtr;    hPtr = Tcl_FindHashEntry(&serversTable, server);    if (hPtr != NULL) {	return Tcl_GetHashValue(hPtr);    }    return NULL;}/* *---------------------------------------------------------------------- * * FreeTable -- * *	Free the per-thread count of allocated handles table. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */static voidFreeTable(void *arg){    Tcl_HashTable  *tablePtr = arg;    Tcl_DeleteHashTable(tablePtr);    ns_free(tablePtr);}