Trying to create a new thread in MainComponent

I’m trying to create a new thread to act as a basic clock for use within my app using the following code as the class:

class Clock : private juce::Thread {
public:

    Clock() : juce::Thread("Clockthread") {}

    void run() override
    {
        while (!threadShouldExit())
        {
            StartClock();
        }
    }

    int Tempo;

    void SetTempo(int tempo) {
        Tempo = tempo;
    }

    void StartClock() {
        sleep(100);
    }
};

This class is a member of another class and so on the MainContentComponent I am calling:
sequencer.Clock.StartClock();

The problem is that I am receiving this error:

Exception thrown at 0x00007FFAF844CB69 (KernelBase.dll) in Application.exe: 0x8001010D: An outgoing call cannot be made since the application is dispatching an input-synchronous call.
onecore\com\combase\dcomrem\giptbl.cxx(1786)\combase.dll!00007FFAFA3EADD7: (caller: 00007FFAFA4153D8) ReturnHr(1) tid(4f80) 8000FFFF Catastrophic failure

I’m new to JUCE and limited experience with C++ and it seems this particular error doesn’t have much documentation in terms of causes in C++/JUCE (and if it does exist doesn’t make much sense if you don’t understand low level programming)

Is your program actually crashing, or is that just a msg you are seeing in the debug window? if it is crashing, we would need to see the stack to start to consider what the problem might be.

I haven’t implemented any try/catches or anything, so the program seemingly continues. This was just in the output window, I’m a C# developer mainly so not sure on debugging and error handling in C++. and how to see the proper stack trace where that message stems from… I do have the “C++ Exceptions” and “Common Language Runtime Exceptions” ticked in VS22 though and not getting an exception window

I believe it is a system level message you can ignore. I don’t know the exact details, but it’s only showing because of the debug output window, and I’m 99% sure it means nothing to the success of your app running

Sure sounds intimidating. :slight_smile:

I might ask: if you want an accurate repeating callback at regular intervals, why not use a HighResolutionTimer?

The scenario I’m trying to create is that the Clock ‘service’ can throw an event every x (milli)seconds, which I can consume in the UI to update which was my reasoning for threading in the first place so the timer in itself is part of what I’m trying to achieve…

I was looking at: Event handling in native C++ | Microsoft Learn to try do this because I don’t need anything complex… with that context what would you suggest in addition to the HighResolutionTimer?

The scenario I’m trying to create is that the Clock ‘service’ can throw an event every x (milli)seconds, which I can consume in the UI to update

Why not just use the normal GUI Timer of Juce? You can’t directly do GUI updates from HighResolutionTimer or a new thread anyway.

Would this be classed as a non-blocking operation as not to affect the UI while the timer expires?

I was looking some documentation for the timer and found:

class MyComp : public Component : public Timer
{
public:
  MyComp()
  {
     startTimer(100)
  };

  void timerCallback() override
  { 
    repaint()
  };

  void paint(Graphics &g) override
  {
  // paint here
  };

};

When inheriting from the timer class, is the ‘startTimer’ a method in there and then the timerCallBack() overriden to provide your custom logic?

That’s pretty much all I need… every tick, fire a method to control the UI and a child object that is a property of the same component

The Timer calls the timerCallback() that you implement (to change some state, trigger a repaint() or do whatever. It depends on your code, if it is blocking. It is intended to do some quick things and return so the other UI stuff can happen.

So you found the best solution with your example above.

Doing in a separate thread creates many other problems and doesn’t seem necessary to do what you describe.

I have just given it a try and yeah it seems to do everything i’m wanting! I must admit, i just jumped straight into my creation as i find a learn on the fly is less abstract than reading documentation and more effective than simply copying a project… Thanks for guiding me in the right direction!

I’m not sure if you’re still looking to start another thread on the main component

main component h (members)

	std::atomic<bool> kill;
	std::atomic<bool> closed;
	std::thread thread;
    juce::TextButton open_close_button;

main component cpp

MainComponent::MainComponent()
{
	open_close_button.onClick = [this]()
	{
		open_close_button.setToggleState(!open_close_button.getToggleState(), false);

		if (open_close_button.getToggleState())
		{
			kill.exchange(false);

			thread = std::thread([this]()
				{
					myWhileLoop();
				});

			thread.detach();
		}

		else
		{
			kill.exchange(true);
		}
	};
...

MainComponent::~MainComponent()
{
	kill.exchange(true);

	while (!closed.load()) {}
...

I’m not questioning answers very often, but I don’t think this is a good solution at all – sorry for being so forward, nothing personal of course!

  • If you are working with the JUCE library, you should also use the Thread class from JUCE. You are using JUCE for a reason, and you should go all the way. Similar to not using std::string in your JUCE projects. You might have good reasons here and there, but since the OP already wrote he is just starting out, use what JUCE provides you and benefit from the additional convenience and support.
  • You are spin locking the dtor while waiting on the thread to stop. Without knowing what the thread is doing, a spin lock is probably not a good choice.

And then there are some general points I would like to raise even though they would work in the simplified example:

  • Handling with the atomics yourself this way can very easily cause a deadlock down the road. It works when you first write it (while you have everything figured out) but then your code grows and you might accidentally remove or alter a guarantee your code makes that is necessary for not causing a deadlock. This is very dangerous as those bugs are hard to debug because they can occur very sporadically at any time. You’ll also have a hard time putting those guarantees under automated tests – so even less protection against making accidental changes.
  • Passing this to the thread is fine in this case, because the dtor blocks before deconstructing the object. In almost every other case you need to protect yourself against bad memory access with a life time guard. For components, you can use juce::Component::SafePointer. This not only refers to threads, but passing this/references into lambdas for sporadic later execution in general. Also very easy to get yourself into trouble later down the road.
  • You can use Button::setClickingTogglesState instead of managing that yourself in the callback.

I didn’t put more thought than just launching a raylib window with this, so you’re most certainly right about this, and this is probably why I didn’t go “all juce” with it too. Thanks for your advices.

1 Like