I am attempting to write a gtkmm application that uses C++11 multithreading. However, i keep running into Fatal IO error 11 (Resource temporarily unavailable) on X server
error.
I have several Gtk::Image
objects on my window. Each one is in its own Gtk::EventBox
and periodically i have to change the image. To do that i have created a class that holds the event box for the specific block, and it has a function that removes the previous image, generates the new one and places it there.
Here's a block of code:
while (lock_flag.test_and_set()) {} // std::atomic_flag lock
// ...
std::cerr << 1;
eventBox->foreach(
[eb = this->eventBox](Gtk::Widget& w)
{
eb->Gtk::Container::remove(w);
}
);
std::cerr << 2;
eventBox->add(*im);
std::cerr << 3;
eventBox->show_all_children();
std::cerr << 4;
// ...
lock_flag.clear();
When error 11
occurs some of the numbers do not get printed to std::cerr
, but where the problem happens is different each time (i have recently observed it crashing after 12 and after 123). Thus i come to the conclusion that the resource that is being used somewhere is not the image, but the eventBox
. But after initialisation of the program it is not accessed anywhere outside of this function, and this function is wrapped with the std::atomic_flag
lock.
Questions: What can be a reason for such behaviour? Can i ensure that it doesn't happen? Or can i catch this error and expect to recover from it?
Edits:
I have attempted
std::thread
to Glib::Threads::Thread
, but to no avail, still getting the same error.GDK_SYNCHRONIZE
to environment, this has generated [xcb] Unknown request in queue while dequeuing/[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
error. This lead me to this post, after which i have attempted to call XInitThreads()
before starting the new thread (both via Glib::Threads::Thread
and std::thread
), but this did nothing positive; except that once by some fluke the thread has actually executed the entire function (displayed '4' on screen), but then still managed to die with the same error 11
message.GTK is not thread safe. You can use a global lock to access GTK from threads, but the best practice is to only invoke GTK functions from the main thread. You can use Glib::signal_idle().connect and Glib::MainContext::invoke() for that.
In the end here is how i've resolved all the problems.
Going through the link provided by Phillip i have found out about Glib::signal_timeout()
, and that has enabled me to completely rethink the concept of parallelism in my code.
Here is what i had to start with:
if(running) return;
running = true;
static bool firstTime = true;
if(firstTime)
{
XInitThreads();
firstTime=false;
}
std::function<void()> f = [this] () -> void
{
while(running)
{
this->takeStep();
std::this_thread::sleep_for(std::chrono::milliseconds(300));
}
};
std::thread(f).detach();
and this could easily be rewritten as:
if(running) return;
running = true;
runningHandle = Glib::signal_timeout().connect( sigc::mem_fun(*this, &ArtificialIntelligence::takeStep), 300 );
I then only needed to add runningHandle.disconnect();
to my pause function, and all began working wonderfully. In fact the speed of GUI response has gone up.
So if anybody else is trying to do "take an action, then sleep" process, this is a much better alternative. There are of course applications where there is no set periodicity, and then some other solution should be sought.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With