Sorry some zealous re-factoring screwed my earlier script. The objects should have been created outside the push stack. I.e.

my $a = MyObj->new('zippy', 3);
my $b = MyObj->new('biggles', 5);

my $s = new Thread::Semaphore;
my @B :shared;
push @B, $a;
push @B, $b;

...

Then the destruction does happen at the end of the program. I.e.

Creating 'zippy' with 3 lives
Creating 'biggles' with 5 lives
T1: starting
T1: 'biggles' has 5 lives
T1: Remove from list
T2: starting
T2: 'biggles' has 4 lives
T2: Remove from list
T3: starting
T3: 'biggles' has 3 lives
T3: Remove from list
T1: 'biggles' has 2 lives
T1: Remove from list
T2: 'biggles' has 1 lives
T2: Remove from list
T3: 'biggles' has 0 lives
T1: 'zippy' has 3 lives
T1: Remove from list
T2: 'zippy' has 2 lives
T2: Remove from list
T3: 'zippy' has 1 lives
T3: Remove from list
T1: 'zippy' has 0 lives
T2: Exiting
T3: Exiting
T1: Exiting
All threads joined
Destroying 'biggles' with 0 lives
Destroying 'zippy' with 0 lives

[from the mad hatter: please ignore previous message about object being destroyed in wrong place]

Blair Sutton wrote:
Actually this script displays: -

Creating 'zippy' with 3 lives
Destroying 'zippy' with 3 lives
Creating 'biggles' with 5 lives
Destroying 'biggles' with 5 lives
T1: starting
T1: 'biggles' has 5 lives
T1: Remove from list
T2: starting
T2: 'biggles' has 4 lives
T2: Remove from list
T3: starting
T3: 'biggles' has 3 lives
T3: Remove from list
T1: 'biggles' has 2 lives
T1: Remove from list
T2: 'biggles' has 1 lives
T2: Remove from list
T3: 'biggles' has 0 lives
T1: 'zippy' has 3 lives
T1: Remove from list
T2: 'zippy' has 2 lives
T2: Remove from list
T3: 'zippy' has 1 lives
T3: Remove from list
T1: 'zippy' has 0 lives
T2: Exiting
T3: Exiting
T1: Exiting

This shows the objects being DESTROYed before they should be, i.e. the last instance being destroyed in the relevant thread. Maybe P6 can cure this?

Blair Sutton wrote:
Here is a script in Perl 5 with a multi-threaded RAII idiom I use quite a lot (In P5 & C++). I am not sure how threads will be implemented in P6 (but hopefully one will have a *choice* of copying all variables into your private namespace!).Will the LEAVE closure trait take the previous role of P5's DESTROY or will it always be executed when one falls out of scope?

use strict;
use warnings;

use threads;
use threads::shared;
use Thread::Semaphore;

{
   package MyObj;

   sub new {
       my ($this, $name, $lives) = @_;
       my $self = &threads::shared::share({});
       $self->{name} = $name;
       $self->{lives} = $lives;
       $self->{tid} = threads->tid();
       print "Creating '$name' with $lives lives\n";
       bless $self;
   }

   sub DESTROY {
       my $self = shift;
       if (threads->tid() == $self->{tid}) {
print "Destroying '$self->{name}' with $self->{lives} lives\n";
       }
   }
}

my $s = new Thread::Semaphore;
my @B :shared;
push @B, MyObj->new('zippy', 3);
push @B, MyObj->new('biggles', 5);


my $t1 = threads->new(\&worker, 'T1');
my $t2 = threads->new(\&worker, 'T2');
my $t3 = threads->new(\&worker, 'T3');

$t1->join();
$t2->join();
$t3->join();

exit;

sub worker {
   my $name = shift;
   print "$name: starting\n";
   while (1) {
       $s->down;
       if ([EMAIL PROTECTED]) {
           print "$name: Exiting\n";
           $s->up;
           return;
       }
       my $o = pop @B;
       print "$name: '$o->{name}' has $o->{lives} lives\n";
       if ($o->{lives} != 0) {
           print "$name: Remove from list\n";
           $o->{lives}--;
           push @B, $o;
       }
       $s->up;
       sleep 1;
   }
}

__DATA__

Blair

Luke Palmer wrote:
On 12/18/06, Blair Sutton <[EMAIL PROTECTED]> wrote:
I agree entirely that not all objects need this capability but some
certainly do. That is, the capability to execute code once every
reference of an object has been removed. Could you point to, or give an
example of the Perl 6 way for doing something similar?

Well, there is one general method I can think of.  You need to know
more about your program in order to implement it; i.e. if an object
which contains an array of objects, one element of which has a socket
that needs closing, it's going to be tough to get it right.  That's
one of the negative sides of the trade-off Larry mentioned (but he
made an excellent case for the positive sides, and the reason why
we're leaning the nondeterministic way).

The main tool at your disposal is the LEAVE closure trait.  i.e.:

   sub foo {
       do_stuff();
       do_more_stuff();
       LEAVE { say "leaving foo" }
   }

This code will say "leaving foo" whenever its dynamic scope ends,
whether that time is the successful completion of do_more_stuff or
whether do_stuff threw an exception.  If I understand the idiom
correctly, this exceptional case was the main reason behind RAII.  Do
your cleanup in the LEAVE block.

If you have a specific scenario that comes up a lot, other than "the
exact semantics of RAII", do describe it and that will give us
something to think about.  Either there's already some clever trick
you can do to handle it, there's a little extra language feature we'll
have to add, or you'll have to put up with doing a little more manual
bookkeeping.

Luke




Reply via email to