GTK+ Forums

Discussion forum for GTK+ and Programming. Ask questions, troubleshoot problems, view and post example code, or express your opinions.
It is currently Fri Oct 31, 2014 3:05 pm

All times are UTC




Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: Gdk_threads[enter/leave]
PostPosted: Fri Apr 04, 2008 3:57 pm 
Offline
Familiar Face

Joined: Wed Jan 23, 2008 11:08 pm
Posts: 38
Location: Brasil
Hello! Why i have to surround gtk_main() with gdk_threads_enter/leave ?
If i do, doesn't internal gtk code lock forever because of that? It makes
calls to GDK_THREADS_ENTER/LEAVE, so... if i lock it gtk will deadlock? don't?

Documentations notes:

from g_mutex_lock doc:

* GMutex is neither guaranteed to be recursive nor to be non-recursive,
i.e. a thread could deadlock while calling g_mutex_lock(), if it already
has locked mutex. Use GStaticRecMutex, if you need recursive mutexes.

But the gtk_threads_enter/leave impl, use GMutex:

from gdk.h:

Code:
#if !defined (GDK_DISABLE_DEPRECATED) || defined (GDK_COMPILATION)
GDKVAR GMutex *gdk_threads_mutex; /* private */
#endif


from gdk.c:

Code:
void
gdk_threads_init (void)
{
  if (!g_thread_supported ())
    g_error ("g_thread_init() must be called before gdk_threads_init()");

  gdk_threads_mutex = g_mutex_new ();
  if (!gdk_threads_lock)
    gdk_threads_lock = gdk_threads_impl_lock;
  if (!gdk_threads_unlock)
    gdk_threads_unlock = gdk_threads_impl_unlock;
}


and

Code:
static void
gdk_threads_impl_lock (void)
{
  if (gdk_threads_mutex)
    g_mutex_lock (gdk_threads_mutex);
}

static void
gdk_threads_impl_unlock (void)
{
  if (gdk_threads_mutex)
    g_mutex_unlock (gdk_threads_mutex);
}


So why this following code works? :)


Code:
/*-------------------------------------------------------------------------
* Filename:      gtk-thread.c
* Version:       0.99.1
* Copyright:     Copyright (C) 1999, Erik Mouw
* Author:        Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
* Description:   GTK threads example.
* Created at:    Sun Oct 17 21:27:09 1999
* Modified by:   Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
* Modified at:   Sun Oct 24 17:21:41 1999
*-----------------------------------------------------------------------*/
/*
* Compile with:
*
* cc -o gtk-thread gtk-thread.c `gtk-config --cflags --libs gthread`
*
* Thanks to Sebastian Wilhelmi and Owen Taylor for pointing out some
* bugs.
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <pthread.h>

#define YES_IT_IS    (1)
#define NO_IT_IS_NOT (0)

typedef struct
{
  GtkWidget *label;
  int what;
} yes_or_no_args;

G_LOCK_DEFINE_STATIC (yes_or_no);
static volatile int yes_or_no = YES_IT_IS;

void destroy (GtkWidget *widget, gpointer data)
{
  gtk_main_quit ();
}

void *argument_thread (void *args)
{
  yes_or_no_args *data = (yes_or_no_args *)args;
  gboolean say_something;

  for (;;)
    {
      /* sleep a while */
      sleep(rand() / (RAND_MAX / 3) + 1);

      /* lock the yes_or_no_variable */
      G_LOCK(yes_or_no);

      /* do we have to say something? */
      say_something = (yes_or_no != data->what);

      if(say_something)
    {
      /* set the variable */
      yes_or_no = data->what;
    }

      /* Unlock the yes_or_no variable */
      G_UNLOCK (yes_or_no);

      if (say_something)
    {
      /* get GTK thread lock */ // Gdk mutex is locked by main() code!
      gdk_threads_enter ();

      /* set label text */
      if(data->what == YES_IT_IS)
        gtk_label_set_text (GTK_LABEL (data->label), "O yes, it is!");
      else
        gtk_label_set_text (GTK_LABEL (data->label), "O no, it isn't!");

      /* release GTK thread lock */
      gdk_threads_leave ();
    }
    }

  return NULL;
}

int main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *label;
  yes_or_no_args yes_args, no_args;
  pthread_t no_tid, yes_tid;

  /* init threads */
  g_thread_init (NULL);
  gdk_threads_init ();
  gdk_threads_enter ();

  /* init gtk */
  gtk_init(&argc, &argv);

  /* init random number generator */
  srand ((unsigned int) time (NULL));

  /* create a window */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  gtk_signal_connect (GTK_OBJECT (window), "destroy",
              GTK_SIGNAL_FUNC (destroy), NULL);

  gtk_container_set_border_width (GTK_CONTAINER (window), 10);

  /* create a label */
  label = gtk_label_new ("And now for something completely different ...");
  gtk_container_add (GTK_CONTAINER (window), label);
 
  /* show everything */
  gtk_widget_show (label);
  gtk_widget_show (window);

  /* create the threads */
  yes_args.label = label;
  yes_args.what = YES_IT_IS;
  pthread_create (&yes_tid, NULL, argument_thread, &yes_args);

  no_args.label = label;
  no_args.what = NO_IT_IS_NOT;
  pthread_create (&no_tid, NULL, argument_thread, &no_args);

  /* enter the GTK main loop */
  gtk_main (); //MYNOTE: it will block here until GTK main loop returns!
  gdk_threads_leave ();

  return 0;
}



Another weird point on gdk threads docs is:

* Idles, timeouts, and input functions from GLib, such as g_idle_add(), are executed
outside of the main GTK+ lock. So, if you need to call GTK+ inside of such a callback,
you must surround the callback with a gdk_threads_enter()/gdk_threads_leave() pair
or use gdk_threads_add_idle_full() which does this for you.

But, gtk says to do not use gtk_idle_add, and to use g_idle_add() instead. Ok, so it
leave me no options :) i have to call gdk_threads_enter, even if it lead-me to a deadlock ? :)

NOTE: I have an application running and it calls gtk functions from glib idle functions
without problem, is it just luck? It also calls gtk_threads_enter before gtk_main and, i call sections that use these locks without problem :)

Thanks!!!

_________________
http://killocan.freezope.org


Top
 Profile  
 
 Post subject: Any detail ?
PostPosted: Mon Apr 14, 2008 3:54 pm 
Offline
Familiar Face

Joined: Wed Jan 23, 2008 11:08 pm
Posts: 38
Location: Brasil
There is any detail that i'm forgetting? :)

_________________
http://killocan.freezope.org


Top
 Profile  
 
 Post subject: I think i get it!
PostPosted: Fri Apr 18, 2008 4:45 pm 
Offline
Familiar Face

Joined: Wed Jan 23, 2008 11:08 pm
Posts: 38
Location: Brasil
Hi! i made some changes in the code. Now it just lock and unlock the mutex, and make a
trace of it:

Code:
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <pthread.h>

void destroy(GtkWidget *widget, gpointer data)
{
  gtk_main_quit();
}

void *argument_thread(void *args)
{
  for(;;)
    {
      fprintf(stderr,"* GDK_THREADS_ENTER!\n");
      gdk_threads_enter();
      fprintf(stderr,"---------------> IN GDK_THREADS_ENTER!\n");
      gdk_threads_leave();
      fprintf(stderr,"* LEAVING GDK_THREADS_ENTER -> GDK_THREADS_LEAVE!\n");
    }

  return NULL;
}

int main(int argc, char *argv[])
{
  GtkWidget *window;
  pthread_t no_tid, yes_tid;

  g_thread_init(NULL);
  gdk_threads_init();

  fprintf(stderr,"# GDK_THREADS ENTER! (MAIN)\n");
  gdk_threads_enter();

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  gtk_signal_connect(GTK_OBJECT(window), "destroy",
                     GTK_SIGNAL_FUNC(destroy), NULL);

  gtk_widget_show(window);

  pthread_create(&yes_tid, NULL, argument_thread, &a);
  pthread_create(&no_tid, NULL, argument_thread, &b);

  fprintf(stderr,"# GTK_MAIN!\n");
  gtk_main();
  fprintf(stderr,"# LEAVING GTK_MAIN!\n");
  gdk_threads_leave();
  fprintf(stderr,"# LEAVING GDK_THREADS_ENTER -> GDK_THREADS_LEAVE! (MAIN)\n");

  return 0;
}


The output is:

killocan@localhost-marcos:~> ./gtk-thread
# GDK_THREADS ENTER! (MAIN)
# GTK_MAIN!
* GDK_THREADS_ENTER!
---------------> IN GDK_THREADS_ENTER!
* LEAVING GDK_THREADS_ENTER -> GDK_THREADS_LEAVE!
* GDK_THREADS_ENTER!
---------------> IN GDK_THREADS_ENTER!
* LEAVING GDK_THREADS_ENTER -> GDK_THREADS_LEAVE!
* GDK_THREADS_ENTER!
---------------> IN GDK_THREADS_ENTER!
* LEAVING GDK_THREADS_ENTER -> GDK_THREADS_LEAVE!

... GOES ON!

* GDK_THREADS_ENTER!
# LEAVING GTK_MAIN!
# LEAVING GDK_THREADS_ENTER -> GDK_THREADS_LEAVE! (MAIN)

Then i think, did gtk_main() unlock the mutex if its is locked?

And the answer is YES. The code bellow is a piece of gtk_main code:

Code:
  if (g_main_loop_is_running (main_loops->data))
    {
      GDK_THREADS_LEAVE();
      g_main_loop_run (loop);
      GDK_THREADS_ENTER();
      gdk_flush ();
    }


Note: before it call the main event loop it realeases the gdk mutex.

Documentation note:
Docs from g_main_loop_run:
void g_main_loop_run(GMainLoop *loop);

Runs a main loop until g_main_loop_quit() is called on the loop. If this is called for
the thread of the loop's GMainContext, it will process events from the loop, otherwise
it will simply wait.

It means (IMHO) as gtk_main create the loop with:

Code:
loop = g_main_loop_new (NULL, TRUE);


The first parameter (NULL) indicates to use the default main context. So it will process
events in a loop until g_main_loop_quit is called, AND without the mutex lock.

I guess, that's the answer to my question. If i'm wrong, please let me know!

Bye!

_________________
http://killocan.freezope.org


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC


Who is online

Users browsing this forum: Google [Bot], Yahoo [Bot] and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group