/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is AOLserver Code and related documentation * distributed by AOL. * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. */ /* * dbinit.c -- * * This file contains routines for creating and accessing * pools of database handles. */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsdb/dbinit.c,v 1.5 2004/02/05 17:50:53 dossy Exp $, compiled: " __DATE__ " " __TIME__; #include "db.h" /* * The following structure defines a database pool. */ struct Handle; typedef struct Pool { char *name; char *desc; char *source; char *user; char *pass; int type; Ns_Mutex lock; Ns_Cond waitCond; Ns_Cond getCond; char *driver; struct DbDriver *driverPtr; int waiting; int nhandles; struct Handle *firstPtr; struct Handle *lastPtr; int fVerbose; int fVerboseError; time_t maxidle; time_t maxopen; int stale_on_close; } Pool; /* * The following structure defines the internal * state of a database handle. */ typedef struct Handle { char *driver; char *datasource; char *user; char *password; void *connection; char *poolname; int connected; int verbose; Ns_Set *row; char cExceptionCode[6]; Ns_DString dsExceptionMsg; void *context; void *statement; int fetchingRows; /* Members above must match Ns_DbHandle */ struct Handle *nextPtr; struct Pool *poolPtr; time_t otime; time_t atime; int stale; int stale_on_close; } Handle; /* * The following structure maintains per-server data. */ typedef struct ServData { char *defpool; char *allowed; } ServData; /* * Local functions defined in this file */ static Pool *GetPool(char *pool); static void ReturnHandle(Handle * handle); static int IsStale(Handle *, time_t now); static int Connect(Handle *); static Pool *CreatePool(char *pool, char *path, char *driver); static int IncrCount(Pool *poolPtr, int incr); static ServData *GetServer(char *server); 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. * *---------------------------------------------------------------------- */ int Ns_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. * *---------------------------------------------------------------------- */ void Ns_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. * *---------------------------------------------------------------------- */ int Ns_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. * *---------------------------------------------------------------------- */ int Ns_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. * *---------------------------------------------------------------------- */ int Ns_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. * *---------------------------------------------------------------------- */ void NsDbInitPools(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. * *---------------------------------------------------------------------- */ void NsDbInitServer(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. * *---------------------------------------------------------------------- */ void NsDbDisconnect(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. * *---------------------------------------------------------------------- */ void NsDbLogSql(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 void ReturnHandle(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 int IsStale(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 void CheckArgProc(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 void CheckPool(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 int Connect(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 int IncrCount(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_ONE_WORD_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 void FreeTable(void *arg) { Tcl_HashTable *tablePtr = arg; Tcl_DeleteHashTable(tablePtr); ns_free(tablePtr); }