I missed the list in my last reply, so I'm copying it here for the record. In addition to using a cancelable I changed CallbackDispatcher to use a recursive mutex so gui_print can be called from the main thread as well.
While providing such an in depth example to L. James maybe goes against the learn-the-fundamentals-and-help-yourself culture, I'd just like to have a final answer and close the thread after a month and 100+ messages.
---------- Forwarded message ----------
From: "Andrew Potter" <agpotter gmail com>
Date: Aug 20, 2013 11:22 AM
Subject: Re: What is the minimum number of lines to update a gui window without user clicking a button
To: "L. D. James" <ljames apollo3 com>
Cc:
> On Tue, Aug 20, 2013 at 6:01 AM, L. D. James <ljames apollo3 com> wrote:
> > When clicking on the close button to cancel the operation the gui window
> > dims ad become non-responsive... locked up. Â That is a problem, in that some
> > of the programs might only take seconds or minutes to complete.
>
> I designed it that way because I didn't know if it was safe to have
> your blocking operation terminated suddenly; instead it ensures that
> your blocking operation runs until it fully completes. If it is safe
> to stop at any point then you can remove thread_cleanup() from
> Example::~Example(). Otherwise it is best to periodically check in the
> blocking operation whether the window has been closed.
> Gio::Cancellable can be used for this.
>
> // Example 6
> #include <gtkmm.h>
> #include <unistd.h>
> #include <queue>
>
> class CallbackDispatcher {
> public:
> Â Â CallbackDispatcher() {
> Â Â Â Â dispatcher.connect(sigc::mem_fun(this,
> &CallbackDispatcher::on_dispatch));
> Â Â }
>
> Â Â typedef sigc::slot<void> Message;
> Â Â void send(Message msg) {
> Â Â Â Â Glib::Threads::RecMutex::Lock lock(mutex);
> Â Â Â Â queue.push(msg);
> Â Â Â Â dispatcher();
> Â Â }
>
> private:
> Â Â /* CallbackDispatcher may not be copied, so we must hide these
> Â Â Â * constructors. */
> Â Â CallbackDispatcher(const CallbackDispatcher&);
> Â Â CallbackDispatcher& operator=(const CallbackDispatcher&);
>
> Â Â Glib::Threads::RecMutex mutex;
> Â Â std::queue<Message> queue;
> Â Â Glib::Dispatcher dispatcher;
>
> Â Â void on_dispatch() {
> Â Â Â Â Glib::Threads::RecMutex::Lock lock(mutex);
> Â Â Â Â while (!queue.empty()) {
> Â Â Â Â Â Â queue.front()();
> Â Â Â Â Â Â queue.pop();
> Â Â Â Â }
> Â Â }
> };
>
> class Example {
> private:
> Â Â Glib::RefPtr<Gtk::Application> app;
>   Gtk::Window           win;
>   Gtk::TextView          tv;
> Â Â Glib::RefPtr<Gtk::TextBuffer> Â tb;
>   CallbackDispatcher       callback_dispatcher;
>   Glib::Threads::Thread     *thread;
> Â Â Glib::RefPtr<Gio::Cancellable> cancellable;
>
> Â Â /* Appends str to the textbuffer. Like all methods that use the
> Â Â Â * Gtk namespace, it is not safe to call from another thread. */
> Â Â void gui_print_cb(const Glib::ustring& str) {
> Â Â Â Â tb->insert(tb->end(), str);
> Â Â }
>
> Â Â /* Provides threadsafe access to gui_print_cb() */
> Â Â void gui_print(const Glib::ustring& str) {
> Â Â Â Â sigc::slot<void> slot = sigc::bind(sigc::mem_fun(this,
> &Example::gui_print_cb), str);
> Â Â Â Â callback_dispatcher.send(slot);
> Â Â }
>
> Â Â /* This function is called on a separate thread so as to avoid
> Â Â * blocking GTK+'s main loop.
> Â Â */
> Â Â void blocking_operation() {
> Â Â Â Â /* This simulates a blocking process */
> Â Â Â Â sleep(5);
> Â Â Â Â gui_print("5% complete.\n");
> Â Â Â Â if (cancellable->is_cancelled()) return;
>
> Â Â Â Â sleep(5);
> Â Â Â Â gui_print("15% complete.\n");
> Â Â Â Â if (cancellable->is_cancelled()) return;
>
> Â Â Â Â sleep(5);
> Â Â Â Â gui_print("35% complete.\n");
> Â Â Â Â if (cancellable->is_cancelled()) return;
>
> Â Â Â Â sleep(5);
> Â Â Â Â gui_print("55% complete.\n");
> Â Â Â Â if (cancellable->is_cancelled()) return;
>
> Â Â Â Â sleep(5);
> Â Â Â Â gui_print("75% complete.\n");
> Â Â Â Â if (cancellable->is_cancelled()) return;
>
> Â Â Â Â sleep(5);
> Â Â Â Â gui_print("95% complete.\n");
> Â Â Â Â if (cancellable->is_cancelled()) return;
>
> Â Â Â Â sleep(5);
> Â Â Â Â gui_print("100% complete.\n");
>
> Â Â Â Â /* When the process is finished, we notify the GTK+ Main Loop by
> Â Â Â Â Â * using the dispatcher */
> Â Â Â Â callback_dispatcher.send(sigc::mem_fun(this,
> &Example::blocking_operation_finished));
> Â Â };
>
> Â Â /* This function is called on GTK+'s main loop via a
> Â Â Â * Glib::Dispatcher */
> Â Â void blocking_operation_finished() {
> Â Â Â Â cleanup_thread();
> Â Â Â Â gui_print("Operation finished.\n");
> Â Â };
>
> Â Â void cleanup_thread() {
> Â Â Â Â /* We must call Glib::Threads::Thread::join() in order to
> Â Â Â Â Â * correctly clean up the resource. */
> Â Â Â Â if (thread) {
> Â Â Â Â Â Â thread->join();
> Â Â Â Â Â Â thread = NULL;
> Â Â Â Â }
> Â Â }
>
> public:
> Â Â ~Example() {
> Â Â Â Â /* This will prevent the Window from being closed while
> Â Â Â Â Â * the blocking operation is ongoing. */
> Â Â Â Â cancellable->cancel();
> Â Â Â Â cleanup_thread();
> Â Â }
>
> Â Â Example(int argc, char *argv[]) :
> Â Â Â Â app(Gtk::Application::create(argc, argv, "com.example")),
> Â Â Â Â tb(tv.get_buffer()),
> Â Â Â Â thread(NULL),
> Â Â Â Â cancellable(Gio::Cancellable::create())
> Â Â {
> Â Â Â Â win.add(tv);
> Â Â Â Â win.show_all();
> Â Â }
>
> Â Â void run() {
> Â Â Â Â /* Create a slot to the blocking_operation() member function
> Â Â Â Â Â * to pass to Glib::Threads::Thread::create(). */
> Â Â Â Â sigc::slot<void> op_functor = sigc::mem_fun(this,
> &Example::blocking_operation);
>
> Â Â Â Â /* Create a worker thread to perform the blocking_operation
> Â Â Â Â Â * function. */
> Â Â Â Â thread = Glib::Threads::Thread::create(op_functor);
>
> Â Â Â Â /* Now set the text in the buffer. */
> Â Â Â Â gui_print("Operation started.\n");
>
> Â Â Â Â app->run(win);
> Â Â }
> };
>
> int main(int argc, char *argv[]) {
> Â Â Example example(argc, argv);
>
> Â Â example.run();
> Â Â return 0;
> }
> // Example 6