|
|
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;
}