Let's write a module called Book::MyShared, shown in Example 10-1, which we will preload at server startup so that all the variables of this module are initially shared by all children.
package Book::MyShared; use Apache::Peek; my $readonly = "Chris"; sub match { $readonly =~ /\w/g; } sub print_pos { print "pos: ",pos($readonly),"\n";} sub dump { Dump($readonly); } 1;
This module declares the package Book::MyShared, loads the Apache::Peek module and defines the lexically scoped $readonly variable. In most instances, the $readonly variable will be very large (perhaps a huge hash data structure), but here we will use a small variable to simplify this example.
The module also defines three subroutines: match( ), which does simple character matching; print_pos( ), which prints the current position of the matching engine inside the string that was last matched; and finally dump( ), which calls the Apache::Peek module's Dump( ) function to dump a raw Perl representation of the $readonly variable.
Now we write a script (Example 10-2) that prints the process ID (PID) and calls all three functions. The goal is to check whether pos( ) makes the variable dirty and therefore unshared.
use Book::MyShared; print "Content-type: text/plain\n\n"; print "PID: $$\n"; Book::MyShared::match( ); Book::MyShared::print_pos( ); Book::MyShared::dump( );
Before you restart the server, in httpd.conf, set:
MaxClients 2
for easier tracking. You need at least two servers to compare the printouts of the test program. Having more than two can make the comparison process harder.
Now open two browser windows and issue requests for this script in each window, so that you get different PIDs reported in the two windows and so that each process has processed a different number of requests for the share_test.pl script.
In the first window you will see something like this:
PID: 27040 pos: 1 SV = PVMG(0x853db20) at 0x8250e8c REFCNT = 3 FLAGS = (PADBUSY,PADMY,SMG,POK,pPOK) IV = 0 NV = 0 PV = 0x8271af0 "Chris"\0 CUR = 5 LEN = 6 MAGIC = 0x853dd80 MG_VIRTUAL = &vtbl_mglob MG_TYPE = 'g' MG_LEN = 1
And in the second window:
PID: 27041 pos: 2 SV = PVMG(0x853db20) at 0x8250e8c REFCNT = 3 FLAGS = (PADBUSY,PADMY,SMG,POK,pPOK) IV = 0 NV = 0 PV = 0x8271af0 "Chris"\0 CUR = 5 LEN = 6 MAGIC = 0x853dd80 MG_VIRTUAL = &vtbl_mglob MG_TYPE = 'g' MG_LEN = 2
All the addresses of the supposedly large data structure are the same (0x8250e8c and 0x8271af0)—therefore, the variable data structure is almost completely shared. The only difference is in the SV.MAGIC.MG_LEN record, which is not shared. This record is used to track where the last m//g match left off for the given variable, (e.g., by pos( )) and therefore it cannot be shared. See the perlre manpage for more information.
Given that the $readonly variable is a big one, its value is still shared between the processes, while part of the variable data structure is nonshared. The nonshared part is almost insignificant because it takes up very little memory space.
If you need to compare more than one variable, doing it by hand can be quite time consuming and error prone. Therefore, it's better to change the test script to dump the Perl datatypes into files (e.g., /tmp/dump.$$, where $$ is the PID of the process). Then you can use the diff(1) utility to see whether there is some difference.
Changing the dump( ) function to write the information to a file will do the job. Notice that we use Devel::Peek and not Apache::Peek, so we can easily reroute the STDERRstream into a file. In our example, when Devel::Peek tries to print to STDERR, it actually prints to our file. When we are done, we make sure to restore the original STDERR file handle.
The resulting code is shown in Example 10-3.
package Book::MyShared2; use Devel::Peek; my $readonly = "Chris"; sub match { $readonly =~ /\w/g; } sub print_pos { print "pos: ",pos($readonly),"\n";} sub dump { my $dump_file = "/tmp/dump.$$"; print "Dumping the data into $dump_file\n"; open OLDERR, ">&STDERR"; open STDERR, ">$dump_file" or die "Can't open $dump_file: $!"; Dump($readonly); close STDERR ; open STDERR, ">&OLDERR"; } 1;
Now we modify our script to use the modified module, as shown in Example 10-4.
use Book::MyShared2; print "Content-type: text/plain\n\n"; print "PID: $$\n"; Book::MyShared2::match( ); Book::MyShared2::print_pos( ); Book::MyShared2::dump( );
Now we can run the script as before (with MaxClients 2). Two dump files will be created in the directory /tmp. In our test these were created as /tmp/dump.1224 and /tmp/dump.1225. When we run diff(1):
panic% diff -u /tmp/dump.1224 /tmp/dump.1225 12c12 - MG_LEN = 1 + MG_LEN = 2
we see that the two padlists (of the variable $readonly) are different, as we observed before, when we did a manual comparison.
If we think about these results again, we come to the conclusion that there is no need for two processes to find out whether the variable gets modified (and therefore unshared). It's enough just to check the data structure twice, before the script was executed and again afterward. We can modify the Book::MyShared2 module to dump the padlists into a different file after each invocation and then to run diff(1) on the two files.
Suppose you have some lexically scoped variables (i.e., variables declared with my( )) in an Apache::Registryscript. If you want to watch whether they get changed between invocations inside one particular process, you can use the Apache::RegistryLexInfo module. It does exactly that: it takes a snapshot of the padlist before and after the code execution and shows the difference between the two. This particular module was written to work with Apache::Registry scripts, so it won't work for loaded modules. Use the technique we described above for any type of variables in modules and scripts.
Another way of ensuring that a scalar is read-only and therefore shareable is to use either the constant pragma or the readonly pragma, as shown in Example 10-5. But then you won't be able to make calls that alter the variable even a little, such as in the example that we just showed, because it will be a true constant variable and you will get a compile-time error if you try this.
package Book::Constant; use constant readonly => "Chris"; sub match { readonly =~ /\w/g; } sub print_pos { print "pos: ",pos(readonly),"\n";} 1; panic% perl -c Book/Constant.pm Can't modify constant item in match position at Book/Constant.pm line 5, near "readonly)" Book/Constant.pm had compilation errors.
However, the code shown in Example 10-6 is OK.
package Book::Constant1; use constant readonly => "Chris"; sub match { readonly =~ /\w/g; } 1;
It doesn't modify the variable flags at all.
 
Continue to: