GTK2 crash after multi-threading joined



Hi, all. 

I use threads to call LWP functions for non-blocking GTK2 gui:

#! /usr/bin/perl -w

package main;

use strict;
use warnings;
use URI::Heuristic;
use threads;
use Gtk2 qw/-init -threads-init/;
use Glib qw/TRUE FALSE/;

die "Glib::Object thread safetly failed" 
    unless Glib::Object->set_threadsafe(TRUE);
    
my $raw_url;
my $url;

my $window = Gtk2::Window->new('toplevel');
$window->signal_connect('delete_event' => sub 
                        {
                            my @joinable = threads->list(threads::joinable);
                            while(my $thr = shift @joinable)
                            {
                                $thr->join;
                            }

                            my @running = threads->list(threads::running);
                            while(my $thr = shift @running)
                            {
                                $thr->kill('KILL')->detach();
                            }

                            Gtk2->main_quit; 
                        });

my $vbox = Gtk2::VBox->new(FALSE, 4);
$window->add($vbox);

my $label = new Gtk2::Label;
$vbox->pack_start($label, 0, 0, 4);

my $entry = new Gtk2::Entry;
$vbox->pack_start($entry, 0, 0, 4);

my $go = Gtk2::Button->new('_Go');
$vbox->pack_start($go, 0, 0, 4);
$go->signal_connect(clicked => sub 
                    { 
                        $go->set_sensitive(FALSE);
                        $raw_url = $entry->get_text;
                        $url = URI::Heuristic::uf_urlstr($raw_url);
                        my $worker = Worker->new($url, $label);
                        my $timer = Glib::Timeout->add(100, sub
                                                       {
                                                           if($worker->{child}->is_joinable())
                                                           {
                                                               $worker->{child}->join;
                                                               print "Thread ".$worker->{child}->tid." was 
joined.\n";
                                                               $go->set_sensitive(TRUE);
                                                               return FALSE;
                                                           }
                                                           else
                                                           {
                                                               return TRUE;
                                                           }
                                                       }
                            );
                    }
                    );

$window->show_all();

Gtk2->main();

package Worker;

use strict;
use warnings;
use threads;
use LWP::Simple;
use LWP::UserAgent;
use Glib qw(TRUE FALSE);

sub new
{
    my $class = shift;
    my ($url, $label) = @_;
    
    my $self = ();

    $self->{url} = $url;
    $self->{label} = $label;

    bless $self, $class;

    $self->{child} = threads->new(\&_worker_thread, $self);
        
    return $self;
}

sub _worker_thread
{
    my $self = shift;

    my $thread = threads->self;
    print "Thread ".$thread->tid." started.\n";

    my $res;

    my $url = $self->{url};
    my $label = $self->{label};

    my $ua = LWP::UserAgent->new
        (
         agent => "Test UserAgent",
         keep_alive => 1,
         env_proxy => 1,
         timeout => 10,
     );

    $thread->yield();

    my $header = $ua->head($url);
    
    if($header->is_error())
    {
        $res = "Error: ".$header->status_line."\n";
    }
    else
    {
        $res = $header->headers_as_string."\n";
    }
        
    Gtk2::Gdk::Threads->enter;
    $label->set_text($res);
    Gtk2::Gdk::Threads->leave;

    print "Thread ".$thread->tid." ended\n";
}

It works well, but when I create the Worker thread twice:

#! /usr/bin/perl -w

package main;

use strict;
use warnings;
use URI::Heuristic;
use threads;
use Gtk2 qw/-init -threads-init/;
use Glib qw/TRUE FALSE/;

die "Glib::Object thread safetly failed" 
    unless Glib::Object->set_threadsafe(TRUE);

my $raw_url;
my $raw_url2;
my $url;
my $url2;

my $window = Gtk2::Window->new('toplevel');
$window->signal_connect('delete_event' => sub 
                        {
                            my @joinable = threads->list(threads::joinable);
                            while(my $thr = shift @joinable)
                            {
                                $thr->join;
                            }

                            my @running = threads->list(threads::running);
                            while(my $thr = shift @running)
                            {
                                $thr->kill('KILL')->detach();
                            }

                            Gtk2->main_quit; 
                        });

my $vbox = Gtk2::VBox->new(FALSE, 4);
$window->add($vbox);

my $label = new Gtk2::Label;
$vbox->pack_start($label, 0, 0, 4);

my $entry = new Gtk2::Entry;
$vbox->pack_start($entry, 0, 0, 4);

my $go = Gtk2::Button->new('_Go');
$vbox->pack_start($go, 0, 0, 4);
$go->signal_connect(clicked => sub 
                    { 
                        $go->set_sensitive(FALSE);

                        $raw_url = $entry->get_text;

                        $url = URI::Heuristic::uf_urlstr($raw_url);

                        my $worker = Worker->new($url, $label);

                        my $timer = Glib::Timeout->add(100, sub
                                                       {
                                                           if($worker->{child}->is_joinable())
                                                           {
                                                               $worker->{child}->join;
                                                               print "Thread ".$worker->{child}->tid." was 
joined.\n";
                                                               $go->set_sensitive(TRUE);
                                                               return FALSE;
                                                           }
                                                           else
                                                           {
                                                               return TRUE;
                                                           }
                                                       }
                            );
                    }
                    );

my $label2 = new Gtk2::Label;
$vbox->pack_start($label2, 0, 0, 0);

my $entry2 = new Gtk2::Entry;
$vbox->pack_start($entry2, 0, 0, 0);

my $go2 = Gtk2::Button->new('_Go2');
$vbox->pack_start($go2, 0, 0, 0);
$go2->signal_connect(clicked => sub 
                    { 
                        $go2->set_sensitive(FALSE);

                        $raw_url2 = $entry2->get_text;

                        $url2 = URI::Heuristic::uf_urlstr($raw_url2);

                        my $worker2 = Worker->new($url2, $label2);

                        my $timer2 = Glib::Timeout->add(100, sub
                                                       {
                                                           if($worker2->{child}->is_joinable())
                                                           {
                                                               $worker2->{child}->join;
                                                               print "Thread ".$worker2->{child}->tid." was 
joined.\n";
                                                               $go2->set_sensitive(TRUE);                     
                                         
                                                               return FALSE;
                                                           }
                                                           else
                                                           {
                                                               return TRUE;
                                                           }
                                                       }
                            );
                    }
                    );

$window->show_all();

Gtk2->main();

package Worker;

use strict;
use warnings;
use threads;
use LWP::Simple;
use LWP::UserAgent;
use Glib qw(TRUE FALSE);

sub new
{
    my $class = shift;
    my ($url, $label) = @_;
        
    my $self;

    $self->{url} = $url;
    $self->{label} = $label;

    bless $self, $class;

    $self->{child} = threads->new(\&_worker_thread, $self);
        
    return $self;
}

sub _worker_thread
{
    my $self = shift;
    
    my $thread = threads->self;
    print "Thread ".$thread->tid." started.\n";

    my $res;

    my $url = $self->{url};
    my $label = $self->{label};

    $thread->yield();

    my $ua = LWP::UserAgent->new
        (
         agent => "Test UserAgent",
         keep_alive => 1,
         env_proxy => 1,
         timeout => 10,
     );

    my $header = $ua->head($url);
    
    if($header->is_error())
    {
        $res = "Error: ".$header->status_line."\n";
    }
    else
    {
        $res = $header->headers_as_string."\n";
    }
        
    Gtk2::Gdk::Threads->enter;
    $label->set_text($res);
    Gtk2::Gdk::Threads->leave;

    print "Thread ".$thread->tid." ended.\n";
}

It was crashed when I click the two "Go" buttons alternately:

Thread 1 started.
Thread 1 ended.
Thread 1 was joined.
Thread 2 started.
Thread 2 ended.
Thread 2 was joined.
Thread 3 started.
Thread 3 ended.
Thread 3 was joined.
Thread 4 started.
Thread 4 ended.
Thread 4 was joined.
Thread 5 started.
Thread 5 ended.
GLib-GObject-WARNING **: instance with invalid (NULL) class pointer
during global destruction.
GLib-GObject-CRITICAL **: g_signal_emit_valist: assertion
`G_TYPE_CHECK_INSTANCE (instance)' failed during global destruction.
GLib-GObject-WARNING **: instance with invalid (NULL) class pointer
during global destruction.
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion
`G_TYPE_CHECK_INSTANCE (instance)' failed during global destruction.
GLib-GObject-WARNING **: instance with invalid (NULL) class pointer
during global destruction.
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion
`G_TYPE_CHECK_INSTANCE (instance)' failed during global destruction.
Scalars leaked: -27
Thread 5 was joined.


How can I re-create thread in GTK2-Perl? Thanks all.

-- 
Hominid He <hominidhe 163 com>





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