Proper Way to Provide Variable to Objects below the present calling subroutine? - multithreading

In my code base, I have several variables that "live" in the main namespace and various modules I utilize can expect to always find in main (for example, $main::author is a reference to a hash about the user, $main::dbh is the open database handle, $main::loader is an object of a core utility class and $main::FORM has the processed QUERY_STRING). E.g. when the program starts up:
$main::author = &getAuthor;
$main::loader = SAFARI::Loader->new;
use SAFARI::View;
my $view = SAFARI::View->new;
$view->output;
And then when I'm in SAFARI::View::output, I can call on those core variables, e.g.:
# Display user name:
$output .= 'Hello, ' . $main::author->{'fullName'} . '! Welcome!';
The problem: when the code is running in a threaded environment, one thread may need a different $loader object or have a different $author logged in than the other thread. More pressingly still, each thread has, of course, a different database handle.
I know I could pass along the core information when creating objects such as View by adding parameters it takes. But that means every type of object that presently can just reference these items in the main namespace must have a lengthy list of parameters instead. I'm trying to think of the most efficient and "safe" way to solve this problem.
I've considered creating a hash reference that has all the different bits in it within each thread, e.g. $common that has $common->{'FORM'}, $common->{'loader'}, $common->{'author'}, etc., and then pass those as a single argument to each object:
my $common = #Logic to set up this combination of bits for this particular thread.
my $view = SAFARI::View->new({ 'common' => $common });
my $article = SAFARI::Article->new({ 'common' => $common });
my $category = SAFARI::Category->new({ 'common' => $common });
That's not too tedious, but it still seems inefficient; it'd be preferable if the "environment" of just that thread could contain something that object within in it could access. As far as I can tell, declaring our $common within the subroutine that is run by the thread would do this and any objects created within that subroutine would have access to that variable. Is there any harm in this approach?
It seems like it would be cleaner to have these items in some sort of namespace, but if I refer to, say, $SAFARI::common, that name space would reach across threads just like main does. Would having $SAFARI::common but then declaring a local variant of it in each thread be reasonable?
Is there a "best practice" for what I'm trying to do? It's going to take some significant reworking of code to fix this one way or another, so I'd really like to get it "right."

This is a complex question, with multiple major components.
For starters, there is a question of how to pass an initial data-structure to threads so that they can use it, but without it being shared.
In Perl, when threads are created the existing data are copied to each thread. That's why it's often a good idea to create threads right up front, before there's much data in the program, to avoid those threads being bloated. The copied data is not shared, by default, in Perl.
So you can just create your initial $common structure in the main:: and then create threads, and each will get its own copy of it. Then threads can create their own SAFARI:: objects and do as they please with the data structure. A simple demo
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd pp);
use threads;
my $ds = { val => 10, ra => [ 10..12 ] };
my #threads = map { async(\&proc_ds, $ds) } 1..3;
$_->join() for #threads;
say "main: ", pp $ds;
sub proc_ds {
my ($ds) = #_;
# Modify data in the thread
$ds->{val} += 10 * threads->tid;
$_ += $ds->{val} for #{$ds->{ra}};
say "Thread ", threads->tid, ": ", pp $ds;
}
This prints
Thread 1: { ra => [30, 31, 32], val => 20 }
Thread 2: { ra => [40, 41, 42], val => 30 }
Thread 3: { ra => [50, 51, 52], val => 40 }
main: { ra => [10, 11, 12], val => 10 }
If, instead, you need to share data structures perhaps see this page, for example.
Each thread then needs to use a class hierarchy, whereby multiple subclasses should be initialized the same way using a common data structure modified in each thread.†
In Perl's inheritance model, methods gets inherited from the parent class but not data. So the question here is how to nicely populate all subclasses to same data.
There are advanced techniques that may make the process more elegant, but I'd suggest to simply introduce the attribute and define the method in the parent class and then have all subclasses use it to initialize. That is going to be crystal clear, and as economical as anything else. Like
use SAFARI::View;
use SAFARI::Other;
# ... set/customize $common
my ($sview_obj, $sother_obj) =
map { $_->new->init_common($common) }
qw(SAFARI::View SAFARI::Other);
say "View object: $sview_obj"; # overload "" (quotes) for this, see below
This would be done in each thread, where $common is first customized per thread as needed.
There isn't a magical way for the derived classes to pick up data from the parent, and you don't want a base class to have to know about its derived classes, in principle. And there is nothing wrongjk with nicely instantiating all subclasses, much like in the question itself.
The sub init_common need only be defined in SAFARI, the parent class
SAFARI.pm file
package SAFARI;
use warnings;
use strict;
sub new {
my ($class, #args) = #_;
my $self = {
common => {}, # introduce the attribute, for clarity
# ...
};
return bless $self, $class;
}
sub init_common {
my ($self, $data) = #_;
$self->{common}->{dbh} = $data->{dbh}; # etc, populate common
return $self;
}
...
1;
We don't need to specify the attribute in the constructor if it's not being set as it will be created by writing to $self reference in init_common, but listing it helps clarity. (Of course, common attribute can be written at construction ‡ as well; and, we don't need a separate method for it either.)
Derived subclasses need not mention any of this, attribute nor init_common method, unless they should customize things.
SAFARI/View.pm file
package SAFARI::View;
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(pp);
# Make sure #INC gets set as needed
use parent 'SAFARI';
use overload ( q("") => sub { return pp {%$_[0]} } );
# no need for init_common, nor for new (except to modify/override)
sub output {
my ($self, #args) = #_;
say $self->{common}->{...};
return $self;
};
...
1;
† Inheritance is already in place here, otherwise composition or roles would be good alternatives. Even so, still consider using a role here; see this post for instance.
‡ If 'common' data can be passed to the constructor
sub new {
my ($class, $attr) = #_; # pass a hashref with attribute => value
my $self = {
common => {}, # introduce the attribute, for clarity
# ...
};
bless $self, $class;
$self->init_common( $attr->{common} ) if exists $attr->{common};
return $self;
}
sub init_common {
my ($self, $data) = #_;
$self->{common}->{$_} = $data->{$_} for keys %$data;
return $self;
}
then 'common' can also be initialized as
# ... set/customize $common
my ($sview_obj, $sother_obj) =
map { $_->new( { common => $common } ) }
qw(SAFARI::View SAFARI::Other);

Related

How to set a puppet class variable from within a hiera_hash each loop?

hiera data
ae::namespace_by_fqdn_pattern:
'((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((client))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com': '/test/blah/regression/client'
'((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((server))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com': '/test/blah/regression/server'
class
class ae {
$namespace = hiera('ae::namespace')
$target_host_patterns = hiera('ae::target_host_patterns')
hiera_hash('ae::namespace_by_fqdn_pattern').each |String $pattern, String $ns| {
if $facts['networking']['fqdn'].match($pattern) {
$ae::namespace = "${ns}"
}
}
<snip>
... yields
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Illegal attempt to assign to 'ae::enforcerd_namespace'. Cannot assign to variables in other namespaces (file: /etc/puppetlabs/code/environments/ar/modules/ae/manifests/init.pp, line: 21, column: 13) on node dfw-ubuntu1604-client02.pp-devcos.us-central1.gcp.dev.blah.com
... anyone here know how to do this correctly? trying to conditionally override that $ae::namespace variable but i'm too puppet-ignorant to know how to get it working : (
the loop and the pattern matching bits work. just can't figure out how to correctly set that class variable from within the hiera_hash().each loop.
How to set a puppet class variable from within a hiera_hash each loop?
You cannot. The associated block of an each() call establishes a local scope for each iteration. Variable assignments within apply to that local scope, and therefore last only for the duration of one execution of the block. You cannot anyway assign a new value to a variable during its lifetime, so even if you could assign to a class variable from within an each() call, it would be difficult to use that capability (and your approach would not work).
There are several ways you could approach the problem without modifying the form of the data. You could leverage the filter() function, for example, but my personal recommendation would be to use the reduce() function, something like this:
$namespace = lookup('ae::target_host_patterns').reduce(lookup('ae::namespace')) |$ns, $entry| {
$facts['networking']['fqdn'].match($entry[0]) ? { true => $entry[1], default => $ns }
}
That does pretty much exactly what your original code seems to be trying to do, except that the selected namespace is returned by the reduce() call, to be assigned to a variable by code at class scope, instead of the lambda trying to assign it directly. Note also that it takes care not only of testing the patterns but of assigning the default namespace when none of the patterns match, as it needs to do because you can only assign to the namespace variable once.
so the solution i landed on was to change the hiera data to:
ae::namespace : '/test/blah/regression'
ae::namespace_patterns: ['((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((client))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com', '((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((server))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com']
ae::namespace_by_pattern:
'((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((client))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com': '/test/paypal/regression/client'
'((^dfw-oel6)|(^dfw-oel7)|(^dfw-ubuntu1604))-((server))([0-9]{2}).pp-devcos-ae.us-central1.gcp.dev.blah.com': '/test/paypal/regression/server'
then the class code to:
$pattern = hiera_hash('ae::namespace_patterns').filter |$pattern| {
$facts['networking']['fqdn'] =~ $pattern
}
if length($pattern) {
$namespace = hiera('ae::namespace_by_pattern')[$pattern[0]]
} else {
$namespace = hiera('ae::namespace')
}
definitely still open to better answers. just what my own hacking produced as workable so far through much trial and error.

Perl 6 - Is it possible to create an attribute trait that set a meta-attribute?

I try to create an attribute trait. The use case is to mark some attributes of a class as "crudable" in the context of an objects-to-documents-mapping while other are not.
role crud {
has Bool $.crud is default(True);
}
multi trait_mod:<is>(Attribute $a, crud, $arg) {
$a.container.VAR does crud($arg);
}
class Foo {
has $.bar is rw;
# Provide an extra nested information
has $.baz is rw is crud(True);
}
By reading and adapting some example code, I managed to get something that seems to do what I want. Here is a snippet with test case.
When I instantiate a new Foo object and set the $.bar attribute (that is not crud), it looks like that:
.Foo #0
├ $.bar is rw = 123456789
└ $.baz is rw = .Scalar+{crud} #1
└ $.crud +{crud} = True
What I understand from this is that the $.baz attribute got what I call a meta-attribute that is independent from its potential value.
It looks good to me (if I understood correctly what I did here and that my traits use is not a dirty hack). It is possible to reach $foo.baz.crud that is True. Though, I don't understand very well what .Scalar+{crud} means, and if I can set something there and how.
When I try to set the $.baz instance attribute, this error is returned:
Cannot modify an immutable Scalar+{crud} (Scalar+{crud}.new(crud => Bool::True))
in block <unit> at t/08-attribute-trait.t line 30
Note: This is the closest thing to a working solution I managed to get. I don't need different crud settings for different instances of instantiated Foo classes.
I never want to change the value of the boolean, in fact, once the object instantiated, just providing it to attributes with is crud. I am not even interested to pass a True or False value as an argument: if it would be possible to just set the boolean trait attribute to True by default, it would be enough. I didn't manage to do this though, like:
multi trait_mod:<is>(Attribute $a, :$crud!) {
# Something like this
$a.container.VAR does set-crud;
}
class Foo {
has $.bar is rw;
has $.baz is rw is crud;
}
Am I trying to do something impossible? How could I adapt this code to achieve this use case?
There are several things going on here. First of all, the signature of the trait_mod looks to be wrong. Secondly, there appears to be a bad interaction when the name of a trait is the same as an existing role. I believe this should be an NYI exception, but apparently it either goes wrong in parsing, or it goes wrong in trying to produce the error message.
Anyways, I think this is what you want:
role CRUD {}; # since CRUD is used as an acronym, I chose to use uppercase here
multi trait_mod:<is>(Attribute:D $a, :$crud!) { # note required named attribute!
$a.^mixin: CRUD if $crud; # mixin the CRUD role if a True value was given
}
class A {
has $.a is crud(False); # too bad "is !crud" is invalid syntax
has $.b is crud;
}
say "$_.name(): { $_ ~~ CRUD }" for A.^attributes; # $!a: False, $!b: True
Hope this helps.

Can't locate object method "say_hello" via package "1"

I just started to learn Perl. When I moved to object orientation I am getting an error like
Can't locate object method "say_hello" via package "1" (perhaps you forgot to load "1"?) at ./main.pl line 8.
I googled a lot for a solution. Got some similar issues like this. My understanding is it's not a general issue.
Here is my class
# MyModule.pm
package MyModule;
use strict;
use warnings;
sub new {
print "calling constructor\n";
}
sub say_hello {
print "Hello from MyModule\n";
}
1;
Here is my test script
# main.pl
#!/usr/bin/perl -w
use strict;
use warnings;
use MyModule;
my $myObj = new MyModule();
$myObj->say_hello();
The code is working perfectly if remove last line from main.pl
Your constructor new needs to return a blessed reference to the data structure you are using to contain the object's information. You have no relevant data here, but you still need to return something
bless associates the data with a specific package. In this case, your object should be blessed into MyModule, so that perl knows to look for MyModule::say_hello when it sees a method call like $myObj->say_hello()
Your current constructor returns the value returned by the print statement, which is 1 if it succeeded, as it almost certainly does. That is why you see the "1" in the error message
Can't locate object method "say_hello" via package "1" (perhaps you forgot to load "1"?) at ./main.pl line 8.
The most common container for an object's data is a hash, so you need to change new to this
sub new {
print "calling constructor\n";
my $self = { };
bless $self, 'MyModule';
return $self;
}
and then your program will work as it should. It creates an anonymous hash and assigns it to the $self variable, then blesses and returns it
Note that this can be made much more concise:
Without a return statement, a subroutine will return the value of the most recently executed statement
By default, bless will bless the data into the current package
There is no need to store the reference in a variable before blessing it
So the same effect may be achieved by writing
sub new {
print "calling constructor\n";
bless { };
}
Note also that your call
my $myObj = new MyModule()
is less than ideal. It is called indirect object notation and can be ambiguous. It is better to always use a direct method reference, such as
my $myObj = MyModule->new()
so as to disambiguate the call
You're not creating a new object, and thus $myObj is just the return code of the "print" statement (or 1).
You need to bless something and return it.
sub new {
my ( $class ) = #_;
print "Calling Constructor\n";
my $self = {};
bless $self, $class;
return $self;
}
That way $myObj will actually be an object, not just a return code :)

How to access the data stored in this object?

I'm using BioPerl module to obtain a string from a set of parameters. I followed the HOWTO:Beginners page. The module apparently returns a hash object. How do I get the actual string out of the hash object?
use Bio::DB::GenBank;
use Data::Dumper;
my $gb = Bio::DB::GenBank->new(-format => 'Fasta',
-seq_start => 1,
-seq_stop => 251,
-strand => 1
-complexity => 1);
my $seq = $gb->get_Seq_by_acc('NG_016346');
my $sequence_string = lc($seq->seq());
my $seq_obj = Bio::Seq->new(-seq => $sequence_string,
-alphabet => 'dna' );
my $prot_obj = $seq_obj->translate;
print Dumper($prot_obj);
The data dumper prints the following:
$VAR1 = bless( {
'primary_seq' => bless( {
'length' => 83,
'_root_verbose' => 0,
'_nowarnonempty' => undef,
'seq' => 'RLCVKEGPWPAVEGTWSWG*HRPGSRACPRWGAPNSVQATSYTPSPTHAPFSVSPIPIC*MSLLEASCWPGSREDGARMSAGM',
'alphabet' => 'protein'
}, 'Bio::PrimarySeq' ),
'_root_verbose' => 0
}, 'Bio::Seq' );
How do I obtain 'seq' that is stored in $prot_obj?
I tried
print $prot_obj{'primary_seq'}{'seq'};
but it doesn't print anything. Data dumper printed the word bless. Maybe seq is a field of an object oriented variable.
The correct format for accessing object properties uses ->:
print $prot_obj->{'primary_seq'}->{'seq'};
I'm going to dispute the other answer, and say - the correct way to access object properties is not to do so, and use a method instead.
The reason for doing this is the whole point of OO. Which is to encapsulate chunks of your program, such that multiple developers can work with it concurrently, and the code scales because you can find where things are going wrong more easily.
This only works if you used published methods - the specified way of driving the object - because then you don't have to know what's going on behind the scenes. It also means the implementor is free to change what is going on - maybe simply validating, but maybe overloading or having different responses depending on another property within the object.
All this is subverted by direct access to object properties.
You shouldn't do it, even if perl will "let" you. Let's face it, perl will let you do many bad things.
Bio::PrimarySeq has a method call of seq. to retrieve the seq() attribute. Bio::Seq has an accessor for the primary sequence:
So:
$prot_obj -> seq();
I think would probably do it. (Although, the doc isn't exactly easy reading).
There is an accepted answer but I would also advise against poking around in the intervals of objects like that with the only exception being to see what kind of object is returned (or just use ref). Here is how I would approach the problem:
use 5.010;
use strict;
use warnings;
use Bio::DB::GenBank;
use Bio::Seq;
my $gb = Bio::DB::GenBank->new(
-format => 'Fasta',
-seq_start => 1,
-seq_stop => 251,
-strand => 1,
-complexity => 1
);
my $seq = $gb->get_Seq_by_acc('NG_016346');
my $seq_obj = Bio::Seq->new(
-id => $seq->id,
-seq => $seq->seq,
-alphabet => 'dna'
);
say join "\n", ">".$seq_obj->id, $seq_obj->translate->seq;
Running this gives you the translated FASTA record:
>gi|283837914:1-251
RLCVKEGPWPAVEGTWSWG*HRPGSRACPRWGAPNSVQATSYTPSPTHAPFSVSPIPIC*MSLLEASCWPGSREDGARMSAGM
The real benefit of using BioPerl is in combining the different classes together to solve problems with minimal (but also readable and reusable) code. There was also a minor typo in your code that would have been caught with strict and warnings pragmas enabled (that is my best advice).

How should I update a hash of hashes when using multi-threading in Perl?

I have been spending the last hours trying to figure this out and now I'm really confused.
This is the outline of my task. I should write a Perl subroutine that gets a reference to a hash of hashes.
I have another sub (helper) that gets a single inner hash and does some stuff to with, including adding keys.
sub helper {
$href = shift;
$href->{NEW_KEY}=1;
}
Since each of the internal hashes is independent of the others, I would like to use multi-threading to call helper.
I'm using Thread::Pool::Simple which almost lacks any documentation. Thread::Pool is not supported by my Perl version.
So I have something like this:
sub my_sub {
$hohref = shift;
# create thread pool
my $pool = Thread::Pool::Simple->new(
do => [ \&helper ]
);
# submit jobs
foreach my $hashref ( values %{$hohref} ) {
$pool->add( $hashref );
}
# wait for all threads to end
$pool->join();
}
The key point is that I would like the main hash of hashes to reflect all the changes made to the inner hashes.
my_sub gets an unshared reference to $hohref so I tried creating a shared copy in the body of my_sub:
my $shared_hohref = shared_clone $hohref;
use it and return it instead, but still, the internal hashes were not updated.
When I use the exact same code, but simply replace all the thread pool block with a simple loop
foreach my $hashref ( values %{$hohref} ) {
helper( $hashref );
}
then everything works fine.
Your help would be greatly appreciated.
UPDATE
See this runnable example:
use strict;
use warnings;
use threads;
use threads::shared;
use Thread::Pool::Simple;
use 5.010;
use Data::Dumper;
sub helper {
say "helper starts";
my $href = shift;
say "href is $href";
$href->{NEW_KEY} = 1;
say "helper ends with $href";
}
sub my_sub {
my $hohref = shift;
my $shared_hohref = shared_clone $hohref;
my $pool = Thread::Pool::Simple->new( do => [\&helper] );
# submit jobs
foreach my $hashref ( values %{$shared_hohref} ) {
say "adding to pool: $hashref";
$pool->add($hashref);
}
# wait for all threads to end
$pool->join();
return $shared_hohref;
}
my $hoh = {
A => { NAME => "a" },
B => { NAME => "bb" }
};
say "1\t", Dumper $hoh;
my $updated_hoh = my_sub($hoh);
say "2\t", Dumper $updated_hoh;
'helper starts' but that's it... what ever happens to it?
See http://www.perlmonks.org/?node_id=860786.

Resources