Re: RAII in Perl6/Parrot
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 describ
Re: RAII in Perl6/Parrot
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
Re: RAII in Perl6/Parrot
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
Re: RAII in Perl6/Parrot
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
Re: RAII in Perl6/Parrot
Larry Wall wrote: ... (Perl 6 provides several ways that are much handier than try/finally, and just about as handy as RAII.) But baking such handicaps into every object merely guarantees it will not scale well in the real world. Thanks for the information. I must admit I rely heavily on the deterministic nature of C++ for writing "real-time" feed collectors into stock exchanges. RAII plays a big part in managing locks between threads and tearing down sockets cleanly (as well as many other things). 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? Best regards, Blair
Re: RAII in Perl6/Parrot
On Mon, Dec 18, 2006 at 12:02:35PM +, Blair Sutton wrote: : Dear all : I hope I am sending this to the correct place. : I regularly use the RAII idiom in Perl 5 and C++ to automatically clean : up resources during object destruction. : I recently read a mail thread "Is RAII possible in Python?" at : http://www.thescripts.com/forum/thread25072.html and "Perl vs Python - : GC and Reference Counting" at : http://discuss.joelonsoftware.com/default.asp?joel.3.323675.32. : The above threads suggests that Python cannot do RAII since its garbage : collection does not offer deterministic finalization. I understand that : Parrot will not use reference counting for garbage collection and so may : suffer from the same problem. Are my fears warranted? Short answer: absolutely. Long answer: emphatically not. I believe you have a bit of an XY problem here. You're wanting transactional security and timely destruction but have predisposed yourself not to accept them unless they happen to look like deterministic reference counting. Perl 6 offers the former without offering you the latter, because Perl 6 must run on a variety of platforms that may or may not support deterministic reference counting semantics easily. Perl 6 is a language, not an implementation. As such, it officially Doesn't Care what Parrot does about GC under the hood. But the problem goes much deeper than that, and has to do with the necessary virtualization of time as we scale things up and distribute responsibility for them. In this day of multiple, distributed processing units of uncertain proximity, it's going to get harder and harder to say what a deterministic reference is. What if a bit your program is running on a different processor and it goes down? You can't know that till the next GC run at the appropriate granularity fails to sync with the down processor. A GC solution can finesse around such problems, up to a point, whereas a deterministic solution really has only one approach. In the larger and wider scale of things, you can't know whether a reference is valid till you try it--see the Web for an example. The web would never have take off with perfect accountability. (That's why Xanadu didn't work, basically.) All of computing is getting more like that. For instance, people are now finding that deterministic locking solutions don't scale up either. STM (software transactional memory) seems to be a way to get around that, and that's partly because a transactional memory manager can virtualize time much like a garbage collector can defer decisions it isn't sure of. Two transactions can be in conflict in real time, which would prevent at least one of them from happening under deterministic locking. But an STM manager can decide the fate of the transactions based *only* on the data dependencies, ignoring the bogus realtime dependency, and maybe decide to allow both transactions if a consistent state is maintained. In short, programs written in languages that require unnecessary determinism will not escape the von Neumann bottleneck as easily as programs written in languages that do not. Determinism is fine when you need it, and a good language will provide adequate ways to express that when you do need it. (Perl 6 provides several ways that are much handier than try/finally, and just about as handy as RAII.) But baking such handicaps into every object merely guarantees it will not scale well in the real world. The real world itself avoids computing deterministically most of the time! And if anything could manage determinism, it'd probably be the real world! Languages that require determinism will scale only up only when the entire world is running one huge completely entangled quantum computer. Determinism is not interoperable. Anyway, that's the trend I see. And that's why Perl 6 has lots of ways to promise you Don't Care about various dependencies, but Do Care about others. That's why Perl 6 has pipes and lazy lists, junctions and hyperoperators, contend and defer. These are for building scalable solutions to run on cell processors and GPUs and clusters of servers and corporate intranets. And maybe even trigger a Singularity or two. :-) Larry