texus / TGUI

Cross-platform modern c++ GUI

Home Page:https://tgui.eu

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Runtime errors when using ListBox with multiple threads

Miv99 opened this issue · comments

I'm running TGUI release 0.8.5 with SFML 2.5.1.
When I have two or more concurrent threads that instantiate RenderWindows and tgui Guis with tgui ListBox added to the gui, I sometimes get runtime errors that change at random without me changing the code (maybe something to do with memory?). There were also instances where the program did not crash, but the ListBox entries displayed corrupted data or nothing at all.
Here is the code:

#include <SFML/Graphics.hpp>
#include <TGUI/TGUI.hpp>
#include <thread>

void test() {
	sf::RenderWindow window(sf::VideoMode(800, 600), "TGUI window");
	window.setFramerateLimit(60);

	tgui::Gui gui(window);

	/*
	auto slider = tgui::Slider::create();
	slider->setPosition(10, 560);
	slider->setSize(200, 18);
	slider->setValue(4);
	gui.add(slider);
	*/

	auto listBox = tgui::ListBox::create();
	listBox->setSize(250, 120);
	listBox->setItemHeight(24);
	listBox->setPosition(10, 340);
	listBox->addItem("Item 1");
	listBox->addItem("Item 2");
	listBox->addItem("Item 3");
	gui.add(listBox);

	while (window.isOpen()) {
		sf::Event event;
		while (window.pollEvent(event)) {
			if (event.type == sf::Event::Closed)
				window.close();

			gui.handleEvent(event);
		}

		window.clear();
		gui.draw();
		window.display();
	}
}

int main() {
	sf::Thread a(&test);
	a.launch();
	sf::Thread b(&test);
	b.launch();

	std::system("pause");
	return 0;
}

The multithreading works fine when using tgui Sliders instead of ListBox but I haven't tested with any other widgets. Using std::thread instead of sf::Thread still causes errors. Creating only one thread works fine, but two or more causes errors.

Did some more testing and simplified the code down to this.

#include <SFML/Graphics.hpp>
#include <TGUI/TGUI.hpp>
#include <thread>
#include <iostream>

void test() {
	auto listBox = tgui::ListBox::create();
	listBox->setSize(250, 120);
	listBox->setItemHeight(24);
	listBox->setPosition(10, 340);
	listBox->addItem("Item 1");
	listBox->addItem("Item 2");
	listBox->addItem("Item 3");
}

int main() {
	sf::Thread a(&test);
	a.launch();
	sf::Thread b(&test);
	b.launch();

	std::system("pause");
	return 0;
}

Narrowed it down further to the error being related to the widget constructor.

#include <SFML/Graphics.hpp>
#include <TGUI/TGUI.hpp>
#include <thread>

bool ok = false;

void test() {
	auto listBox = tgui::ListBox::create();
	ok = true;
}

int main() {
	sf::Thread a(&test);
	a.launch();
	while (!ok) {
		
	}
	sf::Thread b(&test);
	b.launch();

	std::system("pause");
	return 0;
}

The error occurs when the first thread's ListBox constructor hasn't returned by the time the second thread starts. The above code works without errors, but removing the while loop brings back the errors. I think Slider only worked out of luck the few times I tested it, so you can ignore that part of the OP.

Unless a c++ library explicitly mentions they are thread-safe, they usually aren't. TGUI can't be used from multiple threads simultaneously. Although TGUI isn't restricted to the main thread like some other gui libraries, it is a recommended practice. If you do use TGUI from multiple threads then you have to ensure that there are never two pieces of TGUI code executing at the same time (by using locks). It is probably a better strategy to send the data you have in thread to the main thread and passing it to the gui there.

Ah, ok. I was multithreading with multiple RenderWindows and using locks solved some gui draw issues as well. Thanks for the help.