1#!/usr/bin/perl 2 3# 4# $Id$ 5# 6# -rm 7# 8 9use strict; 10use warnings; 11use Data::Dumper; 12 13use Glib qw(TRUE FALSE); 14use Gtk2 qw/-init -threads-init 1.050/; 15 16die "Glib::Object thread safetly failed" 17 unless Glib::Object->set_threadsafe (TRUE); 18 19my $win = Gtk2::Window->new; 20$win->signal_connect (destroy => sub { Gtk2->main_quit; }); 21$win->set_title ($0); 22$win->set_border_width (6); 23$win->set_default_size (640, 480); 24 25my $hbox = Gtk2::HBox->new (FALSE, 6); 26$win->add ($hbox); 27 28my $vbox = Gtk2::VBox->new (FALSE, 6); 29$hbox->pack_start ($vbox, FALSE, FALSE, 0); 30 31my $worklog = Log->new; 32$hbox->pack_start ($worklog, TRUE, TRUE, 0); 33 34my @workers; 35my $worker; 36foreach (1..5) 37{ 38 $worker = Worker->new ($worklog); 39 $vbox->pack_start ($worker, FALSE, FALSE, 0); 40 $worker->set_worker_label ('Worker '.$_); 41 push @workers, $worker; 42} 43 44my $pending = Gtk2::Label->new ('0 jobs pending'); 45$vbox->pack_start ($pending, FALSE, FALSE, 0); 46Glib::Timeout->add (500, sub { 47 $pending->set_text (Worker->jobs_pending.' jobs pending'); 48 1; 49 }); 50 51my $count = 0; 52 53my $go = Gtk2::Button->new ('_Go'); 54$vbox->pack_start ($go, FALSE, FALSE, 0); 55$go->signal_connect (clicked => sub { 56 foreach (@workers) 57 { 58 Worker->do_job ($count + rand); 59 $count++; 60 } 61 }); 62 63my $quit = Gtk2::Button->new_from_stock ('gtk-quit'); 64$vbox->pack_start ($quit, FALSE, FALSE, 0); 65$quit->signal_connect (clicked => sub { 66 $go->set_sensitive (FALSE); 67 $quit->set_sensitive (FALSE); 68 Worker->all_fired; 69 Gtk2->main_quit; 70 }); 71 72$win->show_all; 73Gtk2->main; 74 75package Worker; 76 77use strict; 78use warnings; 79use Data::Dumper; 80 81use threads; 82use threads::shared; 83use Thread::Queue; 84 85use Glib qw(TRUE FALSE); 86 87use base 'Gtk2::HBox'; 88 89our $_nworkers : shared = 0; 90my $_jobs; 91 92BEGIN 93{ 94 $_jobs = Thread::Queue->new; 95} 96 97sub do_job 98{ 99 shift; # class 100 101 $_jobs->enqueue (shift); 102} 103 104sub all_fired 105{ 106 shift; # class 107 108 # put on a quit command for each worker 109 foreach (1..$_nworkers) 110 { 111 $_jobs->enqueue (undef); 112 } 113 while ($_nworkers) 114 { 115 Gtk2->main_iteration; 116 } 117} 118 119sub jobs_pending 120{ 121 return $_jobs->pending; 122} 123 124sub new 125{ 126 my $class = shift; 127 my $worklog = shift; 128 129 my $self = Gtk2::HBox->new (FALSE, 6); 130 131 # rebless to a worker 132 bless $self, $class; 133 134 # gui section 135 136 my $label = Gtk2::Label->new ('Worker:'); 137 $self->pack_start ($label, FALSE, FALSE, 0); 138 139 my $progress = Gtk2::ProgressBar->new; 140 $self->pack_start ($progress, FALSE, FALSE, 0); 141 $progress->set_text ('Idle'); 142 143 $self->{label} = $label; 144 $self->{progress} = $progress; 145 $self->{worklog} = $worklog; 146 147 # thread section 148 149 $self->{child} = threads->new (\&_worker_thread, $self); 150 151 $_nworkers++; 152 153 return $self; 154} 155 156sub set_worker_label 157{ 158 my $self = shift; 159 my $name = shift; 160 161 $self->{label}->set_text ($name); 162} 163 164sub _worker_thread 165{ 166 my $self = shift; 167 168 my $progress = $self->{progress}; 169 my $worklog = $self->{worklog}; 170 171 my $i; 172 my $job; 173 my $sleep; 174 # undef job means quit 175 while (defined ($job = $_jobs->dequeue)) 176 { 177 $worklog->insert_msg ($self->{label}->get_text 178 ." is doing job ($job)\n"); 179 if (rand > 0.5) 180 { 181 $sleep = 1 + rand; 182 } 183 else 184 { 185 $sleep = 1 - rand; 186 } 187 for ($i = 0; $i < 1.1; $i += 0.25) 188 { 189 Gtk2::Gdk::Threads->enter; 190 $progress->set_fraction ($i); 191 $progress->set_text ($i * 100 .'%'); 192 Gtk2::Gdk::Threads->leave; 193 # we're state employee's, so let's do some 'work'... 194 sleep $sleep; 195 } 196 $worklog->insert_msg ($self->{label}->get_text 197 ." done with job ($job)\n"); 198 } 199 200 $_nworkers--; 201} 202 203package Log; 204 205use strict; 206use warnings; 207 208use Glib qw(TRUE FALSE); 209 210use base 'Gtk2::ScrolledWindow'; 211 212sub new 213{ 214 my $class = shift; 215 216 my $self = Gtk2::ScrolledWindow->new; 217 218 my $buffer = Gtk2::TextBuffer->new; 219 220 my $view = Gtk2::TextView->new_with_buffer ($buffer); 221 $self->add ($view); 222 $view->set (editable => FALSE, cursor_visible => FALSE); 223 224 $self->{view} = $view; 225 $self->{buffer} = $buffer; 226 227 bless $self, $class; 228 229 $self->insert_msg ("Start...\n-------------------------------------\n"); 230 231 return $self; 232} 233 234sub insert_msg 235{ 236 my $self = shift; 237 my $msg = shift; 238 239 my $buffer = $self->{buffer}; 240 241 Gtk2::Gdk::Threads->enter; 242 my $iter = $buffer->get_end_iter; 243 $buffer->insert ($iter, $msg); 244 $iter = $buffer->get_end_iter; 245 $self->{view}->scroll_to_iter ($iter, 0.0, FALSE, 0.0, 0.0); 246 Gtk2::Gdk::Threads->leave; 247} 248