Re: Threads breaking either Perl's or Gtk2's internals



Fine, thanks for such a prompt reply! I'll have a look at your code now.

I'm already proceeding with a similar scheme, where all worker threads are created before any Gtk stuff and instructed via queues to eval strings (the code in not known in advance). I was just hoping that perhaps the development leaped ahead. :)

Btw, earlier I found one of your scripts with a similar approach (attached as thr.pl), but that also gives me a nasty error on exit:

dave jeeves:~$ perl thr.pl
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'.
GLib-GObject-CRITICAL **: g_signal_emit_valist: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'.
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'.
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'.
GLib-GObject-CRITICAL **: g_signal_emit_valist: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'.
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'.
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
GLib-GObject-CRITICAL **: g_object_unref: assertion `G_IS_OBJECT (object)' failed.
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `(null)'.
GLib-GObject-CRITICAL **: g_signal_emit_valist: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `(null)'.
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
GLib-GObject-WARNING **: instance of invalid non-instantiatable type `(null)'.
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed.
Segmentation fault


A bit unsettling as you create the thread before Gtk2->main, which seems to be considered quite safe. :)

The only threaded script I found working 100% so far is thread_usage.pl from some Gtk2-perl tutorial. Interesting is that it seems to create threads at the same point like you (after Gtk2 widget stuff, before Gtk2->main) and it works. It even touches the GUI from other threads. All this makes me a bit nervous about Gtk-perl. Looks like a lot of depends on "unrelated" conditions. My app is gonna have a lot of IPC and threads and I'm seriously thinking about moving to good old Gtk/C, which would mean MUCH more code, time, testing... :(

-- 
David Kubíček
System Specialist

T-Systems Czech Republic a.s.
Na Karmeli 1457
Mladá Boleslav
29301, Czech Republic

Mobile: (+420) 739 242 055
Phone:  (+420) 236 099 459
PGP:    http://awk.cz/key.pgp
        http://awk.cz/key.pem
Email:  kubicek gedas cz
        david kubicek t-systems com
        kubicek awk cz


Connection to localhost closed by remote host.


zentara wrote:
On Mon, 24 Mar 2008 14:47:05 +0100
David Kubíček <dave awk cz> wrote:

  
Hello,

I'm having problems with threads in my GTK application. You can see exactly what I'm doing from the attached files. Both were originally a testing script I found here when browsing archives, so please ignore formatting, etc - they're not mine. :) I
    

Hi, I get the same results as you running your scripts. I have 2 examples to show you.

The first, is almost like yours, and the thing you will notice, is how you need to turn off
thread safety (with explanation by muppet), AND you need to use timers and Idle->add
to manipulate the $pbar from the thread. 

Threads can be very tricky. If you want foolproof thread operation, you need to make
reusable(I call them sleeping threads). I have one in the second example below.
In the third example, I control 3 sleeping threads.
These all run well, and you might want to adapt the sleeping thread model. It is possible
that you may be able to spawn threads dynamically, and destroy(detach) them when done,
but almost every attempt to do that (which I've seen) fails with errors after a few invocations.

So, threads can be used safely, but at the current state of development, you need
to reuse threads and control them thru shared variables. Don't try to make "disposible
detached threads". They may seem to work, but you will often get a memory gain for
each thread, and they fail eventually with repeated calls.

GoodLuck, remember to use Timeouts instead of sleep, and use Glib::Idle->add() from
your thread code block to manipulate widgets in the main thread.

zentara


  

_______________________________________________ gtk-perl-list mailing list gtk-perl-list gnome org http://mail.gnome.org/mailman/listinfo/gtk-perl-list
#!/usr/bin/perl
use warnings;
use strict;
use threads;
use threads::shared;
use Glib qw/TRUE FALSE/;
use Gtk2 qw/-init -threads-init/;

#setup shared hash
my %shash;
share(%shash); #will work for first level keys
$shash{'go'} = 0;
$shash{'work'} = '';
$shash{'die'} = 0;

my $window = Gtk2::Window->new('toplevel');
$window ->signal_connect( 'destroy' => \&delete_event );
$window->set_border_width(10);
$window->set_size_request(300,300);

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

my $hbox= Gtk2::HBox->new( FALSE, 6 );
my $hbox1 = Gtk2::HBox->new( FALSE, 6 );
$vbox->pack_end($hbox,FALSE,FALSE,0);
$vbox->pack_end (Gtk2::HSeparator->new, FALSE, FALSE, 0);
$vbox->pack_end($hbox1,FALSE,FALSE,0);
$hbox->set_border_width(2);
$vbox->pack_end (Gtk2::HSeparator->new, FALSE, FALSE, 0);

my $ebutton = Gtk2::Button->new_from_stock('gtk-quit');
$hbox->pack_end( $ebutton, FALSE, FALSE, 0 );
$ebutton->signal_connect( clicked => \&delete_event );

my $pbar = Gtk2::ProgressBar->new();
$pbar->set_pulse_step(.1);
$hbox->pack_start($pbar,1,1,0);

my $count = 0;
my $label_w_markup = Gtk2::Label->new();
$label_w_markup->set_markup("<span foreground=\"yellow1\" 
  size=\"40000\">$count</span>");

$vbox->pack_end($label_w_markup,FALSE,FALSE,4); 

my $tbutton = Gtk2::Button->new_with_label('Run Thread');
 $hbox1->pack_start($tbutton , 1, 1, 0 );
my $lconnect = $tbutton->signal_connect( clicked => sub{ launch() });
my $sconnect;

$window->show_all();
$pbar->hide;   #needs to be called after show_all

#create 1 sleeping thread passing it the label and pbar to control
my $thread = threads->new(\&work, $label_w_markup, $pbar);

Gtk2->main;
######################################
sub delete_event {
  $shash{'go'} = 0;
  $shash{'die'} = 1;
  $thread->join;
  Gtk2->main_quit;
  return FALSE;
}
#######################################
sub launch{
   $pbar->show;
   $tbutton->set_label('Stop Thread');
   $tbutton->signal_handler_block($lconnect);
   $sconnect = $tbutton->signal_connect( clicked => sub{ stop() });
   $shash{'go'} = 1;
}
##################################################
sub stop{
   print "stopped\n";
   $shash{'go'} = 0;
   $pbar->hide;
   $tbutton->set_label('Run Thread');
   $tbutton->signal_handler_block ($sconnect);
   $tbutton->signal_handler_unblock ($lconnect);
}
#########################################################
sub work{
    
    my ($label,$pbar) = @_;
    $|++; 
    while(1){
       if($shash{'die'} == 1){ goto END }; 
      
       if ( $shash{'go'} == 1 ){

         foreach my $num (1..1000){
                     
                 Glib::Idle->add(
                   sub{
                    if($shash{'die'} == 1){ return };
                    $label->set_markup("<span foreground=\"yellow1\" 
                    size=\"40000\">$num</span>");
                    $pbar->pulse;
                    return FALSE;
                   });
                
                select(undef,undef,undef, .1);

            if($shash{'go'} == 0){last}
            if($shash{'die'} == 1){ goto END }; 
           }
       
           $shash{'go'} = 0; #turn off self before returning      
       }else
         { select(undef,undef,undef,.1) } #sleep time
    }
END:
}
#!/usr/bin/perl

#
# $Header: /cvsroot/gtk2-perl/gtk2-perl-xs/Gtk2/examples/thread_usage.pl,v 1.4 2004/06/23 01:00:15 rwmcfa1 
Exp $
#
# -rm
#

use strict;
use warnings;
use Data::Dumper;

use Glib qw(TRUE FALSE);
use Gtk2 qw/-init -threads-init 1.050/;

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

my $win = Gtk2::Window->new;
$win->signal_connect (destroy => sub { Gtk2->main_quit; });
$win->set_title ($0);
$win->set_border_width (6);
$win->set_default_size (640, 480);

my $hbox = Gtk2::HBox->new (FALSE, 6);
$win->add ($hbox);

my $vbox = Gtk2::VBox->new (FALSE, 6);
$hbox->pack_start ($vbox, FALSE, FALSE, 0);

my $worklog = Log->new;
$hbox->pack_start ($worklog, TRUE, TRUE, 0);

my @workers;
my $worker;
foreach (1..5)
{
        $worker = Worker->new ($worklog);
        $vbox->pack_start ($worker, FALSE, FALSE, 0);
        $worker->set_worker_label ('Worker '.$_);
        push @workers, $worker;
}

my $pending = Gtk2::Label->new ('0 jobs pending');
$vbox->pack_start ($pending, FALSE, FALSE, 0);
Glib::Timeout->add (500, sub {
                $pending->set_text (Worker->jobs_pending.' jobs pending');
                1;
        });

my $count = 0;

my $go = Gtk2::Button->new ('_Go');
$vbox->pack_start ($go, FALSE, FALSE, 0);
$go->signal_connect (clicked => sub {
                foreach (@workers)
                {
                        Worker->do_job ($count + rand);
                        $count++;
                }
        });

my $quit = Gtk2::Button->new_from_stock ('gtk-quit');
$vbox->pack_start ($quit, FALSE, FALSE, 0);
$quit->signal_connect (clicked => sub { 
                $go->set_sensitive (FALSE);
                $quit->set_sensitive (FALSE);
                Worker->all_fired;
                Gtk2->main_quit;
        });

$win->show_all;
Gtk2->main;

package Worker;

use strict;
use warnings;
use Data::Dumper;

use threads;
use threads::shared;
use Thread::Queue;

use Glib qw(TRUE FALSE);

use base 'Gtk2::HBox';

our $_nworkers : shared = 0;
my $_jobs;

BEGIN
{
        $_jobs = Thread::Queue->new;
}

sub do_job
{
        shift; # class

        $_jobs->enqueue (shift);
}

sub all_fired
{
        shift; # class

        # put on a quit command for each worker
        foreach (1..$_nworkers)
        {
                $_jobs->enqueue (undef);
        }
        while ($_nworkers)
        {
                Gtk2->main_iteration;
        }
}

sub jobs_pending
{
        return $_jobs->pending;
}

sub new
{
        my $class = shift;
        my $worklog = shift;

        my $self = Gtk2::HBox->new (FALSE, 6);

        # rebless to a worker
        bless $self, $class;

        # gui section
        
        my $label = Gtk2::Label->new ('Worker:');
        $self->pack_start ($label, FALSE, FALSE, 0);

        my $progress = Gtk2::ProgressBar->new;
        $self->pack_start ($progress, FALSE, FALSE, 0);
        $progress->set_text ('Idle');

        $self->{label} = $label;
        $self->{progress} = $progress;
        $self->{worklog} = $worklog;
        
        # thread section

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

        $_nworkers++;
        
        return $self;
}

sub set_worker_label
{
        my $self = shift;
        my $name = shift;

        $self->{label}->set_text ($name);
}

sub _worker_thread
{
        my $self = shift;

        my $progress = $self->{progress};
        my $worklog = $self->{worklog};

        my $i;
        my $job;
        my $sleep;
        # undef job means quit
        while (defined ($job = $_jobs->dequeue))
        {
                $worklog->insert_msg ($self->{label}->get_text
                                      ." is doing job ($job)\n");
                if (rand > 0.5)
                {
                        $sleep = 1 + rand;
                }
                else
                {
                        $sleep = 1 - rand;
                }
                for ($i = 0; $i < 1.1; $i += 0.25)
                {
                        Gtk2::Gdk::Threads->enter;
                        $progress->set_fraction ($i);
                        $progress->set_text ($i * 100 .'%');
                        Gtk2::Gdk::Threads->leave;
                        # we're state employee's, so let's do some 'work'...
                        sleep $sleep;
                }
                $worklog->insert_msg ($self->{label}->get_text
                                      ." done with job ($job)\n");
        }

        $_nworkers--;
}

package Log;

use strict;
use warnings;

use Glib qw(TRUE FALSE);

use base 'Gtk2::ScrolledWindow';

sub new
{
        my $class = shift;

        my $self = Gtk2::ScrolledWindow->new;
        
        my $buffer = Gtk2::TextBuffer->new;

        my $view = Gtk2::TextView->new_with_buffer ($buffer);
        $self->add ($view);
        $view->set (editable => FALSE, cursor_visible => FALSE);

        $self->{view} = $view;
        $self->{buffer} = $buffer;
        
        bless $self, $class;
        
        $self->insert_msg ("Start...\n-------------------------------------\n");
        
        return $self;
}
  
sub insert_msg
{
        my $self = shift;
        my $msg = shift;

        my $buffer = $self->{buffer};
        
        Gtk2::Gdk::Threads->enter;
        my $iter = $buffer->get_end_iter;
        $buffer->insert ($iter, $msg);
        $iter = $buffer->get_end_iter;
        $self->{view}->scroll_to_iter ($iter, 0.0, FALSE, 0.0, 0.0);
        Gtk2::Gdk::Threads->leave;
}

Attachment: signature.asc
Description: OpenPGP digital signature



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