Timmy
Timmy

Reputation: 65

Tcl_LinkVar and Tcl_UpdateLinkedVar aren't updating my TCL variable

I am relativity new to C++ and very new to TCL. I am trying to write a pair of application (one in C++ and one in TCL) that share a variable, named tValue.

The goal is that the C++ program asks for user input and assigns that input to tValue. The TCL program should then have it's tValue updated.

I've been searching and searching but can't find anything else to try.

EDIT: Originally the tcl script was executed in the the "main" C++ thread, but the C++ thread waited for the tcl script to finish. Shifting the c++ code that calls the tcl script into its own thread did allow for concurrent execution of the two threads (is this the only way of achieving this?), but tValue still wasn't updating

These are the source code for the two programs:

test.cpp

#include <tcl.h>
#include <iostream>

using namespace std;

Tcl_Interp * interp = Tcl_CreateInterp();
char * tValue = Tcl_Alloc(20);

void *thread_proc(void* x);

int main() {

    cout << "C++: INIT: " << Tcl_Init(interp) << "\n";

    cout << "C++: I'm alive, I'm alive, I'M ALIVE!!!\n";

    pthread_t t1;
    int res = pthread_create(&t1,NULL,thread_proc,NULL);

    int errLink = Tcl_LinkVar (interp, tValue, (char *) &tValue, TCL_LINK_STRING);

    cout << "C++: INTERP " << errLink << "\n";

    usleep(5000);

    cout << "C++: Well...\nC++: Has anything happened?\n";

    while (true) {
        cout << "C++: Enter a string\n";
        cin >> tValue;
        cout << "C++: You have entered " << tValue << "\n";
        Tcl_UpdateLinkedVar(interp, tValue);
    }

    return 1;
}

void *thread_proc(void* x) {
    cout << "C++ TP: Thread Launched: " << "\n";

    int errEval = Tcl_EvalFile(interp,"./test.tcl");

    cout << "C++ TP: EVAL: " << errEval << "\n";

    pthread_exit(NULL);
}

and test.tcl

#!/home/gc/tcl/bin/tclsh8.6

set tValue ""

puts "I'm also alive"

after 10000

puts "about to puts"

after 1000

puts $tValue

after 10000

puts "about to end"

after 1000

puts "Ended"

any help would be greatly appreciated, I'm just banging away at the keyboard trying to make it work... and failing...

Upvotes: 0

Views: 246

Answers (1)

JoGusto
JoGusto

Reputation: 973

The correct way to do this is first, to realize that Tcl has a weakness when it comes to multi-threaded applications, or, should I say, a restriction: it uses the "Apartment threading" model. This means that everything done in a thread must remain "apart" from any other thread.

Specifically, the thread which creates an interpreter is the only one which can interact with that interpreter. This includes uses of all the Tcl_xxx C API functions. The ONLY exception, and this is important, is with the Tcl_AsyncXXX API functions.

To properly use the linked variables ACROSS THREADS, you would not call Tcl_UpdateLinkedVar from the C-based thread running separately from your Tcl thread. Instead, that thread should initialize an Async event token and handler using Tcl_AsyncCreate() etc, and use the related functions to invoke the async handler in the Tcl thread, at which point the handler can safely call Tcl_UpdateLinkedVar(). Because the async handler will always execute in the thread which creates the async event token which you will use in your call to Tcl_AsyncMark(), it will work properly, an the apartment-ness of the threading model Tcl requires will be preserved.

I wish I had more time to give a fuller answer, but I think this will get you going on a productive track, if you think about: updating the var in one thread, in C, is "Asynchronous" to the Tcl interpreter, and so you must use the Async mechanisms to "hand off" the updating of the Tcl var to the thread running the Tcl interp you are using.

Good luck!

PS: I'm looking at the Active State Tcl 8.5.15.0 for Windows help documentation as I write this. The documentation is very good and mostly cross platform-applicable; I recommend it as at least one of your constant companion references.

Upvotes: 1

Related Questions