The following example adds critical section enter
and leave
commands to Tcl. This allows you to protect shared resources (i.e., an external file or remote connection) accessed in a Tcl script.
This example can be found in the examples/c/tclcs
directory.
#include "ns.h" #include "nstcl.h" /* * This code implements simple critical section primitives as TCL commands * within the AOLserver. To use this module, add the following to the * [ns\server\server-name\modules] section of your nsd.ini file: * cs=tclcs.dll ; or tclcs.so on Unix platforms * * Within your TCL, these commands should be used as follows: * ... * cs_enter * set err [catch {some tcl}] * cs_leave * if $err { * error $err * } * ... * Note that any error should be caught to avoid leaving the * critical section in the locked state after the script exits. * * Also, because the init function uses the module name to construct * the command names, you can load the module multiple times if you * need more than one critical section. For example, if you needed 2 * critical sections, your nsd.ini would have: * * [ns\server\server-name\modules] * cs1=tclcs.dll * cs2=tclcs.dll * * and then, in your Tcl script, you would access the two critical * sections using their unique names: * * ... * cs1_enter * ... access resource protected by cs 1 ... * cs2_enter * ... access resource 2, leaving resource 1 locked ... * cs2_leave * cs1_leave * */ int Ns_ModuleVersion = 1; static int InitCs(Tcl_Interp *interp, void *ctx); static Tcl_CmdProc EnterCs, LeaveCs; /* * This structure is used to pass the critical section * and enter and leave command names to InitCs function * through the Ns_TclInitInterps function. */ typedef struct { char *enter; char *leave; Ns_CriticalSection *cs; } CsCtx; /* * Ns_ModuleInit - The function is called each time the * tclcs module is loaded into a server. It * constructs the names of the enter and leave Tcl commands * from the module name and then calls Ns_TclInitInterps * to add the command to each interpreter of the * server. */ int Ns_ModuleInit(char *hServer, char *hModule) { Ns_DString dsEnter; Ns_DString dsLeave; CsCtx ctx; int status; Ns_DStringInit(&dsEnter); Ns_DStringAppend(&dsEnter, hModule); Ns_DStringAppend(&dsEnter, "_enter"); Ns_DStringInit(&dsLeave); Ns_DStringAppend(&dsLeave, hModule); Ns_DStringAppend(&dsLeave, "_leave"); ctx.enter = dsEnter.string; ctx.leave = dsLeave.string; ctx.cs = ns_malloc(sizeof(Ns_CriticalSection)); Ns_InitializeCriticalSection(ctx.cs); status = Ns_TclInitInterps(hServer, InitCs, (void *) &ctx); Ns_DStringFree(&dsEnter); Ns_DStringFree(&dsLeave); return status; } /* * InitCs - Initialize a single Tcl interpreter with the * critical section enter and leave commands. */ static int InitCs(Tcl_Interp *interp, void *ctx) { CsCtx *csctx; csctx = ctx; Tcl_CreateCommand(interp, csctx->enter, EnterCs, (ClientData) csctx->cs, NULL); Tcl_CreateCommand(interp, csctx->leave, LeaveCs, (ClientData) csctx->cs, NULL); return TCL_OK; } /* * EnterCs - Enter the critical section passed in as callback data. */ static int EnterCs(ClientData clientData, Tcl_Interp *interp, int argc, char **argv) { Ns_CriticalSection *cs = (Ns_CriticalSection *) clientData; Ns_EnterCriticalSection(cs); return TCL_OK; } /* * LeaveCs - Leave the critical section passed in as callback data. */ static int LeaveCs(ClientData clientData, Tcl_Interp *interp, int argc, char **argv) { Ns_CriticalSection *cs = (Ns_CriticalSection *) clientData; Ns_LeaveCriticalSection(cs); return TCL_OK; }