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

I've posted a sample working app of the issue I'm facing below: In summary I'm trying to pass a blessed object resulting from a call to the Net::Twitter constructor to my worker thread but keep encountering the following error:
Invalid value for shared scalar at thread_test.pl line 77.
use threads; use threads::shared; use Thread::Queue; use strict; use Wx; my %thread_hash; share(%thread_hash); use Net::Twitter; package MyApp; use base 'Wx::App'; sub OnInit { my $self = shift; my $frame = MyFrame->new(); $frame->Show(1); $self->SetTopWindow($frame); return 1; } package MyFrame; use base 'Wx::Frame'; use Wx qw(wxTE_MULTILINE wxVERTICAL wxID_DEFAULT); use Wx::Event qw(EVT_COMMAND EVT_CLOSE EVT_BUTTON); my $done_event : shared = Wx::NewEventType; sub new { my $class = shift; my $self = $class->SUPER::new(undef, -1, "Testing Threads"); $self->{text} = Wx::TextCtrl->new($self, -1, "", [-1,-1], [300, 30 +0], wxTE_MULTILINE); $self->{button} = Wx::Button->new($self, -1, "&Run Test"); my $sizer = Wx::BoxSizer->new(wxVERTICAL); $sizer->Add($self->{text}); $sizer->Add($self->{button}); $self->SetSizer($sizer); $self->{text}->AppendText("test\n"); EVT_COMMAND($self, -1, $done_event, \&done); EVT_CLOSE($self, \&on_close); EVT_BUTTON($self,$self->{button}, \&on_button ); my $sendqueue = Thread::Queue->new(); my $worker = threads->create(\&worker, $self, $sendqueue ); $self->{queue} = $sendqueue; $self->{worker} = $worker; InitTwit(); $self->Fit; return $self; } sub InitTwit { my $user = 'twitter'; my $pass = 'twit_password'; my $twit = Net::Twitter->new( username => $user, password => $pass ); $thread_hash{'twit'} = $twit; } sub on_button { my ($self, $event) = @_; $self->{queue}->enqueue('GO'); } sub on_close { my ($self, $event) = @_; $self->{queue}->enqueue('STOP'); $self->{worker}->join; $event->Skip(1); } sub done { my ($self, $event) = @_; my $text = $event->GetData; $self->{text}->AppendText("$text\n"); } sub worker { my($self, $event) = @_; while (my $message = $event->dequeue ) { return 1 if $message eq 'STOP'; eval { my $result = $thread_hash{'twit'}->update('Hello, world!'); }; if ( my $err = $@ ) { die $@ unless blessed $err && $err->isa('Net::Twitter::Error +'); warn "HTTP Response Code: ", $err->code, "\n", "HTTP Message......: ", $err->message, "\n", "Twitter error.....: ", $err->error, "\n"; } my ($text) = "Updated posted successfuly\n"; my $response : shared = $text; print "$response\n"; my $thread_event = Wx::PlThreadEvent->new(-1, $done_event, $re +sponse); Wx::PostEvent($self, $thread_event); } } package main; MyApp->new->MainLoop;

Any thoughts? I know the easiest solution is to simply call the constructor in the thread but at this point I really would like to understand why it isn't working.
Thanks again, Paul

Replies are listed 'Best First'.
Re: passing shared blessed object part 2
by BrowserUk (Patriarch) on Nov 24, 2009 at 18:27 UTC

    Line 77 is:

    $thread_hash{'twit'} = $twit;

    What about 808994 don't you understand?


    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.
      oh sorry, I should have posted the error I got from trying that. Here it is:
      Can't locate object method "share" via package "Class::MOP::Class::__A +NON__::SER IAL::2" at thread_test.pl line 77.

        Try:

        $thread_hash{'twit'} = share( $twit );

        If that doesn't work, you'll probably need a Moose guru to sort it out.


        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: passing shared blessed object part 2
by stvn (Monsignor) on Nov 25, 2009 at 01:22 UTC

    I am not sure about the specifics of your errors, but the biggest issue I am seeing is that %thread_hash is declared in the main:: namespace and you are accessing is as if it was a local variable inside the MyFrame:: namespace. Try changing

    $thread_hash{'twit'} = $twit;
    to
    $main::thread_hash{'twit'} = $twit;
    and see if that helps. Beyond that I am not really sure what is happening here since I don't have a threaded perl to test this on. I suggest taking this to the moose mailing list (moose at perl.org) or the #moose IRC channel, someone there might be more familiar with Moose and threads.

    -stvn
      thanks guys, I tried both suggestions and still a similar error. At this point I just want to understand why it's not working :(

      -Paul

        Moose did (and perhaps still does) have issues with threads (see this RT bug), but it coredumped and was related to the regexpr that we used to parse the types. It is quite possible Moose has other issues with threads and no one else has encountered them yet. I personally never use Perl threads and I know very few people who do as they are notoriously broken/buggy/problematic. You might want to give Net::Twitter::Lite a try, it is a non-Moose version of Net::Twitter so would at least remove the Moose component from the issue (since I am pretty sure from your code you don't really care about the Moose-ness and are only interested in making this work).

        -stvn
Re: passing shared blessed object part 2
by Anonymous Monk on Nov 24, 2009 at 20:21 UTC
    Any moose experts here?

      One thought crossed my mind (but I'm not in a position to test it).

      I believe that the newer versions of threads::shared override bless in order to enable the new shared blessed refs facility. And whilst I have no direct knowledge that it does, it wouldn't surprise me if Moose or Class::MOP did the same thing for their own purposes. That could well account for (at least some) of the errors you see when you include both.

      It might be worth trying avoiding the conflict by invoking threads::shared::share() directly, rather than importing it. So something like:

      use threads::shared (); ... $thread_hash{'twit'} = threads::shared::share( $twit ); ...

      might provide a work around for the problems.


      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.
        I believe that the newer versions of threads::shared override bless in order to enable the new shared blessed refs facility. And whilst I have no direct knowledge that it does, it wouldn't surprise me if Moose or Class::MOP did the same thing for their own purposes. That could well account for (at least some) of the errors you see when you include both.

        Nope, Moose/MOP does not (and never will) override global behavior like that. The fact that threads::shared does means that might be the source of the problem right there.

        -stvn
Re: passing shared blessed object part 2
by zentara (Cardinal) on Nov 25, 2009 at 17:48 UTC
    I lost my "bless"ing. might have a clue

    .... i just hack at these things... but the first thing i would try is avoid the intermediary $twit, directly put it into the shared hash

    $thread_hash{'twit'} = Net::Twitter->new( username => $user, password => $pass );

    .... another trick is to confine all your Twitter stuff to the thread.... is there really a need to keep a global twitter object?..... just put all the twitter stuff into the thread....

    ....with a sleeping thread model, as in Reusable threads demo .... and here is a Tk example with threads....notice the thread is created before any GUI code is invoked...... this goes along way toward making threaded GUI's work

    #!/usr/bin/perl use strict; use Tk; use threads; use threads::shared; my $data_out:shared = ''; my $data_in:shared = ''; my $thread_die = 0; my $wthr = threads->new( \&update_thread )->detach; #create Tk stuff my $mw = MainWindow->new( -background => 'black', -foreground => 'yellow', -title => "Thread Test" ); $mw->geometry("600x400"); $mw->minsize( 400, 400 ); $mw->maxsize( 800, 800 ); my $sent_text = $mw->Scrolled('Text', -height => 10, -width => 60, -background => 'black', -foreground => 'yellow' )->pack( -side => 'bottom', -anchor => 's', -pady => 2 ); my $received_text = $mw->Scrolled( "Text", -height => 10, -width => 60, -background => 'white', -foreground => 'black', )->pack(); my $repeater = $mw->repeat(1000, sub{ $data_out++; $sent_text->insert( 'end', "Sent $data_out\n" ); $sent_text->see('end'); $received_text->insert( 'end', "Received $data_in\n" ); $received_text->see('end'); }); $mw->Button(-text=> 'Exit', -command => sub{ $thread_die = 1; $repeater->cancel; $mw->withdraw; kill 9, $$; })->pack; MainLoop; ######################################################### #the non-Tk worker code sub update_thread { print "update_thread called...\n"; while (1) { if($thread_die == 1){return} $data_in = 'thread-processing'.$data_out; sleep 1; } }

    GUI's are notorious for having problems with threads..... another no-no with gui's is you can't manipulate the GUI (access widgets) in the main thread from the spawned thread..... Gtk2 has made a great improvement in this aspect, by the GLib::Idle_add method, which you can call from a spawned thread..... google for "glib idle_add" if interested..... Wx may have the idle_add method, since many systems use Gtk2 as the base for Wx.... but not always.


    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku