My intention to ask was.. How can i use shared things like file handles in XS modules, when CLONE/DESTROY don't provide a clean way for destruction in the case that i showed in the example above? I wrote some XS modules to make Perl faster and smaller. I'm using this modules in serveral programs, but it doesn't work right. MY_CXT makes no sense when i have a file handle or socket handle in it, which must be closed. It seems that Perl doesn't work right with threads. I decided to go away from it. Bummer!
| [reply] |
| [reply] |
Try the following.
Make each shared object keep track of how much it is used. In CLONE, increment the usage of each object that the thread will actually use. In DESTROY, decrement the usage of each object that the thread incremented. The key is that you have to decrement all of the things that you increment.
This should work much, much better than having each thread increment everything, then only decrement itself. Which is what your code currently does.
That said, I would personally advocate avoiding using threads unless you absolutely need to. Threads are always a huge complication, and I'm a big advocate of trying the simplest thing that could possibly work.
| [reply] |
| [reply] |
That said, I would personally advocate avoiding using threads unless you absolutely need to.
Most of my programs include server and other working threads.
Make each shared object keep track of how much it is used. In CLONE, increment the usage of each object that the thread will actually use. In DESTROY, decrement the usage of each object that the thread incremented. The key is that you have to decrement all of the things that you increment.
I tried it out, but it doesn't seem to be working better.
Threads are always a huge complication
Looks like you are right. The code below could work better. But... either Perl crashes or threads don't finish anymore. Very strange.
#!/usr/local/bin/perl
use threads;
use Time::HiRes qw/usleep/;
while( 1 ) {
my $class = Class->new();
threads->create( \&thread_sub, $class, 0 );
usleep 200_000;
}
sub thread_sub {
my( $class, $level ) = @_;
print "THREAD ", threads->self->tid, ", $level running\n";
threads->create( \&thread_sub, $class, $level + 1 ) unless $level;
usleep 400_000;
print "THREAD ", threads->self->tid, ", $level finished\n";
threads->self->detach;
}
1;
package Class;
use threads::shared;
our %MY_CXT : shared = ();
our $CXT_ID : shared = 0;
sub new {
my $class = shift;
my $this = {};
$this->{'shared'} = &share( {} ),
$this->{'shared'}{'refcnt'} = 1;
$this->{'shared'}{'tid'} = threads->self->tid;
$this->{'shared'}{'clone'} = 1;
lock( %MY_CXT );
$this->{'id'} = ++ $CXT_ID;
$MY_CXT{$this->{'id'}} = $this->{'shared'};
bless $this, $class;
}
sub CLONE {
lock( %MY_CXT );
while( my( $k, $v ) = each %MY_CXT ) {
# do not CLONE anymore when it's destroyed in the main thread
$v->{'refcnt'} ++ if $v->{'clone'};
print "CLONE: id $k, refcnt $v->{'refcnt'}\n";
}
}
sub DESTROY {
my $this = shift;
my $shared = $this->{'shared'};
if( $shared->{'tid'} == threads->self->tid ) {
# disable futher CLONEs
$shared->{'clone'} = 0;
# refcnt must decrement twice ?
$shared->{'refcnt'} --;
}
$shared->{'refcnt'} --;
print "DESTROY: id $this->{'id'}, refcnt $shared->{'refcnt'}\n";
if( $shared->{'refcnt'} <= 0 ) {
lock( %MY_CXT );
# should not happen
warn "already destroyed!!!" unless $MY_CXT{$this->{'id'}};
delete $MY_CXT{$this->{'id'}};
}
}
1;
| [reply] [d/l] |