0

My C++ applicaton embeds TCL (8.6.7) and needs to get data via Windows named pipes (dll) and TCP socket (TCL script) at the same time. I want to use TCL threads to achieve the simultaneous data acquisition via the pipe and socket. One thread (threadPipe) via TCL C api would manage data reading from the named pipe and another thread via TCL script (threadTclSocket) would manage the socket. The main thread (the C++ application) via TCL script initiates the data acquisition and it waits (non-blocking) for the worker threads using vwait forever in its TCL script.

My problem:

  • How to do I get the TCL C thread (threadPipe) and the thread via TCL script (threadTclSocket) to both set the variable: forever of the main application when they are done so that the main thread can exit its event loop?
  • I have read about Tcl_ThreadQueueEvent and Tcl_ThreadAlert, but I don't understand how they could be used to set variable forever in the main thread.

Any suggestions would be appreciated.

5
  • You could use the twapi extension to use named pipes in Tcl. Commented Nov 30, 2017 at 11:53
  • @JohannesKuhn thanks, the named pipes is already implemented as dll. Commented Nov 30, 2017 at 12:00
  • The usual idiom is something like while {$waitcount < 2} {vwait waitcount} and increasing the variable waitcount when the other threads are finished. Commented Nov 30, 2017 at 13:36
  • Thanks @JohannesKuhn. My current difficulty is how do I get the TCL C thread call to set the variable waitcount and return control to the main TCL interpreter. It seems to hang after set I the variable in the TCL_EventProc. Commented Nov 30, 2017 at 13:44
  • After each thread is done, it should use Tcl_ThreadQueueEvent to queue an event in the main thread that uses Tcl_Eval with incr ::waitcount. Commented Nov 30, 2017 at 14:36

2 Answers 2

2

You should probably use the twapi named pipe implementation. I suspect your named pipe reading code is not presenting this as a channel that supports the tcl fileevent asynchronous API. If you had such support, as is found in the twapi version, then you don't need the additional thread at all. You simpley use fileevent and have some tcl procedure called when data arrives on the channel.

If you persist with the existing code then you use Tcl_ThreadQueueEvent to post an event to the tcl notifier so that the interpreter thread will then call a function when it processes that event. This shifts control to the interpreter thread. Here is an example from something I wrote previously:

static void MailslotSignalledProc(void *clientData)
{
    MailslotData *slotPtr = (MailslotData *)clientData;
    MailslotEvent *evPtr = NULL;
    DWORD cbRead, cbSize = 0, cQueued = 0;
    BOOL br = FALSE;

    Tcl_GetLongFromObj(NULL, slotPtr->sizeObj, &cbSize);
    GetOverlappedResult(slotPtr->handle, &slotPtr->ov, &cbRead,  FALSE);
    do
    {
        evPtr = (MailslotEvent *)ckalloc(sizeof(MailslotEvent));
        evPtr->header.proc = EventProc;
        evPtr->header.nextPtr = NULL;
        evPtr->interp = slotPtr->pkgPtr->interp;
        Tcl_Preserve(evPtr->interp);
        evPtr->slotPtr = slotPtr;
        evPtr->messageObj = Tcl_NewByteArrayObj(slotPtr->message, cbRead);
        Tcl_IncrRefCount(evPtr->messageObj);
        Tcl_ThreadQueueEvent(slotPtr->wait.tid, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);

        /*
         * Schedule another read on the mailslot. This may return
         * immediately if data is already available. We also rate-limit
         * by enforcing a wait after 64 immediate messages.
         */
        br = ReadFile(slotPtr->handle, slotPtr->message, cbSize, &cbRead, &slotPtr->ov);
        ++cQueued;
    } while (br && cQueued < 64);

    Tcl_ThreadAlert(slotPtr->wait.tid);
    return;
}

Of note is we have a structure that 'inherits' from Tcl_Event and allows us to put our own data in the event. We can post a number of events and use Tcl_ThreadAlert to wake up the target once we are done. The target function (EventProc in this case) will be passed this structure but will be running on the interpreter thread, so we have to mind the Tcl_Obj reference counting.

In your case it sounds like you may just wish to set the forever variable in your event procecdure.

If its useful you can see the whole file.

However, are you sure you shouldn't just use fileevent on a properly written named pipe channel?

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your answer. Your suspicion that the named pipes provides no event is spot on, but in the circumstance I have little choice that to work with it. I have implemented your suggestion of Tcl_Event and Tcl_EventProc which seems to work because the Tcl_EventProc is called. But it seems the program is stuck in Tcl_EventProc and control is not returned to the main TCL interpreter.
You can't usually send a Tcl_Obj between threads at all; their default allocator is strongly thread-coupled.
0

Thanks for all your suggestions, I have solved my problem.

Setting the forever variable of vwait forever with the TCL script is quite easy, one only needs to use forever as the result of thread::send and let the main TCL thread wait on it. For the TCL C thread setting forever immediately in the TCL_EventProc hangs the main TCL interpreter. To solve this I delayed the setting of forever until the event loop is idle using after like so:

after idle {set forever thread}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.