2

I have a code that looks like this:

myCmd::myCmd(std::string outFile) : _tclInterp(Tcl_CreateInterp()), _outFile(outFile) 
{
   _myParser = CC::CmdParser(_tclInterp, registerAllCommands);
}

The thread which created the Tcl_CreateInterp() might be different from what is being used in the CC::CmdParser. Internally CC::CmdParser also calls Tcl_DeleteInterp() once all the procedures are complete. It will be a problem if the thread id is different during calls to CreateInterp and DeleteInterp.

How do I associate or tell such that same thread does Tcl_CreateInterp() (in the constructor) and Tcl_DeleteInterp() (in the CmdParser).

NOTE that the CC::CmdParser is a third-party API and I don't have source code.

vijaychsk
  • 39
  • 5
  • You're going to have to work on this. It sounds like you're trying to mesh together two very different threading models, and that's always painful. – Donal Fellows Aug 22 '21 at 11:49

1 Answers1

2

Tcl interpreter objects internally use thread-specific data extensively in order to avoid most global locks. This goes as far as including thread-specific memory management pools, at least for common allocations, and Tcl code is executed as part of initialising the interpreter state, so it's really not possible to use an interpreter in a different thread from the one in which it was created. The Tcl library checks that you're using it correctly in a few places, at least in some build modes, and will make the process abort() (or Windows's equivalent) if you disobey the rules; that's much friendlier than the crash that would otherwise be coming due to fundamental assumptions not being respected…

This means that you must create, use, and destroy the interpreter on the same thread.

You might be advised to initialise the Tcl interpreter lazily if you can't guarantee that the containing object is used in the same thread that it was created in. You might think that this results in handling of things that isn't really the C++ way, but the thread-bound nature of interpreters means that you've really not got any alternative. (You might want to look at Tcl_CreateExitHandler() for help with cleaning up the interpreter when the thread goes away, but that will only help if Tcl_FinalizeThread() or Tcl_ExitThread() are called; if you're being thread-promiscuous on the C++ side then there's a good chance that you'll be baking in all sorts of problems anyway.)

If the C++ part insists on using multiple threads, you'll have to have a single thread for the Tcl code and use inter-thread messaging to let the other threads tell it things. That's done with Tcl_ThreadQueueEvent(). The canonical way to use that is the thread package, and that includes the canonical example of how to do so (github.com/tcltk/thread/generic/threadCmd.c); most of the complexity in there relates to turning an essentially one-way (but “reliable”) operation into a two-way one that lets you do a procedure call into a different thread; one-way calls are far simpler.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • Yes, correct that is the reason I'm getting a crash as below: `#0 0x0000000001a2f833 in Tcl_AsyncDelete ()` `#1 0x0000000001901f57 in DeleteInterpProc ()` – vijaychsk Aug 22 '21 at 23:08
  • @DonalFellows I'm searching for a way to [validate an existing root window](https://stackoverflow.com/q/73961120/13629335) and thought the [thread specific data](https://github.com/python/cpython/blob/main/Modules/_tkinter.c#L242) may help with a second Tcl Interpreter `tk.Tcl()`. – Thingamabobs Oct 05 '22 at 21:05