in reply to Win32::MMF + threads misbehavior
I just knocked up a crude trace monitor based around a threaded, tcp server receiving trace information from DB::DB based, per thread clients.
Here's a sample of trace from 2 copies of a multi-threaded test app I had kicking around that are each running 100 threads:
# hires timestamp pid tid script lineno package subro +utine args 1144302961.37707 3168( 38) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.29688 3216( 41) threadtest.pl( 29) (eval): n/a 1144302961.29714 3216( 41) threadtest.pl( 29) (eval): n/a 1144302961.29730 3216( 41) threadtest.pl( 29) (eval): n/a 1144302961.29743 3216( 41) threadtest.pl( 29) (eval): n/a 1144302961.29756 3216( 41) threadtest.pl( 29) (eval): n/a 1144302961.29769 3216( 41) threadtest.pl( 16) Win32::Console::Write +Char: n/a 1144302961.29782 3216( 41) threadtest.pl( 16) Win32::Console::Write +Char: n/a 1144302961.29795 3216( 41) threadtest.pl( 16) Win32::Console::Write +Char: n/a 1144302961.29818 3216( 41) threadtest.pl( 29) (eval): n/a 1144302961.35938 3216( 0) ( ) : n/a 1144302961.35980 3216( 0) ( ) : n/a 1144302961.40625 3216( 42) threadtest.pl( 29) (eval): n/a 1144302961.40652 3216( 42) threadtest.pl( 29) (eval): n/a 1144302961.40668 3216( 42) threadtest.pl( 29) (eval): n/a 1144302961.40681 3216( 42) threadtest.pl( 29) (eval): n/a 1144302961.40694 3216( 42) threadtest.pl( 29) (eval): n/a 1144302961.40706 3216( 42) threadtest.pl( 16) Win32::Console::Write +Char: n/a 1144302961.40719 3216( 42) threadtest.pl( 16) Win32::Console::Write +Char: n/a 1144302961.40732 3216( 42) threadtest.pl( 16) Win32::Console::Write +Char: n/a 1144302961.40754 3216( 42) threadtest.pl( 29) (eval): n/a 1144302961.49839 3168( 38) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.49881 3168( 38) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.49897 3168( 38) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50139 3168( 28) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50156 3168( 28) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50169 3168( 28) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50000 3168( 31) threadtest.pl( 29) (eval): n/a 1144302961.50016 3168( 31) threadtest.pl( 29) (eval): n/a 1144302961.50028 3168( 31) threadtest.pl( 29) (eval): n/a 1144302961.50041 3168( 31) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50054 3168( 31) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50066 3168( 31) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50000 3168( 7) threadtest.pl( 29) (eval): n/a 1144302961.50016 3168( 7) threadtest.pl( 29) (eval): n/a 1144302961.50028 3168( 7) threadtest.pl( 29) (eval): n/a 1144302961.50041 3168( 7) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50054 3168( 7) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50066 3168( 7) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50000 3168( 15) threadtest.pl( 29) (eval): n/a 1144302961.50016 3168( 15) threadtest.pl( 29) (eval): n/a 1144302961.50028 3168( 15) threadtest.pl( 29) (eval): n/a 1144302961.50041 3168( 15) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50053 3168( 15) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.50065 3168( 15) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.48975 3168( 6) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.48991 3168( 6) threadtest.pl( 19) Win32::Console::Write +Char: n/a 1144302961.49124 3168( 14) threadtest.pl( 19) Win32::Console::Write +Char: n/a
The 'n/a' is meant to be the subroutine args, but I haven't worked out how to obtain those yet.
You invoke the clients in the usual debugger fashion:
tperl -d:Ttrace threadtest.pl
This is the debug client module(crude):
package Devel::Ttrace; use strict; use warnings; use Time::HiRes qw[ time ]; require IO::Socket::INET; require threads; my $socket = IO::Socket::INET->new( 'localhost:54321' ); sub DB::DB { no warnings 'uninitialized'; my @caller = caller(1); printf $socket "%15.5f %5d(%3d) %s(%5s) %s: %s\n", time(), $$, threads->tid, @caller[ 1, 2, 3 ], 'n/a' ; } 1;
I ran the server, monitor.pl in another console session and just dumped the output from the cetral queue to the screen to produce the above output. You cold modify the Worker thread to
#!perl -slw use strict; use threads; use Thread::Queue; use Smart::Comments::Lite '0'; sub worker { require IO::Socket::INET; my $tid = threads->tid; our $running:shared; our $busy:shared; our $die:shared; my( $Qwork, $Qtrace ) = @_; ++$running; while( my $fno = $Qwork->dequeue() ) { ##1 warn "$tid($fno): Client waiting on $fno\n"; my $client = IO::Socket::INET->new; $client->fdopen( $fno, '+>' ) or die "$tid: Failed to reopen fileno: $fno"; ##2 warn "$tid($fno): reopened $client\n"; ++$busy; ##2 warn "$tid($fno): Reading from client\n"; while( my $data = <$client> ) { chomp $data; ##2 warn "$tid($fno): Got '$data'\n"; $Qtrace->enqueue( $data ); } ##2 warn "$tid($fno): Client disconnected\n"; close $client; shutdown( $client, 2 ); --$busy; } --$running; } our $START ||= 2; our $MAX ||= 2000; our $running:shared = 0; our $busy:shared = 0; our $die:shared = 0; our $connects:shared=0; ## Use ctrl-break to terminate the server. local $SIG{INT} = sub{ warn "SIGINT(2) terminating"; $die = 1; }; my $Qwork = new Thread::Queue; my $Qtrace = new Thread::Queue; our %clients; my @threads = map{ threads->create( \&worker, $Qwork, $Qtrace ) or warn( "Create thread $_ failed with $^E\n" ), (); } 1 .. $START; ## This creates new threads if the current pool is running low async{ until( $die ) { if( ( $busy + 2 ) == $running and $running < $MAX ) { push @threads, threads->create( \&worker, $Qwork, $Qtrace +) or warn( "Create thread $_ failed with $^E\n" ), (); } sleep 1; } }; ## This reads and dumps the trace information to the screen. async{ until( $die ) { print $Qtrace->dequeue(); } } require IO::Socket::INET; my $server = IO::Socket::INET->new( LocalPort => 54321, Listen => 1000, Reuse => 1. ) or die $!, $^E; ## Currently, the client sockets are not cleaned up and will eventuall +y run out of resource. ## Fixes welcome :) my $fno; while( not $die and ( my $client = $server->accept ) > 0 ) { ++$connects; $clients{ $client->fileno } = $client; $Qwork->enqueue( $client->fileno ); } warn "\a*** Accept failed ***\a\n" and $die = 1; close $server; $Qwork->enqueue( undef ) for 1 .. $running; sleep 1 while $running; $_->join for @threads;
Smart::Comments::Lite is my doctored version of the theDamian's CPAN tool. Comment out the use line and it will disable it completely.
This was hacked together from bits of existing code in about one hour. It imposes very little load on the programs being traced and produced sequenced information that ought to make working out where your application is disappearing up it own navel fairly easy. Redirecting the output to a file, (via tee might be good), would allow you to get a permanent record of the sequence and timing of events that lead up to the problem.
HTH.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^2: Win32::MMF + threads misbehavior
by renodino (Curate) on Apr 06, 2006 at 06:46 UTC | |
by BrowserUk (Patriarch) on Apr 06, 2006 at 07:17 UTC | |
by renodino (Curate) on Apr 06, 2006 at 15:30 UTC |