VST problem InternalTimerThread


#1

I’m having a slightly weird problem, though I’m not 100% sure Juce is to blame.

I’m using Windows, Juce 1.35, JuceAudioPlugin 1.10, MSVC 7.1 (2003) and Bidule 0.7003 as a host. When building the JuceAudioPlugin demo VST plug-in and launching Bidule from the MSVC debugger (Bidule scans for new plug-ins), the InternalTimerThreads do not seem to want to stop when they are destroyed by the DeleteAtShutdown singleton (which results in a time-out and an assertion failure). It seems to happen with both the InternalTimerThread from the VST wrapper which cleans up the memory for the “chunk buffer” and the InternalTimerThread created on setTimeBeforeWaitCursor() of the MessageManager.

It happens most of the time, but not always. When not debugging, the plug-in gets scanned and later works just fine. Once the plug-in has been scanned and marked as working, debugging works. Haven’t tested in other hosts, perhaps it’s a problem of Bidule. I tried looking myself why exactly the threads don’t stop, but couldn’t really find it.


#2

Have you tried putting a breakpoint in the InternalTimerThread’s destructor just before the stopThread() method? If you catch it there, you could see what the thread is doing at the time it’s asked to stop.


#3

Thanks for the reply.

If I stop before calling stopThread(), in cases when there’s no problem (other hosts, bidule occasionally) there’s a single “Juce Timer” thread which doesn’t seem to be doing anything out of the ordinary. However, in cases when there is a problem (bidule most of the time), there’s no “Juce Timer” thread and if I set a breakpoint at the beginning of threadEntryProc() it’s not reached. The handle returned from _beginthreadex() seems okay though…

Commenting out the assert before juce_killThread() or using another host seem to work around the issue sufficiently…


#4

Strange. I wonder why the thread died… You could try putting a breakpoint at the end of the Thread::threadEntryPoint method, so you can see when it ends. If it never reaches that point, then I guess it could be bidule killing the thread directly (somehow…)


#5

The beginning of Thread::threadEntryPoint() isn’t even reached before the call to stopThread().

It seems like the _beginthreadex() returns (a valid thread handle) before the thread’s entry point is called and the DLL is unloaded in between (or something). If I call ::Sleep(2000); just after the _beginthreadex() call, it works fine. I suppose a flag indicating if the entry point was reached which is checked on Thread::isThreadRunning() would also work.

I’ll ask the guys from Bidule if I see one of them.


#6

Maybe a plugin’s getting created and destroyed so quickly that the threads never start up… Though I doubt that. I’ll bear it in mind in case I can think of any other possibilities.


#7

I don’t know if this is any help, but whenever I’ve had to do this sort of thing, I’ve always found it best to detect the request for shutdown in the thread, clean-up and suspend in the thread,
and then sleep for a few ticks in the destructor (yuch, probably - bit gives the thread a look-in to bow-out gracefully), and then tidied up and destoyed the thread (having checked that it’s suspended first)

[code] (Delphi)

(thread)
begin

while not Terminated do
begin
… Thready stuff
end;
sleep(0); //Give distructer a chance to look in
suspend;
end

(… parent destructor)

 if (Assigned(Thread))
 then
 begin
      Thread.Terminate;
      While (not Thread.Suspended) do
      begin
            Sleep(1);
      end;
      Thread.Free;
      Thread := nil;
 end;

[/code]


#8

I’m having this problem too, did it ever get solved?


#9

I’ve still never seen it happen. Have you got any clues from debugging it at all? I’d be interested to know what happens in the InternalTimerThread constructor, where it tries to start the thread, if that’s where it’s failing.


#10

Remember, if you want to test a var in threads (like a shudown var), then make sure you make that var volitale so it will never be cached on the cpu cache. Although the lookup for it is far slower, it is only one lookup in an already multi-threaded environment. You will never run into problems with it not being volitale on a single core, but you will on dual or higher.


#11

Yes, all my code uses volatile variables for this, of course. But the problem here seems to be that the thread isn’t actually being started. I can’t think of any obvious reason why that’d happen, so any clues would be appreciated.

My only idea is that perhaps when bidule calls the plugin’s startup code, it has some kind of lock that blocks the win32 library from starting a new thread, and everything deadlocks because windows has told juce that the thread has started, but in fact it’s still sitting there waiting to run.


#12

My plugin right now is just an emtpy shell, it doesn’t actually do anything.

The problem occurs during the tracktion plugin scan.

I’ll look into it some more and post what I find.


#13

If i put a break point in the InternalTimerThread::run() function, it never gets hit. The thread is getting deleted before it every starts.

DLL_PROCESS_DETACH gets called before the thread has a chance to start running.


#14

Ok, I’ve just seen this happen in tracktion, and it seems that for some reason threads just don’t start correctly while a plugin scan is taking place, presumably because there’s no message loop active. As a fix, here are some changes to the start of juce_Timer.cpp which start the thread asynchronously…

[code]#include “juce_Timer.h”
#include “juce_MessageManager.h”
#include “juce_AsyncUpdater.h”
#include “…/application/juce_Application.h”
#include “…/application/juce_DeletedAtShutdown.h”
#include “…/…/juce_core/basics/juce_Time.h”
#include “…/…/juce_core/threads/juce_Thread.h”
#include “…/…/juce_core/threads/juce_ScopedLock.h”
#include “…/…/juce_core/containers/juce_VoidArray.h”

//==============================================================================
class InternalTimerThread : private Thread,
private MessageListener,
private DeletedAtShutdown,
private AsyncUpdater
{
private:
static InternalTimerThread* instance;
static CriticalSection lock;

Timer* firstTimer;
bool volatile callbackNeeded;

InternalTimerThread (const InternalTimerThread&);
const InternalTimerThread& operator= (const InternalTimerThread&);

void addTimer (Timer* const t) throw()
{

#ifdef JUCE_DEBUG
Timer* tt = firstTimer;

    while (tt != 0)
    {
        // trying to add a timer that's already here - shouldn't get to this point,
        // so if you get this assertion, let me know!
        jassert (tt != t);

        tt = tt->next;
    }

    jassert (t->previous == 0 && t->next == 0);

#endif

    Timer* i = firstTimer;

    if (i == 0 || i->countdownMs > t->countdownMs)
    {
        t->next = firstTimer;
        firstTimer = t;
    }
    else
    {
        while (i->next != 0 && i->next->countdownMs <= t->countdownMs)
            i = i->next;

        jassert (i != 0);

        t->next = i->next;
        t->previous = i;
        i->next = t;
    }

    if (t->next != 0)
        t->next->previous = t;

    jassert ((t->next == 0 || t->next->countdownMs >= t->countdownMs)
              && (t->previous == 0 || t->previous->countdownMs <= t->countdownMs));

    notify();
}

void removeTimer (Timer* const t) throw()
{

#ifdef JUCE_DEBUG
Timer* tt = firstTimer;
bool found = false;

    while (tt != 0)
    {
        if (tt == t)
        {
            found = true;
            break;
        }

        tt = tt->next;
    }

    // trying to remove a timer that's not here - shouldn't get to this point,
    // so if you get this assertion, let me know!
    jassert (found);

#endif

    if (t->previous != 0)
    {
        jassert (firstTimer != t);
        t->previous->next = t->next;
    }
    else
    {
        jassert (firstTimer == t);
        firstTimer = t->next;
    }

    if (t->next != 0)
        t->next->previous = t->previous;

    t->next = 0;
    t->previous = 0;
}

void decrementAllCounters (const int numMillisecs) const
{
    Timer* t = firstTimer;

    while (t != 0)
    {
        t->countdownMs -= numMillisecs;
        t = t->next;
    }
}

void handleAsyncUpdate()
{
    startThread (7);
}

public:
InternalTimerThread()
: Thread (T(“Juce Timer”)),
firstTimer (0),
callbackNeeded (false)
{
triggerAsyncUpdate();
}

[/code]


#15

This patch fixes the problems I’d been having with Bidule. Thanks.
Hope it makes it into a future Juce release…


#16