Threaded Gtk2->main causing seg fault



Hi

I understand that this is not normal usage of perl gtk+, but I really have no options because I am writing a plugin which must provide a GUI. If I just run the Gtk2->main without a new thread, the plugin will block.

My system config:

Freebsd-stable
gtk+-2.4.0
perl-5.8.2
p5-Gtk2-1.040


What I did was:

In main:
1. create a instance of some object, namely $base (which has signal)
2. create a thread.

In the thread:
1. create the widgets.
2. connect the Gtk2->main_quit to window's destroy signal.
3. connect $base 's signal.
4. run Gtk2->main.

Back in main:
1. sleep for awhile.
2. make $base emit a signal.
3. make the thread->join


The seg fault comes when I tried to close the window. It gives alot of warnings about wrong reference counts. I rewrite the same thing in C just to make sure gtk allows gtk_main in new thread, and the C version runs fine.

The warnings are as followed:

in callbackUnbalanced string table refcount: (2) for "::ISA::CACHE::" during global destruction. Unbalanced string table refcount: (1) for "signal_connect" during global destruction.
Unbalanced string table refcount: (1) for "add" during global destruction.
Unbalanced string table refcount: (2) for "DESTROY" during global destruction. Unbalanced string table refcount: (1) for "show_all" during global destruction.

The C and perl version of the code are attached. Any one interested may try running them.

Kevin Leung
use strict;
use warnings;

package Base;
use Gtk2;

use Glib::Object::Subclass
        'Glib::Object',
        signals => {
                'refresh' => {}
        },
        properties => [
                Glib::ParamSpec->boxed (
                        'servers',
                        'servers',
                        'Hash of servers',
                        'Glib::Scalar',
                        [qw/readable writable/]),
        ]
;

sub INIT_INSTANCE
{
        my $self = shift;
        $self->{servers} = {};
}

package main;
use Gtk2 '-init';
use threads;
use threads::shared;

my $base = Base->new ();
share ($base);
my $thr = threads->new (\&run);

sub run
{
        my $win;
        my $label;
        
        $win = Gtk2::Window->new ();
        $label = Gtk2::Label->new ('NULL');
        
        $win->add ($label);
        lock ($base);
        $base->signal_connect ('refresh', \&refresh_cb, $label);
        $win->signal_connect ('destroy', sub {
                        Gtk2->main_quit;
                        });

        $win->show_all ();

        Gtk2->main ();

}

sub refresh_cb
{
        my ($self, $label) = @_;
        $label->set_text ('IN CALLBACK');
        print 'in callback';

}

sleep 1;
$base->signal_emit ('refresh');
$thr->join ();
#include <stdio.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <pthread.h>

/* Define a new Object here */
typedef struct _Base Base;
typedef struct _BaseClass BaseClass;

#define TYPE_BASE (base_get_type ())

struct _Base {
        GObject parent;
};

struct _BaseClass {
        GObjectClass parent_class;
        void (*refresh) (void *);
};

enum {
        REFRESH,
        TOTAL
};

static void base_class_init (BaseClass *klass);
static void base_init (Base *base);
static void base_finalize (GObject *object);

static GObjectClass *parent_class = NULL;
static guint signals[TOTAL];
static Base *base = NULL;


GType base_get_type (void)
{
        static GType type = 0;
        if (!type) {
                static const GTypeInfo info = {
                        sizeof (BaseClass),
                        NULL, NULL,
                        (GClassInitFunc) base_class_init,
                        NULL, NULL,
                        sizeof (Base),
                        0,
                        (GInstanceInitFunc) base_init,
                        0
                };
                type = g_type_register_static (G_TYPE_OBJECT, "Base", &info, 0);
        }
        return type;
}

static void base_class_init (BaseClass *klass)
{
        GObjectClass *obj_class = (GObjectClass *) klass;
        obj_class->finalize = base_finalize;
        parent_class = g_type_class_peek_parent (klass);

        signals[REFRESH] = g_signal_new (
                        "refresh",
                        TYPE_BASE,
                        G_SIGNAL_RUN_FIRST,
                        G_STRUCT_OFFSET (BaseClass, refresh),
                        NULL,
                        NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE,
                        0);

}

static void base_init (Base *base)
{
                        
}

static void base_finalize (GObject *object)
{
        parent_class->finalize (object);
}

static void base_emit (Base *base, gpointer ptr)
{
        g_signal_emit (base, signals[REFRESH], 0, ptr);
}

static Base* base_new (void)
{
        Base *base = g_object_new (TYPE_BASE, NULL);
        return base;
}

/* Using the Object */
static void* start_gtk (void *);

int main (int argc, char **argv)
{
        gtk_init (&argc, &argv);
        
        pthread_t thread;
        
        base = base_new ();

        if (pthread_create (&thread, NULL, start_gtk, NULL)) {
                printf ("Error pthread_create\n");
                return 1;
        }
        sleep (3);
        base_emit (base, NULL);

        pthread_join (thread, NULL);

        return 0;
}

static void refresh_cb (Base *base, gpointer ptr)
{
        printf ("Refreshed\n");
}

static void* start_gtk (void *data)
{
        GtkWidget *win;
        GtkWidget *label;

        win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        label = gtk_label_new ("NULL");
        gtk_container_add (GTK_CONTAINER (win), label);
        g_signal_connect (G_OBJECT (win), "destroy",
                        G_CALLBACK (gtk_main_quit), NULL);
        g_signal_connect (G_OBJECT (base), "refresh",
                        G_CALLBACK (refresh_cb), NULL);
        gtk_widget_show_all (win);
        gtk_main ();
        return NULL;
}




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]