perl-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stas Bekman <s...@stason.org>
Subject Re: [mp2] Our API is not perl thread-safe
Date Tue, 22 Mar 2005 22:56:12 GMT
Stas Bekman wrote:
> [now take your time to read the Example::CLONE manpage in [1] which 
> explains all the issues and shows possible solutions]

To save you the hussle, here is the manpage (but you will probably want to 
see the code and the tests anyway).

__END__

=head1 NAME

Example::CLONE - Demonstrate the CLONE function under perl threads

=head1 Synopsis

   use Example::CLONE;
   my $obj = Example::CLONE->new($expected);
   my $read = $obj->read;
   my $count = $obj->count;

=head1 Description

This module demonstrates how to handle objects containing references
to C structs under threads. Example::CLONE accepts a string to store
as the argument to new() and then it can read() that string and tell
how many times it has been via count().

As the module name implies it shows how to write a special CLONE
function that takes an object cloned by perl (which doesn't know that
the object contains a pointer to a C struct) and replaces the objects
guts (_possess()) with a properly cloned C struct. In addition to
preventing several perl interpreters accessing and modifying the same
object at the same time (race condition) it also prevents the problem
when several threads try to free the same C struct.

The only unusual things for module writers are:

=over

=item *

the method _possess(), which takes an object and replaces its guts
with new contents, potentially copying the original content. The
external object doesn't change, i.e. it still lives inside the same
SV.

=item *

the function CLONE(), which is called by perl when perl_clone() is
invoked.

When a new object is created its weak-reference copy is put into the
object package's storage. When perl_clone() is called, perl clones all
perl objects, though it doesn't know how to handle C structs (it
doesn't even know that objects contain a pointer to a C struct, since
they are merely IV values (numbers) to Perl). Therefore Perl checks
whether a package defines a function CLONE, and if it does it calls
it. In this function we iterate over all the objects that were created
so far (we can do that since we have stored a weak-reference copy of
the object in the package scoped storage), and do a proper cloning of
the object. We make sure that the object itself looks the same on the
outside, so it gets updated in the user space as well.

The weak-reference copy is used so that if the object is destroyed in
the user space, the copy won't prevent its destruction. If it was a
plain copy the object reference count will be more than one, and the
object won't be destroyed till its copy is not destroyed.

When DESTROY is invoked the local storage is removed.

=back

=head1 Other things that need to be CLONEd

META: this probably belongs to some perl*.pod manpage.

=head2 Data stored in PL_modglobal

Every module that stores per-interpreter data in PL_modglobal may need
to "clone" it in the CLONE method as well.

This is especially true for modules that use MY_CXT_INIT to allocate
the per-interpreter storage.  It allocates the buffer as the PVX of an
untracked SV and then just stores the pointer as a UV.  The SV is
freed when the interpreter is destructed.

perl_clone() will copy the UV ptr in PL_modglobal, but if the first
interpreter is destroyed before the second one, then the PL_modglobal
entry in the second interpreter points to freed memory.

To clone PL_modglobal one could do:

   #define MY_CXT_CLONE                                                 \
       dMY_CXT_SV;                                                      \
       my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
       Copy(INT2PTR(my_cxt_t, SvUV(my_cxt_sv)), my_extp, 1, my_cxt_t);  \
       sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))

And now any module using the MY_CXT_INIT call, needs to call this
macro in an XS CLONE method such as:

   void
   CLONE(...)
       CODE:
       MY_CXT_CLONE;


=head1 Debug

To enable Perl debug tracing set: DEBUG => 1 in lib/Example/CLONE.pm

To enable C debug tracing build the module with:

   % perl Makefile.PL CCFLAGS="-DEXAMPLE_CLONE_DEBUG"

=head1 Examples

t/basic.t - test the normal module functionality, no threads and
demonstration features re involved

t/threads.t - test that the new features (CLONE/_possess) work. For
example if you comment out the call to _possess() in CLONE.pm, this
test will either hang or segfault.


=head1 XS Specifics

Due to a bug in perl 5.8.2 (fixed in 5.8.3), XSUBs called from CLONE
will be called with the parent interpreter context and not clone's
one. In which case XSUBs won't receive the correct arguments. Do see
this problem, build this module with perl 5.8.2 w/ ithreads and
comment out this line in CLONE.xs:

   /* #define PERL_NO_GET_CONTEXT */

Now whenever a new perl intereprter is cloned you will get an error:

   Usage: Example::CLONE::_possess(ref)

defining C<PERL_NO_GET_CONTEXT> avoids this problem, though may need
to adjust your XS module to pass aTHX around as explained in the
I<perlguts> manpage. You have to do it if that module will ever be run
under perl 5.8.2. The good news is that your code will be faster when
C<PERL_NO_GET_CONTEXT> is set, since now the perl context will be
passed around instead of being retrieved from the global storage.

=head1 Bugs

  Attempt to free unreferenced scalar: SV 0x822ecf8 during global destruction.

This is a bug in Scalar::Util::weaken, hopefully to be fixed in
5.8.3. For now just ignore this warning.

=head1 Author

Stas Bekman <stas@stason.org>

=cut



-- 
__________________________________________________________________
Stas Bekman            JAm_pH ------> Just Another mod_perl Hacker
http://stason.org/     mod_perl Guide ---> http://perl.apache.org
mailto:stas@stason.org http://use.perl.org http://apacheweek.com
http://modperlbook.org http://apache.org   http://ticketmaster.com

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@perl.apache.org
For additional commands, e-mail: dev-help@perl.apache.org


Mime
View raw message