Log in

No account? Create an account
entries friends calendar profile Previous Previous Next Next
This I have discovered recently - Ed's journal
This I have discovered recently
So, I've been fiddling around with Perl, and threading.
One of the things that's been bugging me, is that when I've tried to do a 'return' from a perl subroutine, it's not worked - and I couldn't for the life of me figure out why.
What's _supposed_ to happen, is that you do 'thread -> join' to join the thread (once it's finished running) and that's supposed to capture the return result.

Why it wasn't working is thanks to this little snippet in the documentation (Yes, RTFM, I know. But to be fair, I wasn't looking for it in _that_ bit).

"The context (void, scalar or list) for the return value(s) for "->join()" is determined at the time of thread creation."

Perhaps I'd better backtrack a little though - I mean, anyone who's not really 'into' perl, might not have a clue what I'm talking about when I say 'context'.
So I shall summarise.

Perl is quite clever - it has two 'real' variable types - scalar - which contains anything that's a single value (So any string, integer, float, character, reference). And array (or list) - which is a group of zero or more scalars.

The clever bit is that it can figure out what you mean, by the context in which you do it - a brief illustration (lj cut to avoid breaking formatting).


use strict;
use warnings;

sub context_test
  print "Called in a void context\n" unless defined wantarray();
  if ( wantarray() )
    return ( "Called in", " an array", " context" );
    return "Called in a scalar context";


my $scalar_result = &context_test();
print $scalar_result;
print "\n";

my @array_result = &context_test();
print join (":", @array_result),"\n";

#And for bonus points, what does _this_ do?
print &context_test();
print "\n";

What's happening is the rather clever function 'wantarray' is being used to tell the call context of the subroutine 'wantarray' is undefined if it's a void context (the result is discarded). True if a list/scalar context and false if in a scalar context.

As for why this is useful - consider if you do something like 'grep' - a Unix command to find 'matches' against text patterns. If you do it in a 'list' context, having a list of the lines it found would be useful. If you do it in a scalar context, then having a number of matches is probably more useful (0 being 'false' you could do 'if grep("pattern", @text_block)' for example)

So anyway - the context of a threaded subroutine is set when the thread is _created_.
Which means you need to do something like:

foreach my $thing ( @list_of_things )
  my $thread = threads -> create ( \&thing_processing_sub, $thing );

On the face of it, you immediately discard '$thread' because it drifts out of scope (and it does). However, it also means your thread is created in a scalar context, so any results it returns will be scalar.
If you _don't_ do this, it'll be in a void context, and any return is discarded. Which was what was tripping me up.

And you will then be able to do:

foreach my $thread ( threads -> list() )
  my $result = $thread -> join();
  print $thread -> tid(), " returned: ", $result,"\n";

Which won't work if you don't start the thread in a scalar context.

Tags: ,

3 comments or Leave a comment
warmage From: warmage Date: July 19th, 2012 02:02 am (UTC) (Link)
ok so when you start the thread 1) how do you instantiate with a vector context and 2)how do you get a call that the thread completed? These things I know how to do in Object Pascal, but haven't dealt with Object Perl so much.
sobrique From: sobrique Date: July 23rd, 2012 12:28 pm (UTC) (Link)
If I recall correctly, 'vector' in Pascal maps to array (or list) in perl.
These are denoted by the sigil '@'.

So you can do
my @thread = threads -> create ( \&thread_sub, ....); 

Or alternatively - because a list is a group of scalars - this should work too:
my ( $thread ) = threads -> create ( \&thread_sub,... );

Brackets around it means 'this is a list' - in this case, one with a single element (anything else 'fed into it' would be discarded).

Collecting the result of the thread, you can do:
my $result = $thread_id -> join(); 

my @result = $thread_id -> join(); 

for array context.

I tend to use a construct like:
foreach my $thread ( threads -> list(threads::joinable) )
  my $result = $thread -> join(); 

You can use the method 'list' without argument - It'll return a list of all running threads, and you can then call 'is_running()' or 'is_joinable()' on each if that's preferable.
I think perhaps this is worth a longer post though.
warmage From: warmage Date: July 23rd, 2012 12:49 pm (UTC) (Link)
Nope, no need to go deeper, that's enough for me to see where Perl is laying down its particular flavor. I haven't done any real backend stuff (Perl or otherwise) in about 10 years and have gone pretty much into webapp and browser coding for the stuff I do. Thanks :)
3 comments or Leave a comment