From: Stephen D. <sd...@us...> - 2005-07-30 03:11:17
|
Update of /cvsroot/naviserver/naviserver/nsd In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28549/nsd Modified Files: nsd.h nsmain.c tclinit.c Log Message: * nsd/nsd.h: * nsd/nsmain.c: * nsd/tclinit.c: Allocate interps efficiently, max 1 per-thread per-server. (RFE #1241351) Index: tclinit.c =================================================================== RCS file: /cvsroot/naviserver/naviserver/nsd/tclinit.c,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** tclinit.c 12 Jul 2005 07:31:39 -0000 1.9 --- tclinit.c 30 Jul 2005 03:11:07 -0000 1.10 *************** *** 80,89 **** */ ! static Tcl_Interp *CreateInterp(); ! static Tcl_InterpDeleteProc FreeInterpData; ! static int UpdateInterp(NsInterp *itPtr); ! static NsInterp *PopInterp(CONST char *server); static void PushInterp(NsInterp *itPtr); static Tcl_HashEntry *GetCacheEntry(NsServer *servPtr); static Ns_TlsCleanup DeleteInterps; static void RunTraces(NsInterp *itPtr, int why); --- 80,90 ---- */ ! static NsInterp *PopInterp(NsServer *servPtr, Tcl_Interp *interp); static void PushInterp(NsInterp *itPtr); static Tcl_HashEntry *GetCacheEntry(NsServer *servPtr); + static Tcl_Interp *CreateInterp(NsInterp **itPtrPtr); + static NsInterp *NewInterpData(Tcl_Interp *interp); + static int UpdateInterp(NsInterp *itPtr); + static Tcl_InterpDeleteProc FreeInterpData; static Ns_TlsCleanup DeleteInterps; static void RunTraces(NsInterp *itPtr, int why); *************** *** 169,178 **** Ns_TclCreateInterp(void) { ! Tcl_Interp *interp; ! ! interp = CreateInterp(); ! (void) NsInitInterp(interp, NULL, NULL); ! ! return interp; } --- 170,174 ---- Ns_TclCreateInterp(void) { ! return CreateInterp(NULL); } *************** *** 186,190 **** * * Results: ! * Tcl result. * * Side effects: --- 182,186 ---- * * Results: ! * Always TCL_OK. * * Side effects: *************** *** 197,201 **** Ns_TclInit(Tcl_Interp *interp) { ! return NsInitInterp(interp, NULL, NULL); } --- 193,198 ---- Ns_TclInit(Tcl_Interp *interp) { ! (void) NewInterpData(interp); ! return TCL_OK; } *************** *** 248,261 **** * Ns_TclAllocateInterp -- * ! * Allocate an interpreter from the per-thread list. Note that a ! * single thread can have multiple interps for multiple virtual ! * servers. * * Results: ! * Pointer to Tcl_Interp. * * Side effects: ! * See PopInterp for details on various traces which may be ! * called. * *---------------------------------------------------------------------- --- 245,256 ---- * Ns_TclAllocateInterp -- * ! * Return a pre-initialized interp for the given server or create ! * a new one and cache it for the current thread. * * Results: ! * Pointer to Tcl_Interp or NULL if invalid server. * * Side effects: ! * May invoke alloc and create traces. * *---------------------------------------------------------------------- *************** *** 265,272 **** Ns_TclAllocateInterp(CONST char *server) { NsInterp *itPtr; ! itPtr = PopInterp(server); ! return (itPtr ? itPtr->interp : NULL); } --- 260,282 ---- Ns_TclAllocateInterp(CONST char *server) { + NsServer *servPtr; NsInterp *itPtr; ! /* ! * Verify the server. NULL (i.e., no server) is valid but ! * a non-null, unknown server is an error. ! */ ! ! if (server == NULL) { ! servPtr = NULL; ! } else { ! servPtr = NsGetServer(server); ! if (servPtr == NULL) { ! return NULL; ! } ! } ! itPtr = PopInterp(servPtr, NULL); ! ! return itPtr->interp; } *************** *** 278,285 **** * * Return an interp to the per-thread cache. If the interp is ! * associated with a connection, silently do nothing as cleanup ! * will occur later with connection cleanup. If the interp is ! * only a Tcl interp, i.e., missing the NsInterp structure, ! * simply delete the interp directly (this is suspect). * * Results: --- 288,293 ---- * * Return an interp to the per-thread cache. If the interp is ! * associated with a connection, simply adjust the refcnt as ! * cleanup will occur later when the connection closes. * * Results: *************** *** 299,305 **** --- 307,316 ---- itPtr = NsGetInterpData(interp); if (itPtr == NULL) { + Ns_Log(Bug, "Ns_TclDeAllocateInterp: no interp data"); Tcl_DeleteInterp(interp); } else if (itPtr->conn == NULL) { PushInterp(itPtr); + } else { + itPtr->refcnt--; } } *************** *** 332,336 **** if (connPtr->itPtr == NULL) { ! itPtr = PopInterp(connPtr->server); itPtr->conn = conn; itPtr->nsconn.flags = 0; --- 343,347 ---- if (connPtr->itPtr == NULL) { ! itPtr = PopInterp(connPtr->servPtr, NULL); itPtr->conn = conn; itPtr->nsconn.flags = 0; *************** *** 409,420 **** Ns_TclDestroyInterp(Tcl_Interp *interp) { ! NsInterp *itPtr = NsGetInterpData(interp); /* ! * If this is a server interp, invoke the delete traces. */ ! if (itPtr != NULL) { RunTraces(itPtr, NS_TCL_TRACE_DELETE); } --- 420,435 ---- Ns_TclDestroyInterp(Tcl_Interp *interp) { ! NsInterp *itPtr = NsGetInterpData(interp); ! Tcl_HashEntry *hPtr; /* ! * If this is a server interp, invoke the delete traces ! * and remove from thread cache. */ ! if (itPtr != NULL && itPtr->servPtr != NULL) { RunTraces(itPtr, NS_TCL_TRACE_DELETE); + hPtr = GetCacheEntry(itPtr->servPtr); + Tcl_SetHashValue(hPtr, NULL); } *************** *** 1039,1042 **** --- 1054,1058 ---- } itPtr->delete = 1; + return TCL_OK; } *************** *** 1177,1184 **** *---------------------------------------------------------------------- * ! * NsInitInterp -- * ! * Initialize the given interp with basic commands and if servPtr ! * is not null, virtual server commands. * * Results: --- 1193,1200 ---- *---------------------------------------------------------------------- * ! * NsTclAppInit -- * ! * Initialize an interactive command interp with basic and ! * server commands using the default virtual server. * * Results: *************** *** 1186,1191 **** * * Side effects: ! * Depends on Tcl init script sourced by Tcl_Init. Some Tcl ! * object types will be initialized on first call. * *---------------------------------------------------------------------- --- 1202,1206 ---- * * Side effects: ! * Depends on Tcl init script sourced by Tcl_Init. * *---------------------------------------------------------------------- *************** *** 1193,1267 **** int ! NsInitInterp(Tcl_Interp *interp, NsServer *servPtr, NsInterp **itPtrPtr) { ! static volatile int initialized = 0; ! NsInterp *itPtr; ! int result = TCL_OK; /* ! * Core one-time server initialization to add a few Tcl_Obj ! * types. These calls cannot be in NsTclInit above because ! * Tcl is not fully initialized at libnsd load time. */ ! if (!initialized) { ! Ns_MasterLock(); ! if (!initialized) { ! NsTclInitQueueType(); ! NsTclInitAddrType(); ! NsTclInitTimeType(); ! NsTclInitSpecType(); ! NsTclInitKeylistType(); ! initialized = 1; ! } ! Ns_MasterUnlock(); } ! /* ! * Allocate and initialize a new NsInterp struct. ! */ ! ! itPtr = NsGetInterpData(interp); ! if (itPtr == NULL) { ! itPtr = ns_calloc(1, sizeof(NsInterp)); ! itPtr->interp = interp; ! Tcl_InitHashTable(&itPtr->sets, TCL_STRING_KEYS); ! Tcl_InitHashTable(&itPtr->chans, TCL_STRING_KEYS); ! Tcl_InitHashTable(&itPtr->https, TCL_STRING_KEYS); ! ! /* ! * Associate the new NsInterp with this interp. At interp delete ! * time, Tcl will call FreeInterpData to cleanup the struct. ! */ ! ! Tcl_SetAssocData(interp, "ns:data", FreeInterpData, itPtr); ! ! /* ! * Add basic commands which function without a virtual server. ! */ ! ! NsTclAddBasicCmds(itPtr); ! ! /* ! * Add virtual-server commands and update the interp ! * state, run create traces. ! */ ! ! if (servPtr != NULL) { ! itPtr->servPtr = servPtr; ! NsTclAddServerCmds(itPtr); ! RunTraces(itPtr, NS_TCL_TRACE_CREATE); ! if ((result = UpdateInterp(itPtr)) != TCL_OK) { ! Ns_TclLogError(interp); ! } ! } else { ! RunTraces(itPtr, NS_TCL_TRACE_CREATE); ! } } ! if (itPtrPtr != NULL) { ! *itPtrPtr = itPtr; } ! return result; } --- 1208,1245 ---- int ! NsTclAppInit(Tcl_Interp *interp) { ! NsServer *servPtr; ! NsInterp *itPtr; ! Tcl_HashEntry *hPtr; /* ! * There can only be one interp per-server per-thread, and it ! * needs to be the one Tcl just handed us. Clear out any ! * previously cached interp. */ ! servPtr = NsGetServer(nsconf.defaultServer); ! if (servPtr == NULL) { ! Ns_Log(Bug, "NsTclAppInit: invalid default server: %s", ! nsconf.defaultServer); ! return TCL_ERROR; } ! hPtr = GetCacheEntry(servPtr); ! itPtr = Tcl_GetHashValue(hPtr); ! if (itPtr != NULL) { ! Ns_TclDestroyInterp(itPtr->interp); ! Tcl_SetHashValue(hPtr, NULL); } ! ! if (Tcl_Init(interp) != TCL_OK) { ! Ns_TclLogError(interp); } + Tcl_SetVar(interp, "tcl_rcFileName", "~/.nsdrc", TCL_GLOBAL_ONLY); ! itPtr = PopInterp(servPtr, interp); ! ! return TCL_OK; } *************** *** 1354,1366 **** * PopInterp -- * ! * Pop next avaialble virtual-server interp from the per-thread ! * cache, allocating a new interp if necessary. * * Results: ! * Pointer to next available NsInterp. * * Side effects: ! * Will invoke alloc traces and, if the interp is new, create ! * traces. * *---------------------------------------------------------------------- --- 1332,1345 ---- * PopInterp -- * ! * Get virtual-server interp from the per-thread cache and ! * increment the reference count. Allocate a new interp if ! * necessary. * * Results: ! * NsInterp. * * Side effects: ! * Will invoke alloc traces if not recursively allocated and, if ! * the interp is new, create traces. * *---------------------------------------------------------------------- *************** *** 1368,1414 **** static NsInterp * ! PopInterp(CONST char *server) { - NsServer *servPtr; NsInterp *itPtr; - Tcl_Interp *interp; Tcl_HashEntry *hPtr; static Ns_Cs lock; /* ! * Verify the server. NULL (i.e., no server) is valid but ! * a non-null, unknown server is an error. ! */ ! ! if (server == NULL) { ! servPtr = NULL; ! } else { ! servPtr = NsGetServer(server); ! if (servPtr == NULL) { ! return NULL; ! } ! } ! ! /* ! * Pop the first interp off the list of availabe interps for ! * the given virtual server on this thread. If none exists, ! * create and initialize a new interp. */ hPtr = GetCacheEntry(servPtr); itPtr = Tcl_GetHashValue(hPtr); ! if (itPtr != NULL) { ! Tcl_SetHashValue(hPtr, itPtr->nextPtr); ! } else { if (nsconf.tcl.lockoninit) { Ns_CsEnter(&lock); } ! interp = CreateInterp(); ! NsInitInterp(interp, servPtr, &itPtr); if (nsconf.tcl.lockoninit) { Ns_CsLeave(&lock); } } - itPtr->nextPtr = NULL; /* --- 1347,1388 ---- static NsInterp * ! PopInterp(NsServer *servPtr, Tcl_Interp *interp) { NsInterp *itPtr; Tcl_HashEntry *hPtr; static Ns_Cs lock; /* ! * Get an already initialized interp for the given virtual server ! * on this thread. If it doesn't yet exist, create and ! * initialize one. */ hPtr = GetCacheEntry(servPtr); itPtr = Tcl_GetHashValue(hPtr); ! if (itPtr == NULL) { if (nsconf.tcl.lockoninit) { Ns_CsEnter(&lock); } ! if (interp != NULL) { ! itPtr = NewInterpData(interp); ! } else { ! interp = CreateInterp(&itPtr); ! } ! if (servPtr != NULL) { ! itPtr->servPtr = servPtr; ! NsTclAddServerCmds(itPtr); ! RunTraces(itPtr, NS_TCL_TRACE_CREATE); ! if (UpdateInterp(itPtr) != TCL_OK) { ! Ns_TclLogError(interp); ! } ! } else { ! RunTraces(itPtr, NS_TCL_TRACE_CREATE); ! } if (nsconf.tcl.lockoninit) { Ns_CsLeave(&lock); } + Tcl_SetHashValue(hPtr, itPtr); } /* *************** *** 1417,1424 **** */ ! RunTraces(itPtr, NS_TCL_TRACE_ALLOCATE); ! if (Tcl_EvalEx(itPtr->interp, "ns_init", -1, 0) != TCL_OK) { ! Ns_TclLogError(itPtr->interp); } return itPtr; } --- 1391,1401 ---- */ ! if (++itPtr->refcnt == 1) { ! RunTraces(itPtr, NS_TCL_TRACE_ALLOCATE); ! if (Tcl_EvalEx(itPtr->interp, "ns_init", -1, 0) != TCL_OK) { ! Ns_TclLogError(itPtr->interp); ! } } + return itPtr; } *************** *** 1430,1434 **** * PushInterp -- * ! * Return a virtual-server interp to the per-thread interp list. * * Results: --- 1407,1411 ---- * PushInterp -- * ! * Return a virtual-server interp to the thread cache. * * Results: *************** *** 1436,1440 **** * * Side effects: ! * Will invoke de-alloc traces, interp may be destroyed. * *---------------------------------------------------------------------- --- 1413,1418 ---- * * Side effects: ! * May invoke de-alloc traces, destroy interp if no longer ! * being used. * *---------------------------------------------------------------------- *************** *** 1445,1449 **** { Tcl_Interp *interp = itPtr->interp; - Tcl_HashEntry *hPtr; /* --- 1423,1426 ---- *************** *** 1453,1468 **** */ ! RunTraces(itPtr, NS_TCL_TRACE_DEALLOCATE); ! if (Tcl_EvalEx(interp, "ns_cleanup", -1, 0) != TCL_OK) { ! Ns_TclLogError(interp); ! } ! if (itPtr->delete) { ! Ns_TclDestroyInterp(interp); ! } else { ! Tcl_ResetResult(interp); ! hPtr = GetCacheEntry(itPtr->servPtr); ! itPtr->nextPtr = Tcl_GetHashValue(hPtr); ! Tcl_SetHashValue(hPtr, itPtr); } } --- 1430,1445 ---- */ ! if (itPtr->refcnt == 1) { ! RunTraces(itPtr, NS_TCL_TRACE_DEALLOCATE); ! if (Tcl_EvalEx(interp, "ns_cleanup", -1, 0) != TCL_OK) { ! Ns_TclLogError(interp); ! } ! if (itPtr->delete) { ! Ns_TclDestroyInterp(interp); ! return; ! } } + Tcl_ResetResult(interp); + itPtr->refcnt--; } *************** *** 1520,1523 **** --- 1497,1501 ---- CreateInterp(NsInterp **itPtrPtr) { + NsInterp *itPtr; Tcl_Interp *interp; *************** *** 1532,1535 **** --- 1510,1522 ---- } + /* + * Allocate and associate a new NsInterp struct for the interp. + */ + + itPtr = NewInterpData(interp); + if (itPtrPtr != NULL) { + *itPtrPtr = itPtr; + } + return interp; } *************** *** 1539,1566 **** *---------------------------------------------------------------------- * ! * FreeInterpData -- * ! * Tcl assoc data callback to destroy the per-interp NsInterp ! * structure at interp delete time. * * Results: ! * None. * * Side effects: ! * None. * *---------------------------------------------------------------------- */ ! static void ! FreeInterpData(ClientData arg, Tcl_Interp *interp) { ! NsInterp *itPtr = arg; ! NsFreeAdp(itPtr); ! Tcl_DeleteHashTable(&itPtr->sets); ! Tcl_DeleteHashTable(&itPtr->chans); ! Tcl_DeleteHashTable(&itPtr->https); ! ns_free(itPtr); } --- 1526,1596 ---- *---------------------------------------------------------------------- * ! * NewInterpData -- * ! * Create a new NsInterp struct for the given interp, adding ! * basic commands and associating it with the interp. * * Results: ! * Pointer to new NsInterp struct. * * Side effects: ! * Depends on Tcl init script sourced by Tcl_Init. Some Tcl ! * object types will be initialized on first call. * *---------------------------------------------------------------------- */ ! static NsInterp * ! NewInterpData(Tcl_Interp *interp) { ! static volatile int initialized = 0; ! NsInterp *itPtr; ! /* ! * Core one-time server initialization to add a few Tcl_Obj ! * types. These calls cannot be in NsTclInit above because ! * Tcl is not fully initialized at libnsd load time. ! */ ! ! if (!initialized) { ! Ns_MasterLock(); ! if (!initialized) { ! NsTclInitQueueType(); ! NsTclInitAddrType(); ! NsTclInitTimeType(); ! NsTclInitKeylistType(); ! initialized = 1; ! } ! Ns_MasterUnlock(); ! } ! ! /* ! * Allocate and initialize a new NsInterp struct. ! */ ! ! itPtr = NsGetInterpData(interp); ! if (itPtr == NULL) { ! itPtr = ns_calloc(1, sizeof(NsInterp)); ! itPtr->interp = interp; ! itPtr->servPtr = NULL; ! Tcl_InitHashTable(&itPtr->sets, TCL_STRING_KEYS); ! Tcl_InitHashTable(&itPtr->chans, TCL_STRING_KEYS); ! Tcl_InitHashTable(&itPtr->https, TCL_STRING_KEYS); ! ! /* ! * Associate the new NsInterp with this interp. At interp delete ! * time, Tcl will call FreeInterpData to cleanup the struct. ! */ ! ! Tcl_SetAssocData(interp, "ns:data", FreeInterpData, itPtr); ! ! /* ! * Add basic commands which function without a virtual server. ! */ ! ! NsTclAddBasicCmds(itPtr); ! } ! ! return itPtr; } *************** *** 1610,1616 **** *---------------------------------------------------------------------- * * DeleteInterps -- * ! * Delete all per-thread interps at thread exit time. * * Results: --- 1640,1676 ---- *---------------------------------------------------------------------- * + * FreeInterpData -- + * + * Tcl assoc data callback to destroy the per-interp NsInterp + * structure at interp delete time. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + + static void + FreeInterpData(ClientData arg, Tcl_Interp *interp) + { + NsInterp *itPtr = arg; + + NsFreeAdp(itPtr); + Tcl_DeleteHashTable(&itPtr->sets); + Tcl_DeleteHashTable(&itPtr->chans); + Tcl_DeleteHashTable(&itPtr->https); + ns_free(itPtr); + } + + + /* + *---------------------------------------------------------------------- + * * DeleteInterps -- * ! * Delete all cached virtual-server interps at thread exit time. * * Results: *************** *** 1633,1638 **** hPtr = Tcl_FirstHashEntry(tablePtr, &search); while (hPtr != NULL) { ! while ((itPtr = Tcl_GetHashValue(hPtr)) != NULL) { ! Tcl_SetHashValue(hPtr, itPtr->nextPtr); Ns_TclDestroyInterp(itPtr->interp); } --- 1693,1697 ---- hPtr = Tcl_FirstHashEntry(tablePtr, &search); while (hPtr != NULL) { ! if ((itPtr = Tcl_GetHashValue(hPtr)) != NULL) { Ns_TclDestroyInterp(itPtr->interp); } Index: nsd.h =================================================================== RCS file: /cvsroot/naviserver/naviserver/nsd/nsd.h,v retrieving revision 1.26 retrieving revision 1.27 diff -C2 -d -r1.26 -r1.27 *** nsd.h 22 Jul 2005 06:46:40 -0000 1.26 --- nsd.h 30 Jul 2005 03:11:06 -0000 1.27 *************** *** 170,173 **** --- 170,174 ---- Tcl_HashTable servertable; Tcl_DString servers; + char *defaultServer; /* *************** *** 747,756 **** typedef struct NsInterp { - struct NsInterp *nextPtr; Tcl_Interp *interp; ! NsServer *servPtr; ! int delete; ! int epoch; ! /* * The following pointer maintains the first in --- 748,757 ---- typedef struct NsInterp { Tcl_Interp *interp; ! NsServer *servPtr; ! int delete; /* Interp should be deleted on next deallocation */ ! int epoch; /* Run the update script if != to server epoch */ ! int refcnt; /* Counts recursive allocations of cached interp */ ! /* * The following pointer maintains the first in *************** *** 864,873 **** extern NsServer *NsGetServer(CONST char *server); extern NsServer *NsGetInitServer(void); - extern NsInterp *NsGetInterpData(Tcl_Interp *interp) NS_GNUC_NONNULL(1); - extern int NsInitInterp(Tcl_Interp *interp, NsServer *servPtr, - NsInterp **itPtrPtr) - NS_GNUC_NONNULL(1); - extern void NsFreeConnInterp(Conn *connPtr) - NS_GNUC_NONNULL(1); extern Ns_Cache *NsFastpathCache(char *server, int size); --- 865,868 ---- *************** *** 952,957 **** --- 947,958 ---- extern void NsWaitJobsShutdown(Ns_Time *toPtr); + extern Tcl_AppInitProc NsTclAppInit; extern void NsTclInitServer(CONST char *server) NS_GNUC_NONNULL(1); + extern NsInterp *NsGetInterpData(Tcl_Interp *interp) + NS_GNUC_NONNULL(1); + extern void NsFreeConnInterp(Conn *connPtr) + NS_GNUC_NONNULL(1); + extern void NsLoadModules(CONST char *server); extern struct Bucket *NsTclCreateBuckets(char *server, int nbuckets); Index: nsmain.c =================================================================== RCS file: /cvsroot/naviserver/naviserver/nsd/nsmain.c,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** nsmain.c 12 Jul 2005 07:31:39 -0000 1.16 --- nsmain.c 30 Jul 2005 03:11:06 -0000 1.17 *************** *** 63,68 **** */ - static Tcl_AppInitProc CommandInit; - static int StartWatchedServer(void); static void SysLog(int priority, char *fmt, ...); --- 63,66 ---- *************** *** 83,87 **** */ - static char *cmdServer; /* Command mode interp server */ static int watchdogExit = 0; /* Watchdog loop toggle */ --- 81,84 ---- *************** *** 695,699 **** } } ! cmdServer = server; /* --- 692,696 ---- } } ! nsconf.defaultServer = server; /* *************** *** 749,753 **** cmdargv[cmdargc++] = argv[i]; } ! Tcl_Main(cmdargc, cmdargv, CommandInit); } --- 746,750 ---- cmdargv[cmdargc++] = argv[i]; } ! Tcl_Main(cmdargc, cmdargv, NsTclAppInit); } *************** *** 940,972 **** *---------------------------------------------------------------------- * - * CommandInit -- - * - * Initialize the command interp with basic and server commands. - * - * Results: - * Tcl result. - * - * Side effects: - * Depends on init scripts. - * - *---------------------------------------------------------------------- - */ - - static int - CommandInit(Tcl_Interp *interp) - { - NsServer *servPtr = NsGetServer(cmdServer); - - Tcl_InitMemory(interp); - if (Tcl_Init(interp) != TCL_OK) { - Ns_TclLogError(interp); - } - return NsInitInterp(interp, servPtr, NULL); - } - - - /* - *---------------------------------------------------------------------- - * * StatusMsg -- * --- 937,940 ---- |