Re: sharing objects

[email protected] (Dean Arnold) Tue, 10 Oct 2006 09:56:53 -0700
Newsgroups perl.ithreads
Message-ID <[email protected]>
Christopher Fowler wrote:
> On Tue, 2006-10-10 at 09:23 -0500, Michael J. Pomraning wrote:
>> Perhaps pass an unblest reference through perl's prefab Thread::Queue
>> class?
> 
> That should be the best way.  Is there any great way to share objects
> between threads?
> 
> 

Sharing objects between threads is simple: just make sure that you
use a shared array or hash for your object, and only assign either
simple scalars or shared refs to its members. Then you can pass
the objects between threads via a simple Thread::Queue object.

I've done a quick rewrite of your example (note I have no idea what
the function of your script is, but the Packet class is a pretty simple container
class, so hopefully this captures what you're attempting).

Some major issues I've noticed:

1) using a `host $addr` to do resolution may be a problem, since
you're shelling out from a thread...I'm not certain how well that's going to work.
I've noted the use of gethostbyaddr() as an alternate (tho I don't know how thread
safe it is either)

2) You never waited to join() your threads; I don't know what effect that
might have on your exit condition

3) The way you setup your object sharing was a bit obtuse; I've simplified it

4) I've replaced your @obj ad-hoc queue with Thread::Queue, which handles
the needed locking  (which it appears you weren't applying)

Code below. NOTE: I've not run it, it likely has some syntax errors.

HTH,
Dean Arnold
Presicient Corp.


package Packet;

use threads;
use threads::shared;
use strict;
use warnings;

sub new {
	my ($class, $v1, $v2) = @_;
#
#	NOTE: this assume that the packet and timestamp
#	elements are simple scalars; if not, then you'll
#	need more share()'ing to coerce them into something
#	that can be assigned to a shared hash
#
	my %pkt : shared = (
		packet => $v1,
		timestamp => $v2
	);
	return bless \%pkt, $class;
}

sub get_timestamp {
	my $self = shift;
#
#	FYI/Warning: I've encountered some instances where
#	derefencing an element off a shared hash would
#	silently discard the returned value, and had to do
#	an intermediate assignment to a lexical variable
#	before using the value; I've not yet determined
#	the cause of the issue yet
#
	return $self->{'timestamp'};
}

sub get_packet {
	my $self = shift;
#
#	see caveat above
#
	return $self->{'packet'};
}

1;

package main;

use Net::PcapUtils;
use NetPacket::Ethernet;
use NetPacket::IP;
use NetPacket::TCP;
use threads;
use threads::shared;
use Thread::Queue;
use Data::Dumper;

use strict;
use warnings;

$| = 1;
my $prog = "tcp[13] = 2 and src net not 192.168.2";
#
#	create a thread queue
#
my $q = Thread::Queue->new();

sub get_timestamp {
	my $ti = shift;
	$ti = time() unless $ti;
	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
		localtime($ti);

	my $text = sprintf("%02d-%02d %02d:%02d:%02d",
		($mon+1), $mday, $hour, $min, $sec);
	return $text;

}

sub now {
	return time();
}

sub resolv {
	my $addr = shift;
#
#	OUCH! this may never work properly...forking from a thread
#	may lead to chaos, esp. on Win32...isn't there a better
#	Perlish solution (e.g., get host by addr ?
#
#	my $host = gethostbyaddr($addr, AF_INET);
#
	my $host = `host $addr`;
	chomp $host;

	return $addr if $host =~ m/not found/;
	return $addr unless $host =~ m/pointer\s(.+?)\.$/;

	$addr = $1;
	return $addr;

}

sub process_thread {
	while(1) {

#
#	wait for a packet (or for 'DIE')
#
		my $pktobj = $q->dequeue();
#
#	if not a ref, then it must be 'DIE'
#
		return 1
			unless ref $pktobj;
#
# Get the packet off the list and unpack the time
#
		my $pkt = $pktobj->get_packet();
		my $ti = $pktobj->get_timestamp();

# get the source IP adrress
		my $src_ip = NetPacket::IP->decode(
			NetPacket::Ethernet::strip($pkt))->{src_ip};

# I want incoming
		next if $src_ip =~ m/209\.168\.246\.233/;

		my $pt = NetPacket::TCP->decode(
			 NetPacket::IP::strip(
				 NetPacket::Ethernet::strip($pkt)));

		print get_timestamp($ti)." SYN ".resolv($src_ip)."($src_ip) -> $pt->{'dest_port'}\n";
	}
}

sub catch {
	my ($arg,$hdr,$pkt) = @_ ;
#
#	enqueue the packet for processing
#
	$q->enqueue(Packet->new($pkt, now()));
}

#
#	spawn the thread
#
my $pthr = threads->new(\&process_thread);
#
#	start snooping
#
Net::PcapUtils::loop(\&catch, FILTER=> $prog);
#
#	I don't know what your exit condition is ???
#	but here's how I've instrumented cleanup
#	NOTE that if you start >1 thread, you'll need
#	to send multiple DIE's, and join all the threads
#
$q->enqueue('DIE');
$pthr->join();