Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Re: Problem with Threaded Socket Server

by BrowserUk (Pope)
on Aug 18, 2013 at 20:19 UTC ( #1049941=note: print w/ replies, xml ) Need Help??


in reply to Problem with Threaded Socket Server

If i detach the threads or join them, when they are done it crashes my serial connection to the arduino board.

You do not close the connection to your USB device before returning from your thread proc, so the symptoms above lead me to the conclusion that when Perl tries to garbage collect your thread procs -- ie. when you either join them; or immediately when you return if you detach them; it tries to DESTROY the local copy of the usb device handle $local_arduino. And that closes your connection.

You can confirm this speculation if you enable DEBUG on the device handle and you see a debug message of the form:

"Destroying $self->{NAME}" at ... etc

You might be able to work around Device::SerialPort's lack of thread-awareness by overriding its DESTROY method:

sub DESTROY { my $self = shift; return unless (defined $self->{NAME}); if ($self->{"_DEBUG"}) { carp "Destroying $self->{NAME}"; } $self->close; }

And preventing it from closing the connection unless it is the last instance of the handle; or perhaps, only if the DESTROY method is being called from the same thread as created the connection. How you would arrange to know either of these things is down to your ingenuity.

Alternatively, and more likely to work, would be to have a single thread only communicate with your USB device.

In this scenario, you start a thread which creates the connection to the USB device; and pass it a queue handle at start up. That thread the loops reading that Q waiting for command requests from other threads; enacts them; and then returns the results to the requesting threads (via a queue handle they pass along with the command request).

Your USB handling thread would look something like this:

sub USB { my $Q = shift; my $arduino = Device::SerialPort->new("/dev/serial/by-id/usb-Ardui +no__www.arduino.cc__Arduino_Mega_2560_74136333033351011152-if00"); if ( defined( $arduino ) ) { $arduino->baudrate(9600); $arduino->parity("none"); $arduino->databits(8); $arduino->stopbits(1); $arduino->read_char_time(0); $arduino->read_const_time(500); $arduino->write_settings || undef $arduino; writelog( "server:[portSet]" ); } else { writelog( "server:[portSet]: failed" ); } while( my $request = $Q->dequeue ) { my $retQ = $Q->dequeue; $arduino->write($cmd); my $answer = getLineFromArduino() $retQ->enqueue( $answer ); } }

To make requests, your client threads then do:

sub client { my $client = shift; my $usbQ = shift; my $retQ = new Thread::Queue; ... my $command = <$client>; $usbQ->enqueue( $command, $retQ ); my $result = $retQ->dequeue; print $client $result; ... }

Season to taste with logging and error handling.


With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
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.


Comment on Re: Problem with Threaded Socket Server
Select or Download Code
Re^2: Problem with Threaded Socket Server
by piece (Novice) on Aug 18, 2013 at 20:34 UTC
    Just like you assumed:

    Destroying /dev/serial/by-id/usb-Arduino__www.arduino.cc__Arduino_Mega +_2560_74136333033351011152-if00 at ./roll_server.pl line 0 thread 1

    I will try to implement your suggestions. Though it could take a while with my perl knowledge :)

    Is it somehow possible to declare the $arduino object as shared, so the garba collection avoids it?
    This
    my $arduino : shared;
    does not work.

    So the idea is to create a new Device::SerialPort class which inherits the original class and overrides the destructor, right?
      Is it somehow possible to declare the $arduino object as shared, so the garba collection avoids it?

      Not directly no. Each thread gets its own copy (actually not a fully copy, but a proxy to the real thing) of each shared variable; and that proxy is local to the thread and must be destroyed before the thread ends. In many cases, the appropriate action is to do nothing in the destroy unless this is the last copy of the handle.

      In theory, there is a mechanism whereby module authors can make their modules thread-aware and possible thread-safe, by taking control of the cloning process by defining sub CLONE{ .. } in their packages; but the mechanism is barely described and I've never seen an example of anyone using it; so I cannot comment further on it. Besides which you'd either have to get the author of D::SP interested; or make your own changes.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      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.

      Just saw your update:

      So the idea is to create a new Device::SerialPort class which inherits the original class and overrides the destructor, right?

      That is the essence of the first of the two possibilities I offered. And you could go the full subclassing route if that's your thing. Personally, I wouldn't.

      I'd just inject my override directly into the D::SP namespace. Ie. At the top of your program somewhere after you've used Device::SerialPort, define a subroutine:

      sub Device::SerialPort::DESTROY { my $self = shift; return unless (defined $self->{NAME}); if ($self->{"_DEBUG"}) { carp "Destroying $self->{NAME}"; } #$self->close; }

      You'll get a "subroutine redefined" warning -- which you can disable -- but the affect will be the same as 'doing it properly' without the hassle.

      Simply commenting out the #$self->close; may be enough. If not, then you will either have to investigate why not; or try my second suggestion.

      Don't forget to call close() in the same thread as you created the USB connection before finishing.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      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.
        Ok, tried it properly with a new class which inherits DEVICE::SerialPort and the direct way above. Both have the same effect:
        *** Error in `/usr/bin/perl': corrupted double-linked list: 0x00000000 +0173d840 ***
        or
        Segmentation fault (core dumped)

        The error messages differ on the crashes. Seems the garbage collection still wants to clean up something and messes it up.

        Found this. Sadly i don't understand it and don't know if this may be of any help.

        Tomorrow i'll try your second suggestion. Thx alot so far.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1049941]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (5)
As of 2014-09-21 04:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (166 votes), past polls