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