Old 03-19-2017, 02:26 PM   #1
SpiderFox
Human being with feelings
 
Join Date: Jan 2017
Posts: 29
Default JUCE GUI hangs when executing std::future

I want to fetch data for a GUI.

I do this by calling std::future<std::vector<Project>> f = std::async(std::launch::async, &ProjectsController::getProjects, &pc) in the component which is intended to show the data.

Code:
ProjectBrowserComponent::ProjectBrowserComponent() : m_btn("OK") {
	addAndMakeVisible(m_tabs);
	setSize(500, 600);
	initData();
}

void ProjectBrowserComponent::initData() {
	std::cout << "requesting projs";
	std::future<std::vector<Project>> f = std::async(std::launch::async, &ProjectsController::getProjects, &pc);
	std::cout << "requested projs";
	std::vector<Project> projs;
	if (isReady(f)) {
		projs = f.get();
		std::cout << "got projs";
	}
}

template<typename R>
bool isReady(std::future<R> const& f)
{
	return f.wait_for(std::chrono::seconds(-1)) == std::future_status::ready;
}
The data is provided by a method called ProjectsController::getProjects() which lets it's thread sleep to simulate a remote service call which takes some time.

Code:
std::vector<Project> ProjectsController::getProjects() {
	std::vector<Project> result;
	if(serviceClient != nullptr) {
		try
		{
			std::cout << "controller requesting projs\n";
			std::this_thread::sleep_for(std::chrono::seconds(5));
			std::cout << "controller requested projs\n";
			result = serviceClient->getAvailableProjects();
		}
		catch (const std::exception&)
		{

		}
	}
	
	return result;
}
However, the UI does only apper after the thread sleep time has elapsed.

How can I make it load in the background?

If you want to have a look at it you can setup the project as follows:

Source code https://github.com/CodingSpiderFox/ReaperDAWHub

Install the conan package manager as python package
Code:
pip install conan
Install the dependencies

Code:
conan install --build Boost --build zlib --build Catch --build rapidjson -s arch=x86 -s build_type=Release -s compiler="Visual Studio" -s compiler.runtime=MD -s compiler.version=14
The project can be built with Visual Studio 2015 in the Release|Win32 configuration. (For lazyness, the project is built to C:\Program Files (x86)\REAPER\Plugins) so make sure to start VS 2015 as administrator or change the output directories

Last edited by SpiderFox; 03-19-2017 at 02:37 PM.
SpiderFox is offline   Reply With Quote
Old 03-19-2017, 04:04 PM   #2
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by SpiderFox View Post

std::future<std::vector<Project>> f = std::async(std::launch::async, &ProjectsController::getProjects, &pc);

How can I make it load in the background?
You have to store the returned future from std::async somewhere else than a local variable. The destructor of the future variable (or I think in your case running the isReady function) will block the execution of the current thread, negating the use of the async operation if the future is a local variable in a function. So make it a class member variable or a global variable instead. You will of course have to do the ready checking somewhere else too, taking care you don't block the GUI thread from returning to the event loop as soon as possible. (Maybe you could use a Juce timer that periodically checks if the future is ready or not, but also there you have to use do the check very quickly and not wait for the future.)

This problem is universal to GUI frameworks, not specific to JUCE. You should not do any operations that block the execution of the GUI thread for a long time.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 03-19-2017, 04:13 PM   #3
SpiderFox
Human being with feelings
 
Join Date: Jan 2017
Posts: 29
Default

Quote:
Originally Posted by Xenakios View Post
The destructor of the future variable (or I think in your case running the isReady function) will block the execution of the current thread, negating the use of the async operation if the future is a local variable in a function.
I guess that's the case because local variables are deleted at the end of the function?
SpiderFox is offline   Reply With Quote
Old 03-19-2017, 04:25 PM   #4
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by SpiderFox View Post
I guess that's the case because local variables are deleted at the end of the function?
Right.

There's a better way to do execution of background tasks, I thought I had done a utility function for that but I can't find it at the moment...

Anyway the idea is something like this :

Code:
void runAsync(std::function<void(void)> task, std::function<void(void)> completionHandler)
{
  std::thread th([task,completionHandler]()
  {
    task();
    MessageManager::callAsync(completionHandler);
  });
  th.detach(); 
  // the thread object being destroyed here doesn't matter, detach() has persisted the thread 
}

void MyComponent::test()
{
  auto task = [](){ Sleep(4000); };
  auto completion = [this](){ m_statuslabel.setText("Task finished!") };
  m_statuslabel.setText("Running task...");
  runAsync(task,completion);
}
That wasn't tested that it compiles and runs correctly, since I typed it here directly. The neat part in that code is that it uses the Juce MessageManager::callAsync function to do some task completion handling in the GUI thread. (One must not update/repaint etc the GUI from other threads.)
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.

Last edited by Xenakios; 03-19-2017 at 04:36 PM.
Xenakios is offline   Reply With Quote
Old 03-19-2017, 05:05 PM   #5
SpiderFox
Human being with feelings
 
Join Date: Jan 2017
Posts: 29
Default

Your first post helped me a lot, it works now.

Will look at the second later
SpiderFox is offline   Reply With Quote
Old 03-23-2017, 04:06 PM   #6
SpiderFox
Human being with feelings
 
Join Date: Jan 2017
Posts: 29
Default

I messed it up again ... I wish I were better in C++ ... I mess it up all the time and it really drives me crazy ... could you please have a look why it hangs again?

What I wanted to achieve is to use the Model View Controller pattern and the Strategy pattern for the code which should fetch the data asynchronously.

The code went into RepeatedPollingProjectsStrategy.cpp which is used in ProjectEntryListController which then is finally used in the GUI.

I tried to initialize the variable in the protected section
The broken version is https://github.com/CodingSpiderFox/R...29486bb8ccd48c

Could you please help me?
SpiderFox is offline   Reply With Quote
Old 03-23-2017, 04:19 PM   #7
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by SpiderFox View Post
I messed it up again ... I wish I were better in C++ ... I mess it up all the time and it really drives me crazy ... could you please have a look why it hangs again?
Sorry, your project is so big to take a look at, that at the moment I can't give better advice than to be really careful that you don't do anything like sleeping, waiting, incorrect mutex locking, long/infinite loops and such in the GUI thread. The GUI thread must constantly return back to the GUI event loop as quickly as possible. (I didn't extensively look at the code yet, but it looks like your problem is about waiting for the std::future in the GUI thread.)

Another thing to take care of is to not use the JUCE GUI or Reaper API(*) functions from anything else but the GUI thread. They may appear to work at times when called from other threads but probably just lead to hangs and crashes or worse problems.

(*) Some of the Reaper API functions may be thread safe, but I believe most are not.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.

Last edited by Xenakios; 03-23-2017 at 04:59 PM.
Xenakios is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 05:00 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.