http://www.perlmonks.org?node_id=648665

Saladino has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to stop a capture thread from another thread breaking the $capobjects loop, for that I need to share that variable, but it fails with the share: thread failed to start: Invalid value for shared scalar at test.pl line 22. (open_live line)
use threads; use threads::shared; use Net::Pcap; my $capobj:shared = (); my $capthread = threads->new(\&capture); sleep 3; Net::Pcap::breakloop($capobj); sub capture { my $err; my $dev = Net::Pcap::lookupdev(\&err); my ($address,$netmask); Net::Pcap::lookupnet($dev,\$address,\$netmask,\$err); my $filter; $capobj = Net::Pcap::open_live($dev,1024,0,0,\$err); Net::Pcap::compile($capobj,\$filter,'host XX.XX.XX.XX && port +7777 && udp',1,$netmask); Net::Pcap::setfilter($capobj,$filter); Net::Pcap::loop($capobj,0,\&capture_callback, 0); Net::Pcap::close($capobj); } sub capture_callback { .... }
Thank you for your time

Replies are listed 'Best First'.
Re: Shared variables between threads
by zentara (Archbishop) on Nov 02, 2007 at 16:21 UTC
    It's very difficult to pass objects thru threads. Your best bet is to isolate the PCap object in one worker thread, then control it from a main thread with shared variables. See Re: Passing objects between threads...any solutions? (shared objects) for another idea. You may luck out, and seem to get a shared object to work, but it may fail later under heavy testing or on another machine.

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Shared variables between threads
by BrowserUk (Patriarch) on Nov 02, 2007 at 17:29 UTC

    From a quick look at the Net::Pcap source, the return value of open_live() isn't a 'object', at least not in the Perlish sense of a blessed reference. It is the value returned from a direct call to the pcap c-library function:

    pcap_t * pcap_open_live(device, snaplen, promisc, to_ms, err) ... RETVAL = pcap_open_live(device, snaplen, promisc, to_ms, errbuf);

    That looks like a c struct address to me. If that is the case, and that is what is ending up back in your Perl program, it ought to look like an unsigned integer to Perl. And if that were true, it is hard to see why Perl wouldn't let you assign it to a shared scalar.

    Would you try assigning it to a non-shared variable, printing it and post the output here?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      This is the value of $capobj just after the assignation.
      pcap_tPtr=SCALAR(0x8a97fd4)

        From that it seems that you are receiving a bless scalar reference. Older versions of threads shared did not allow you to assign bless references (objects) to shared variables for technical reasons.

        A recent change by jdhedden alleviated this restriction, but (unless you've already done so) you will need to download the latest versions from CPAN to be able to do this:

        print $threads::VERSION, $threads::shared::VERSION;; 1.67 1.14

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Shared variables between threads
by renodino (Curate) on Nov 02, 2007 at 15:50 UTC
    My hunch is that Net::Pcap::open_live returns a ref of some sort, and its not threads::shared. In order to assign a ref to a shared variable, the ref must point to a shared variable.

    So you're pretty much screwed unless you write your own threaded version of Net::Pcap.


    Perl Contrarian & SQL fanboy
Re: Shared variables between threads
by NetWallah (Canon) on Nov 02, 2007 at 16:08 UTC
    It is not clear why you need the pcap object to be SHARED, since your code shows no locking around it anyway, and only one thread appears to be active.

    My suggestion would be to create the my $capobj = Net::Pcap::open_live($dev,1024,0,0,\$err); as a not-explicitly-shared object in the main code, then start the &capture thread - either passing it the object, or letting it access the global (thread copy of the) object.

    You still retain active control in the main code, and, presumably, can still interrupt the capture by calling "breakloop", since you retain the (original) $capobj.

         "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom

      Well, The posted version is a simpler than the original, i do things at the main thread. In fact that is the key, because i need to finalize the capture thread just after finishing those things (changed by the "sleep 3" in the example).
      If i understand correctly the thread system in perl all not explicitly shared variables are copied in the child thread.
      So, if i open_live before splitting my child will obtain a copy of that descriptor. I don't know how can that affect to my script (closing or using a copied descriptor).
      I've tried it and breakloop should force the loop exit but instead it does nothing because breakloop returns correctly but loop inside the child thread never returns.