multithreadingc++builderc++builder-2010rad-studio

TThread::Queue crashes C++ Builder


I am using C++ Builder 2010. I have a TThread::Synchronize call which works perfectly fine - a thread function calls Synchronize and that goes well. However, if I replace it with TThread::Queue it crashes immediately.

Is there a particular bug in that version of C++ Builder or something else going on?

This is the function which I use to call main thread function:

void RunInMainThread(void(__closure *FuncToCall)(const __int64, const wchar_t), const __int64 fP1, const wchar_t fP2)
{
struct
    {
    private: typedef void(__closure *FTCdef)(const __int64, const wchar_t);

    public: __int64 P1;
            wchar_t P2;
            FTCdef  FTC;

            void __fastcall ExecFunc()
                {
                FTC(P1,P2);
                }
    } Args = { fP1, fP2, FuncToCall };

TThread::Synchronize(NULL, &Args.ExecFunc);
//TThread::Queue    (NULL, &Args.ExecFunc);
}

The function it calls is really very simple it just updates a toolbar with some text maybe 2-3 lines of code.


Solution

  • TThread::Queue() runs asynchronously. The method you pass to it is put in an internal queue, and then TThread::Queue() exits immediately. The main UI thread runs the queued method at its earliest convenience, usually long after the queuing thread has moved on to do other things.

    The method you are passing to TThread::Queue() belongs to a variable that is local to RunInMainThread(), it goes out of scope and is no longer valid by the time the queued method is called. That is why your code crashes.

    You don't have that problem with TThread::Synchronize() because it runs synchronously, it does not exit until after the synced method has been called. So, it is OK to use a method that belongs to a local variable, the variable won't go out of scope until after the synced method exits.

    To fix your use of TThread::Queue(), you need to allocate the variable dynamically and let the main UI thread free it after the queued method is done, eg:

    typedef void (__closure *FTCdef)(const __int64, const wchar_t);
    
    void RunInMainThread(FTCdef FuncToCall, const __int64 fP1, const wchar_t fP2)
    {
        struct Args
        {
            __int64 P1;
            wchar_t P2;
            FTCdef FTC;
    
            /*
            void __fastcall ExecFuncSync()
            {
                FTC(P1, P2);
            }
            */
    
            void __fastcall ExecFuncQueue()
            {
                try {
                    FTC(P1, P2);
                } __finally {
                    delete this;
                }
            }
        };
    
        //Args args{fP1, fP2, FuncToCall};
        //TThread::Synchronize(NULL, &args.ExecFuncSync);
    
        Args *args = new Args;
        args->P1 = fP1;
        args->P2 = fP2;
        args->FTC = FuncToCall;
        TThread::Queue(NULL, &args->ExecFuncQueue);
    }