deadlock problem with threads in GTK - c++

In my GUI, I have a list store treeview in my main window. When the user double clicks a row, a dialog pops up. The problem is that the data I populate in the dialog box takes a while to process, so what I've done is started a thread (using boost threads) to do the dialog box calculations.
In main:
.......
g_signal_connect (G_OBJECT (m_treeview), "row_activated", G_CALLBACK (m_row_activated),
(gpointer) main_window);
.......
In m_row_activated:
.........
// combo_box and dialog are GtkWidget* global variables
create_dialog(dialog, combo_box); // function creates the combobox
set_combo_box_with_loading_message;
gtk_widget_show_all (dialog);
thread m_thread (bind (&do_dialog_calculations, data1, data2, combobox));
.........
In do_dialog_calculations:
.........
// do_calculations takes about 15 seconds to complete
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
gtk_combo_box_append_text(...);
gdk_threads_leave()
Everything works fine (i.e. when the user double clicks a row, a dialog pops up immediately with a loading message and it is populated eventually when the thread returns), but my problem is when the user closes the dialog before do_calculations in do_dialog_calculations completes. If the dialog is destroyed, my combobox within it will be destroyed and my call to gtk_combo_box_append_text will seg fault.
I tried to test the combo box before updating it:
In do_dialog_calculations:
.........
do_calculations(MyData data1, MyData data2, combobox);
gdk_threads_enter();
if (GTK_IS_COMBO_BOX (combobox))
gtk_combo_box_append_text(...);
gdk_threads_leave()
but this results in a deadlock at the call to GTK_IS_COMBO_BOX. I think this is beause GTK_IS_COMBO_BOX probably calls gdk_threads_enter(). I've also tried testing for NULL
if (combobox == NULL)
but that doesn't seem to work either. Any suggestions on how to get around this problem?
UPDATE: The deadlock at GTK_IS_COMBO_BOX only occurs if I close the dialog immediately after it opens (i.e. before do_calculations() completes. If I just let the dialog sit, it will eventually update. Also, if I switch the combobox check before writing calling gdk_threads_enter():
if (GTK_IS_COMBO_BOX (combobox)
{
gdk_threads_enter();
gtk_combo_box_append_text(...);
gdk_threads_leave();
}
No deadlock occurs when I destroy the dialog before this code executes. However, I'm afraid of the rare possibility that the user will close the dialog after the GTK_IS_COMBO_BOX check completes.
PS - I use threads to do my dialog box calculations because the dialog boxes are non modal, and I want the user to be able to do other things with the main UI while the dialog boxes populate.

I think this is beause GTK_IS_COMBO_BOX probably calls gdk_threads_enter()
I don't think this is the case. These macros are generally pretty simple and I wouldn't expect it to take a lock. In fact, as far as I can tell the whole idea of gdk_threads_enter is that the library itself shouldn't call this, only code that knows it's running in another thread should.
Here's my idea: Did you forget to call g_thread_init and gdk_threads_init?
Also, one thing to keep in mind... By default, gdk_threads_enter is not using a recursive mutex. Though some people have religious objections to recursive mutexes, it's possible to have gdk_threads_enter use one:
static GStaticRecMutex my_gdk_lock;
static void my_gdk_lock_enter() {g_static_rec_mutex_lock(&my_gdk_lock);}
static void my_gdk_lock_leave() {g_static_rec_mutex_unlock(&my_gdk_lock);}
// ...
g_thread_init(NULL);
g_static_rec_mutex_init(&my_gdk_lock);
gdk_threads_set_lock_functions(G_CALLBACK(my_gdk_lock_enter),
G_CALLBACK(my_gdk_lock_leave));
gdk_threads_init();
// ...
Update: From your comment it sounds like you have a race condition between destroying the dialog and populating the combo box. One potential solution is that you bump up the reference count of the combo box (i.e. gtk_widget_ref) so that it doesn't get freed while your asynchronous worker is doing something. Then release it with gtk_widget_unref when the other thread no longer needs the pointer.

Related

Animated gif image isn't being animated in my modeless Gtk::Dialog

My goal is to show a brief "Please Wait..." dialog with an animated gif (spinner) in a Gtk::Dialog.
My problem is that when I do not use Gtk:Dialog::run(), the gif won't be animated, and when I do use the Gtk:Dialog::run() method it completely blocks my running code afterwards. And since I don't have any buttons in my dialog it would hang there indefinitely. Is there a way around that? I have had no success in getting the animated gif to work in a non-modal dialog, i.e without using the run() method.
I'm using gtkmm 3.0
Compile with : g++ examplewindow.cc main.cc -o main `pkg-config gtkmm-3.0 --cflags --libs`
main.cc
#include "examplewindow.h"
#include <gtkmm/application.h>
#include <iostream>
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create("org.gtkmm.example");
ExampleWindow window;
//Shows the window and returns when it is closed.
//return app->make_window_and_run<ExampleWindow>(argc, argv);
return app->run(window);
}
examplewindow.h
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H
#include <gtkmm.h>
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow();
virtual ~ExampleWindow();
protected:
//Signal handlers:
void on_button_clicked();
//Child widgets:
Gtk::Box m_VBox;
Gtk::Box m_ButtonBox;
Gtk::Button m_Button;
};
#endif //GTKMM_EXAMPLEWINDOW_H
examplewindow.cc
#include "examplewindow.h"
#include <iostream>
ExampleWindow::ExampleWindow()
: m_VBox(Gtk::Orientation::ORIENTATION_VERTICAL),
m_ButtonBox(Gtk::Orientation::ORIENTATION_VERTICAL),
m_Button("Show Dialog")
{
set_title("Test animated gif");
set_default_size(800, 600);
add(m_VBox);
m_VBox.pack_start(m_ButtonBox);
m_ButtonBox.pack_start(m_Button);
m_Button.set_hexpand(true);
m_Button.set_halign(Gtk::Align::ALIGN_CENTER);
m_Button.set_valign(Gtk::Align::ALIGN_CENTER);
m_Button.grab_focus();
m_Button.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_button_clicked));
show_all_children();
}
ExampleWindow::~ExampleWindow()
{
}
void ExampleWindow::on_button_clicked()
{
Gtk::Dialog m_Dialog;
m_Dialog.set_transient_for(*this);
m_Dialog.set_size_request(200, 200);
m_Dialog.set_decorated(false);
Gtk::Image imageLoading = Gtk::Image();
imageLoading.property_pixbuf_animation() = Gdk::PixbufAnimation::create_from_file("gtkmm_logo.gif");
m_Dialog.get_vbox()->pack_start(imageLoading);
m_Dialog.show_all();
m_Dialog.run();
/******** This, below, never gets executed as run() is blocking the program...********/
// Dummy "long" operation
for (int i = 0; i <= 2010101010; i++)
{
if (i == 2010101010)
std::cout << "Done" << std::endl;
}
m_Dialog.response(Gtk::RESPONSE_ACCEPT);
m_Dialog.hide();
}
Let us look at the original problem. You created a dialog, called show() on it, did some long-running process, then closed the dialog. The process worked, but your program froze during the processing. Why is that?
A graphical interface works by processing messages (events). Some events run off a timer, such as the ones that tell an animation to go to the next frame. Some are generated as needed, such as the ones that tell an image to draw the current frame. These events need to be both triggered and processed to be effective. You triggered the appropriate events with your call to show_all(), but you did not give your program a chance to handle those events.
You used a button click to start your long-running process. That click is an event that was handled by your main event handling loop. That loop then waited for the click to be fully handled before moving on to the next event. However, you have your long-running process in the handler. The main event loop had to wait for that process to finish before it could handle new events, such as the ones to show and animate your image. You never gave your dialog a chance to do its job before you destroyed it.
Calling the dialog's run() method partially fixed the situation by starting a new event loop for the dialog. So even though the main event loop was still blocked by your click handler, new events could be handled. The dialog's event loop received the events required to show an animation, hence your program was again responsive. Unfortunately, run() blocked your long-running process, so we're not really any better off.
The simplest fix is to no longer completely block your main event loop. You could have your long-running process periodically allow events to be processed via Gtk::Main::iteration(). This function invokes an iteration of the main event loop, allowing your program to stay responsive. Pass it a false argument so that it only processes events if there are some to process (rather than waiting for an event to occur).
for (unsigned long i = 0; i <= 2010101010; i++)
{
if (i == 2010101010)
std::cout << "Done" << std::endl;
// Periodically process events
if ( i % 10000 == 0 ) // <---- after some suitable amount of work
if ( !Gtk::Main::iteration(false) ) // <---- allow events to be processed
// Abort the work.
break;
}
The return value is supposed to tell you if you should quit or not, but I didn't get this working in my test (and the return value seemed to have the opposite meaning compared to the documentation). Maybe the dialog itself was keeping the app alive? Eh, that can be the next question, once this part is working.
Other approaches would move your long-running process out of the click handler. If you let the click handler end quickly, the main event loop can do its job without the extra prompting from you. However, this requires a few adjustments so that the Gtk::Dialog outlives the call to on_button_clicked(). That's a bit of refactoring, but it might be worth the time. I'll present two options (without code).
You could have your work operate on multiple timeout signals. Divide your long-running process into smaller chunks, each chunk suitably sized for a callback. (How big is that? Not sure. For now, let's say at most a few milliseconds.) Have the button click event start the first timeout signal with a priority that allows the GUI to update. (As I recall, PRIORITY_DEFAULT_IDLE should work.) For the interval, I would try 0 if that does not overly confuse Gtk+. (I have not tried it, but it seems plausible.) If the 0-interval works, it might be wise to use connect_once() instead of connect(), and have each chunk schedule the next with another timeout. The final chunk would be responsible for closing the dialog.
You could move your long-running process to another thread. Multi-threaded programming has its own set of problems and sometimes a lot of setup, but this is something it is well-suited for. If your long-running process is in a different thread than your main event loop, the operating system becomes responsible for making sure each thread gets some CPU time. Your long-running process can chug away, and the main event loop would simultaneously be able to process events with no special intervention from you.
Final notes:
If your dialog is for one-way communication to the user, it seems more like a monologue than a dialogue. Excuse me, more like an ordinary window than a dialog. Also, I'll make sure you are aware of Gtk::ProgressBar, which "is typically used to display the progress of a long running operation." Just an option; preferring your image is understandable.

validating UpdateData(true) return value in MFC OnTimer routine

I am working on an MFC application(VS2017).
In that I have a made one second timer. Inside the OnTimer() routine along with my business logic I am calling UpdateData(FALSE) to show some runtime info back to UI.
Also, I have to get some user input from the UI for that inside the user Input Event handler, I am calling UpdateData(TRUE) and checking its return value in OnTimer routine.
Problem is since the timer is an independent entity here. Even though I have guarded the UpdateData(FALSE) inside OnTimer the UpdateData(FALSE) is getting executed for a blank user input, which crashes the program by calling assert.
/***SAMPLE Problem CODE****/`
void abc::OnEnUserInput()
{
IsvalidInput = UpdateData(TRUE);
}
void abc::OnTimer(UINT_PTR nIDEvent)
{
if (IsvalidInput == true)
{
UpdateData(FALSE);
}
}
Any help would be greatly appreciated!!!
Thanks.
Got my solution earlier itself but due to the busy schedule couldn't get time to share it.
for avoiding this issue I opened another dialogue box to receive the user input, since UpdatedData(FALSE) in OnTimer() is in background dialogue box so the user input in child dialog UpdateData(TRUE) will not interfare with background UpdatedData(FALSE).

MessageDialog in separate thread

In my current project I need to perform some calculations when a specific button is pressed, and while I perform these calculations, I want to show a Gtk::MessageDialog that simply states that calculations are being performed. So, I initialize the MessageDialog like this (for the moment just ignore that I actually don't need the pointer here):
Gtk::MessageDialog *waitdialog;
Gtk::MessageDialog dia("Processing", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE, true);
dia.set_title("Wait.");
dia.set_transient_for(*(Gtk::Window *)this);
waitdialog = &dia;
Next I want to start a separate thread with the dialog:
std::thread dialog_thread(wait_dialog,waitdialog);
The wait_dialog method is defined as follows:
void wait_dialog(Gtk::MessageDialog *dialog){
dialog->run();
}
The problem now is, that even though the main window is darkened (because of set_transient_for), the message dialog is not visible. However, when I don't start a seperate thread, but just call waitdialog->run() instead, this will show the dialog properly (but will result in a loop).
So, the question is: why does the workaround with the separate thread not work? I can't make any sense of that :-(
GUI components are required to stay in the GUI loop. Your long running calculations belong in the thread. The calculation thread then signals back to the GUI thread to close the modal dialog. Also, you should use glib threads instead of std::threads. Here's how I would structure program:
// in header, member var dispatcher used to signal GUI thread
// and our member var thread
Glib::Dispatcher m_signalDone;
Glib::Thread* m_someThread;
...
// in constructor, hook up dispatcher event
m_signalDone.connect(sigc::mem_fun(this, &MyClass::OnDone));
...
// later when ready to kick off thread...
// show dialog or progess bar or something and kick off thread...
m_someThread = Glib::Thread::create(sigc::mem_fun(*this, &MyClass::CalcMethod), true);
...
void MyClass::CalcMethod()
{
// do your long running stuff...
// when done signal completion back to GUI
m_signalDone.emit();
}
...
void MyClass::OnDone()
{
// clean up dialog or progress bar or whatever
// kill thread
m_currentBackUpThread->join();
m_currentBackUpThread = NULL;
}

wxProgressDialog somehow keeping app alive after death?

I'm having a strange problem with wxWidgets. I have the following code
MyFrame::OnDoSomeLongThing(...) {
progScreen = new wxProgressDialog(text,text,number,this,wxPD_AUTO_HIDE); // wxProgressDialog *progScreen is class member
doPartOfThing() // calls the update method at the end of it
....
doLastPartOfThing() // again calls update method that pushes value to 100/100
progScreen->Destroy();
}
MyFrame::update() {
progScreen->Update(newValue);
}
Now here's the thing. I can literally comment out the lines relating to progScreen, just let the process go without using a progress dialog, after all is said and done, my apps exits gracefully when I close the main window.
However, just the use of the progress dialog is somehow extending the life of the application. I've tried Destroy(), I've tried simply 'delete progScreen', and both, every time: I'll close the main frame, the process keeps running, and at some point exits with some astronomical number. The only thing I could think might be relevant, is that the doPartsOfThings methods may call boost::this_thread::sleep, because it involves waiting and whatnot down in my model class. But this shouldn't have anything to do with my problem. Or maybe it does... EDIT: I do want to emphasize that progScreen->Update() IS being called from the main (GUI) thread.
So I ask, am I using a wxProgressDialog correctly? If not, how should it be used?
Thanks for your help!
EDIT:
Well... it turns out that removing wxPD_AUTO_HIDE fixed the problem. I'm still not quite sure what the problem is, but the dialog even still behaves as before. App closes as expected.
I think that you need to override the wxApp method that closes the application so that it closes the wxProgressDialog object before it quits.
wxApp::OnExit
virtual int OnExit()
Override this member function for any processing which needs to be
done as the application is about to exit. OnExit is called after
destroying all application windows and controls, but before wxWidgets
cleanup. Note that it is not called at all if OnInit failed.
The return value of this function is currently ignored, return the
same value as returned by the base class method if you override it.
You will need something like, assuming progScreen is a public attribute of your frame
int myApp::OnExit()
{
(MyFrame*)(GetTopWindow())->progScreen->Destroy()
return wxApp::OnExit();
}

CDockingManager GetPaneList() causes assertion failure in wincore.cpp?

So I thought this would be pretty simple, but I forgot it's MFC. Instead of registering a notification listener for data model changes that would possibly require a GUI update on each individual control I figure why not register it once and then send a message to all the open dock panes and allow them to update their controls as needed on their own terms for efficiency.
My callback function for handling the notification from the server looks something like this:
void CMainFrame::ChangeCallback(uint32_t nNewVersion, const std::vector<uint32_t>& anChangedObjectTypes)
{
CObList panes;
GetDockingManager()->GetPaneList(panes); // assert failure
if (!panes.IsEmpty())
{
POSITION pos = panes.GetHeadPosition();
while (pos)
{
CDockablePane* pPane = dynamic_cast<CDockablePane*>(panes.GetNext(pos));
if (pPane)
pPane->PostMessage(DM_REFRESH, nNewVersion);
}
}
}
The error I am getting is an assertion failure on line 926 of wincore.cpp
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL); // right here
There is a comment below this saying this can happen if you pass controls across threads however this is a single threaded MFC application and this is all being done from the main frame.
Does anyone know what else can cause this?
If there is another way to go about sending a message to all the open CDockablePane derived windows in MFC that works as well ...
Here's the obvious workaround that I didn't want to have to do but after hours of debugging and no response here I guess this is a viable answer:
I added std::vector<CDockPane*> m_dockList; to the members of CMainFrame
Now after each call to AddPane in various places that can create and open new dock panes I make a subsequent call to push_back and then I override CDockablePane::OnClose like so:
CMainFrame* pMainFrame = reinterpret_cast<CMainFrame*>(AfxGetMainWnd());
if (pMainFrame)
{
std::vector<CDockPane*>::const_iterator found(
std::find(pMainFrame->DockList()->begin(), pMainFrame->DockList()->end(), this));
if (found != pMainFrame->DockList()->end())
pMainFrame->DockList()->erase(found);
}
CDockablePane::OnClose();
Now this list will only contain pointers to open dock panes which allows me to handle the event notification in my callback and simply do a for loop and PostMessage to each.