Re: vwait doesn't return after setting TCL_SetVar from a thread



zivh@xxxxxxxxx wrote:
//the thread code
DWORD WINAPI TestProc(ClientData CD)
{
Tcl_Interp * pInterp;

pInterp = (Tcl_Interp* ) CD;
....
Tcl_SetVar(pInterp,"testEvent","changed succesfully",TCL_GLOBAL_ONLY);

Let me see if I understand that code bit correctly. You are passing the
interpreter across to a thread and modifying it's state within a thread that it
wasn't created in? Is that correct?

http://www.tcl.tk/doc/howto/thread_model.html

Tcl uses what's called apartment model threading. That means *YOU CAN NOT* call
any Tcl API function that takes a Tcl_Interp* from any thread other than the one
that created the interp.

If you do want such behavior you need to use Tcl_AsyncMark and Tcl_QueueEvent.
IOW, from your worker thread, make a call to Tcl_AsyncMark (this is really the
only thread-safe API that exists in Tcl when the core hasn't been compiled for
thread support, BTW) at the opportune moment using the token you created back in
the parent thread. That might be the item you want to pass.

Within the Tcl_AsyncProc (that will execute in the parent thread at the next
command boundary), call Tcl_QueueEvent from it to insert a new event.

From the Tcl_EventProc, only there can you select the interp and call Tcl_SetVar.
Please *DO NOT* call Tcl_SetVar from the Tcl_AsyncProc as the interp could be in
an unsafe state. If you need to synchronize the worker thread to when that action
takes place, use Tcl_ConditionWait.

Here's a code sample:

/*
* Structs and data types
*/
typedef struct {
Tcl_Interp *interp;
Tcl_Obj *script;
} DataReadyInfo;

typedef struct {
Tcl_Event header;
DataReadyInfo *info;
} DataReadyEvent;


/*
* File scope globals
*/
Tcl_AsyncHandler dataReadyToken = NULL;
DataReadyInfo dataReadyInfo;

/*
* File scope protos
*/
TDataReady DataReady;
Tcl_AsyncProc DataReadyYield;
Tcl_EventProc DataReadyEval;

void
DataReady (void)
{
ADC_Abort();
Tcl_AsyncMark(dataReadyToken);
}

int
DataReadyYield (ClientData clientData, Tcl_Interp *interp, int code)
{
DataReadyEvent *evPtr;
DataReadyInfo *info = (DataReadyInfo *) clientData;

/* Not safe to eval in here, so don't do it! */

evPtr = (DataReadyEvent *) ckalloc(sizeof(DataReadyEvent));
evPtr->header.proc = DataReadyEval;
evPtr->info = info;
Tcl_QueueEvent((Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
return code;
}

int
DataReadyEval (Tcl_Event *evPtr, int flags)
{
Tcl_Interp *interp = ((DataReadyEvent *)evPtr)->info->interp;
Tcl_Obj *script = ((DataReadyEvent *)evPtr)->info->script;

if (!(flags & TCL_FILE_EVENTS)) {
/* deferred, just file events, please */
return 0;
}

/* eval in proper interp. */
if (!Tcl_InterpDeleted(interp)) {
int result;

Tcl_Preserve(interp);
/* Locked */

Tcl_ResetResult(interp);
result = Tcl_EvalObj(interp, script);

if (result == TCL_ERROR) {
char buff[60];
snprintf(buff, 60, "\nError in tpscope's DataReady script \"%s\"",
Tcl_GetStringFromObj(script, 0L));
Tcl_AddObjErrorInfo(interp, buff, -1);
Tcl_BackgroundError(interp);
}

Tcl_Release(interp);
/* Unlocked */
}

return 1;
}

int
SetDataReadyCallbackCmd (ClientData clientData, Tcl_Interp *interp,
int objc, struct Tcl_Obj * CONST * objv)
{
word ok;

if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "script");
return TCL_ERROR;
}

if (dataReadyToken != NULL) {
Tcl_DecrRefCount(dataReadyInfo.script);
Tcl_AsyncDelete(dataReadyToken);
}
dataReadyInfo.interp = interp;
Tcl_IncrRefCount(dataReadyInfo.script = objv[1]);
dataReadyToken = Tcl_AsyncCreate(DataReadyYield, &dataReadyInfo);

ok = tiepie::SetDataReadyCallback(&DataReady);
if (TiePieResultOK(interp, ok)) {
return TCL_OK;
}
return TCL_ERROR;
}

--
You can present the material, but you can't make me care.
-- Calvin

Attachment: signature.asc
Description: OpenPGP digital signature



Relevant Pages