1
// gcc -o 0 $(pkg-config --cflags --libs gtk+-2.0) 1.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
struct tst
{
    GtkWidget *win, *w2, *hb, *vb, *ent, *btn, *b2, *pbar;
    GtkAccelGroup *acc;
};
GCancellable *can1;
GError *err1;
GThread *t1;
static void t1_stop (struct tst *prg)
{
    g_cancellable_cancel (can1);
    can1 = NULL;
}
gpointer t1_do (gpointer ptr1)
{
    struct tst *prg = (gpointer)ptr1;
    g_file_copy (g_file_new_for_path ("/1.avi"), g_file_new_for_path ("/2.avi"), G_FILE_COPY_NOFOLLOW_SYMLINKS, can1, NULL, NULL, &err1);
    if (err1 != NULL) g_error_free (err1);
    gtk_widget_destroy (prg->w2);
}
static void window_pbar (struct tst *prg)
{
    prg->w2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    prg->hb = gtk_hbox_new (FALSE, 0);
    prg->pbar = gtk_progress_bar_new ();
    prg->b2 = gtk_button_new_with_label ("Cancel");
    gtk_container_add (GTK_CONTAINER (prg->w2), GTK_WIDGET (prg->hb));
    gtk_window_set_position (GTK_WINDOW (prg->w2), GTK_WIN_POS_CENTER);
    gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->pbar), FALSE, FALSE, 0);
    gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->b2), FALSE, FALSE, 0);
    g_signal_connect_swapped (prg->w2, "delete_event", G_CALLBACK (t1_stop), prg);
    g_signal_connect_swapped (prg->b2, "clicked", G_CALLBACK (t1_stop), prg);
    gtk_widget_show_all (GTK_WIDGET (prg->w2));
    can1 = g_cancellable_new ();
    err1 = NULL;
    t1 = g_thread_create (t1_do, (gpointer)prg, TRUE, NULL);
}
static void window_new ()
{
    struct tst *prg = g_new0 (struct tst, 1);
    prg->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    prg->vb = gtk_vbox_new (FALSE, 0);
    prg->btn = gtk_button_new_with_label ("start");
    gtk_container_add (GTK_CONTAINER (prg->win), GTK_WIDGET (prg->vb));
    gtk_box_pack_start (GTK_BOX (prg->vb), GTK_WIDGET (prg->btn), FALSE, FALSE, 0);
    g_signal_connect (prg->win, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
    g_signal_connect_swapped (prg->btn, "clicked", G_CALLBACK (window_pbar), prg);
    gtk_window_set_title (GTK_WINDOW (prg->win), "Test program");
    gtk_window_set_position (GTK_WINDOW (prg->win), GTK_WIN_POS_CENTER);
    gtk_widget_show_all (GTK_WIDGET (prg->win));
    gtk_main ();
}
int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);
    window_new ();
    return 0;
}

This program is an example. When 'start' button is clicked the program creates a window with progress bar and 'cancel' button, and make a thread to copy /1.avi to /2.avi, but because of /1.avi doesn't exist the program will write 'Error!' on terminal and close the progress bar window. But there is one problem in this program. While I click 'start' button many times somewhen the program writes another error messages on terminal. Sometimes the messages are about GDK, sometimes about GObject, and sometimes about GTK+. And sometimes program itself is freezed or crashed.

// gcc -o 0 $(pkg-config --cflags --libs gtk+-2.0) 1.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
struct tst
{
    GtkWidget *win, *w2, *hb, *vb, *ent, *btn, *b2, *pbar;
    GtkAccelGroup *acc;
};
GCancellable *can1;
GError *err1;
GThread *t1;
static void t1_stop (struct tst *prg)
{
    g_cancellable_cancel (can1);
    can1 = NULL;
}
gpointer t1_do (gpointer ptr1)
{
    struct tst *prg = (gpointer)ptr1;
    g_file_copy (g_file_new_for_path ("/1.avi"), g_file_new_for_path ("/2.avi"), G_FILE_COPY_NOFOLLOW_SYMLINKS, can1, NULL, NULL, &err1);
    if (err1 != NULL) g_error_free (err1);
    gtk_widget_destroy (prg->w2);
}
static void window_pbar (struct tst *prg)
{
    prg->w2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    prg->hb = gtk_hbox_new (FALSE, 0);
    prg->pbar = gtk_progress_bar_new ();
    prg->b2 = gtk_button_new_with_label ("Cancel");
    gtk_container_add (GTK_CONTAINER (prg->w2), GTK_WIDGET (prg->hb));
    gtk_window_set_position (GTK_WINDOW (prg->w2), GTK_WIN_POS_CENTER);
    gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->pbar), FALSE, FALSE, 0);
    gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->b2), FALSE, FALSE, 0);
    g_signal_connect_swapped (prg->w2, "delete_event", G_CALLBACK (t1_stop), prg);
    g_signal_connect_swapped (prg->b2, "clicked", G_CALLBACK (t1_stop), prg);
    gtk_widget_show_all (GTK_WIDGET (prg->w2));
    can1 = g_cancellable_new ();
    err1 = NULL;
    t1 = g_thread_create (t1_do, (gpointer)prg, TRUE, NULL);
    g_thread_join (t1);
}
static void window_new ()
{
    struct tst *prg = g_new0 (struct tst, 1);
    prg->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    prg->vb = gtk_vbox_new (FALSE, 0);
    prg->btn = gtk_button_new_with_label ("start");
    gtk_container_add (GTK_CONTAINER (prg->win), GTK_WIDGET (prg->vb));
    gtk_box_pack_start (GTK_BOX (prg->vb), GTK_WIDGET (prg->btn), FALSE, FALSE, 0);
    g_signal_connect (prg->win, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
    g_signal_connect_swapped (prg->btn, "clicked", G_CALLBACK (window_pbar), prg);
    gtk_window_set_title (GTK_WINDOW (prg->win), "Test program");
    gtk_window_set_position (GTK_WINDOW (prg->win), GTK_WIN_POS_CENTER);
    gtk_widget_show_all (GTK_WIDGET (prg->win));
    gtk_main ();
}
int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);
    window_new ();
    return 0;
}

So I modified some parts of program. Now the problem not occurs though I click 'start' button many times, but I met another problem. When I can copy /1.avi to /2.avi, if I click 'start' button then program copies /1.avi to /2.avi but during this process the program window freezes, and progress bar window doesn't appear(On first example this problem isn't occured).

What should I do to make this program to meet neither of two problems?

5frame
  • 101
  • 1
  • 8

1 Answers1

2

The immediate problem I can see with your code is that you are calling a Gtk function in t1_do which is run from the secondary thread without enclosing it in gtk_thread_enter (); gtk_thread_leave ();

The general rule is that you should never call a UI function from a thread that isn't the main UI thread. Until you fix that problem you could just get completely random errors and behaviour

iain
  • 5,660
  • 1
  • 30
  • 51
  • Thank you. I solved the problem by enclosing gtk_widget_destroy with gdk_threads_ set: gdk_threads_enter () -> gtk_widget_destroy (...) -> gdk_threads_leave (). – 5frame Aug 10 '11 at 05:07