diff options
Diffstat (limited to 'systems/texlive/tlnet/tlpkg/tlperl/lib/threads')
-rw-r--r-- | systems/texlive/tlnet/tlpkg/tlperl/lib/threads/shared.pm | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/systems/texlive/tlnet/tlpkg/tlperl/lib/threads/shared.pm b/systems/texlive/tlnet/tlpkg/tlperl/lib/threads/shared.pm new file mode 100644 index 0000000000..b9ba66e85b --- /dev/null +++ b/systems/texlive/tlnet/tlpkg/tlperl/lib/threads/shared.pm @@ -0,0 +1,679 @@ +package threads::shared; + +use 5.008; + +use strict; +use warnings; + +use Scalar::Util qw(reftype refaddr blessed); + +our $VERSION = '1.58'; # Please update the pod, too. +my $XS_VERSION = $VERSION; +$VERSION = eval $VERSION; + +# Declare that we have been loaded +$threads::shared::threads_shared = 1; + +# Method of complaint about things we can't clone +$threads::shared::clone_warn = undef; + +# Load the XS code, if applicable +if ($threads::threads) { + require XSLoader; + XSLoader::load('threads::shared', $XS_VERSION); + + *is_shared = \&_id; + +} else { + # String eval is generally evil, but we don't want these subs to + # exist at all if 'threads' is not loaded successfully. + # Vivifying them conditionally this way saves on average about 4K + # of memory per thread. + eval <<'_MARKER_'; + sub share (\[$@%]) { return $_[0] } + sub is_shared (\[$@%]) { undef } + sub cond_wait (\[$@%];\[$@%]) { undef } + sub cond_timedwait (\[$@%]$;\[$@%]) { undef } + sub cond_signal (\[$@%]) { undef } + sub cond_broadcast (\[$@%]) { undef } +_MARKER_ +} + + +### Export ### + +sub import +{ + # Exported subroutines + my @EXPORT = qw(share is_shared cond_wait cond_timedwait + cond_signal cond_broadcast shared_clone); + if ($threads::threads) { + push(@EXPORT, 'bless'); + } + + # Export subroutine names + my $caller = caller(); + foreach my $sym (@EXPORT) { + no strict 'refs'; + *{$caller.'::'.$sym} = \&{$sym}; + } +} + + +# Predeclarations for internal functions +my ($make_shared); + + +### Methods, etc. ### + +sub threads::shared::tie::SPLICE +{ + require Carp; + Carp::croak('Splice not implemented for shared arrays'); +} + + +# Create a thread-shared clone of a complex data structure or object +sub shared_clone +{ + if (@_ != 1) { + require Carp; + Carp::croak('Usage: shared_clone(REF)'); + } + + return $make_shared->(shift, {}); +} + + +### Internal Functions ### + +# Used by shared_clone() to recursively clone +# a complex data structure or object +$make_shared = sub { + my ($item, $cloned) = @_; + + # Just return the item if: + # 1. Not a ref; + # 2. Already shared; or + # 3. Not running 'threads'. + return $item if (! ref($item) || is_shared($item) || ! $threads::threads); + + # Check for previously cloned references + # (this takes care of circular refs as well) + my $addr = refaddr($item); + if (exists($cloned->{$addr})) { + # Return the already existing clone + return $cloned->{$addr}; + } + + # Make copies of array, hash and scalar refs and refs of refs + my $copy; + my $ref_type = reftype($item); + + # Copy an array ref + if ($ref_type eq 'ARRAY') { + # Make empty shared array ref + $copy = &share([]); + # Add to clone checking hash + $cloned->{$addr} = $copy; + # Recursively copy and add contents + push(@$copy, map { $make_shared->($_, $cloned) } @$item); + } + + # Copy a hash ref + elsif ($ref_type eq 'HASH') { + # Make empty shared hash ref + $copy = &share({}); + # Add to clone checking hash + $cloned->{$addr} = $copy; + # Recursively copy and add contents + foreach my $key (keys(%{$item})) { + $copy->{$key} = $make_shared->($item->{$key}, $cloned); + } + } + + # Copy a scalar ref + elsif ($ref_type eq 'SCALAR') { + $copy = \do{ my $scalar = $$item; }; + share($copy); + # Add to clone checking hash + $cloned->{$addr} = $copy; + } + + # Copy of a ref of a ref + elsif ($ref_type eq 'REF') { + # Special handling for $x = \$x + if ($addr == refaddr($$item)) { + $copy = \$copy; + share($copy); + $cloned->{$addr} = $copy; + } else { + my $tmp; + $copy = \$tmp; + share($copy); + # Add to clone checking hash + $cloned->{$addr} = $copy; + # Recursively copy and add contents + $tmp = $make_shared->($$item, $cloned); + } + + } else { + require Carp; + if (! defined($threads::shared::clone_warn)) { + Carp::croak("Unsupported ref type: ", $ref_type); + } elsif ($threads::shared::clone_warn) { + Carp::carp("Unsupported ref type: ", $ref_type); + } + return undef; + } + + # If input item is an object, then bless the copy into the same class + if (my $class = blessed($item)) { + bless($copy, $class); + } + + # Clone READONLY flag + if ($ref_type eq 'SCALAR') { + if (Internals::SvREADONLY($$item)) { + Internals::SvREADONLY($$copy, 1) if ($] >= 5.008003); + } + } + if (Internals::SvREADONLY($item)) { + Internals::SvREADONLY($copy, 1) if ($] >= 5.008003); + } + + return $copy; +}; + +1; + +__END__ + +=head1 NAME + +threads::shared - Perl extension for sharing data structures between threads + +=head1 VERSION + +This document describes threads::shared version 1.58 + +=head1 SYNOPSIS + + use threads; + use threads::shared; + + my $var :shared; + my %hsh :shared; + my @ary :shared; + + my ($scalar, @array, %hash); + share($scalar); + share(@array); + share(%hash); + + $var = $scalar_value; + $var = $shared_ref_value; + $var = shared_clone($non_shared_ref_value); + $var = shared_clone({'foo' => [qw/foo bar baz/]}); + + $hsh{'foo'} = $scalar_value; + $hsh{'bar'} = $shared_ref_value; + $hsh{'baz'} = shared_clone($non_shared_ref_value); + $hsh{'quz'} = shared_clone([1..3]); + + $ary[0] = $scalar_value; + $ary[1] = $shared_ref_value; + $ary[2] = shared_clone($non_shared_ref_value); + $ary[3] = shared_clone([ {}, [] ]); + + { lock(%hash); ... } + + cond_wait($scalar); + cond_timedwait($scalar, time() + 30); + cond_broadcast(@array); + cond_signal(%hash); + + my $lockvar :shared; + # condition var != lock var + cond_wait($var, $lockvar); + cond_timedwait($var, time()+30, $lockvar); + +=head1 DESCRIPTION + +By default, variables are private to each thread, and each newly created +thread gets a private copy of each existing variable. This module allows you +to share variables across different threads (and pseudo-forks on Win32). It +is used together with the L<threads> module. + +This module supports the sharing of the following data types only: scalars +and scalar refs, arrays and array refs, and hashes and hash refs. + +=head1 EXPORT + +The following functions are exported by this module: C<share>, +C<shared_clone>, C<is_shared>, C<cond_wait>, C<cond_timedwait>, C<cond_signal> +and C<cond_broadcast> + +Note that if this module is imported when L<threads> has not yet been loaded, +then these functions all become no-ops. This makes it possible to write +modules that will work in both threaded and non-threaded environments. + +=head1 FUNCTIONS + +=over 4 + +=item share VARIABLE + +C<share> takes a variable and marks it as shared: + + my ($scalar, @array, %hash); + share($scalar); + share(@array); + share(%hash); + +C<share> will return the shared rvalue, but always as a reference. + +Variables can also be marked as shared at compile time by using the +C<:shared> attribute: + + my ($var, %hash, @array) :shared; + +Shared variables can only store scalars, refs of shared variables, or +refs of shared data (discussed in next section): + + my ($var, %hash, @array) :shared; + my $bork; + + # Storing scalars + $var = 1; + $hash{'foo'} = 'bar'; + $array[0] = 1.5; + + # Storing shared refs + $var = \%hash; + $hash{'ary'} = \@array; + $array[1] = \$var; + + # The following are errors: + # $var = \$bork; # ref of non-shared variable + # $hash{'bork'} = []; # non-shared array ref + # push(@array, { 'x' => 1 }); # non-shared hash ref + +=item shared_clone REF + +C<shared_clone> takes a reference, and returns a shared version of its +argument, performing a deep copy on any non-shared elements. Any shared +elements in the argument are used as is (i.e., they are not cloned). + + my $cpy = shared_clone({'foo' => [qw/foo bar baz/]}); + +Object status (i.e., the class an object is blessed into) is also cloned. + + my $obj = {'foo' => [qw/foo bar baz/]}; + bless($obj, 'Foo'); + my $cpy = shared_clone($obj); + print(ref($cpy), "\n"); # Outputs 'Foo' + +For cloning empty array or hash refs, the following may also be used: + + $var = &share([]); # Same as $var = shared_clone([]); + $var = &share({}); # Same as $var = shared_clone({}); + +Not all Perl data types can be cloned (e.g., globs, code refs). By default, +C<shared_clone> will L<croak|Carp> if it encounters such items. To change +this behaviour to a warning, then set the following: + + $threads::shared::clone_warn = 1; + +In this case, C<undef> will be substituted for the item to be cloned. If +set to zero: + + $threads::shared::clone_warn = 0; + +then the C<undef> substitution will be performed silently. + +=item is_shared VARIABLE + +C<is_shared> checks if the specified variable is shared or not. If shared, +returns the variable's internal ID (similar to +C<refaddr()> (see L<Scalar::Util>). Otherwise, returns C<undef>. + + if (is_shared($var)) { + print("\$var is shared\n"); + } else { + print("\$var is not shared\n"); + } + +When used on an element of an array or hash, C<is_shared> checks if the +specified element belongs to a shared array or hash. (It does not check +the contents of that element.) + + my %hash :shared; + if (is_shared(%hash)) { + print("\%hash is shared\n"); + } + + $hash{'elem'} = 1; + if (is_shared($hash{'elem'})) { + print("\$hash{'elem'} is in a shared hash\n"); + } + +=item lock VARIABLE + +C<lock> places a B<advisory> lock on a variable until the lock goes out of +scope. If the variable is locked by another thread, the C<lock> call will +block until it's available. Multiple calls to C<lock> by the same thread from +within dynamically nested scopes are safe -- the variable will remain locked +until the outermost lock on the variable goes out of scope. + +C<lock> follows references exactly I<one> level: + + my %hash :shared; + my $ref = \%hash; + lock($ref); # This is equivalent to lock(%hash) + +Note that you cannot explicitly unlock a variable; you can only wait for the +lock to go out of scope. This is most easily accomplished by locking the +variable inside a block. + + my $var :shared; + { + lock($var); + # $var is locked from here to the end of the block + ... + } + # $var is now unlocked + +As locks are advisory, they do not prevent data access or modification by +another thread that does not itself attempt to obtain a lock on the variable. + +You cannot lock the individual elements of a container variable: + + my %hash :shared; + $hash{'foo'} = 'bar'; + #lock($hash{'foo'}); # Error + lock(%hash); # Works + +If you need more fine-grained control over shared variable access, see +L<Thread::Semaphore>. + +=item cond_wait VARIABLE + +=item cond_wait CONDVAR, LOCKVAR + +The C<cond_wait> function takes a B<locked> variable as a parameter, unlocks +the variable, and blocks until another thread does a C<cond_signal> or +C<cond_broadcast> for that same locked variable. The variable that +C<cond_wait> blocked on is re-locked after the C<cond_wait> is satisfied. If +there are multiple threads C<cond_wait>ing on the same variable, all but one +will re-block waiting to reacquire the +lock on the variable. (So if you're only +using C<cond_wait> for synchronization, give up the lock as soon as possible). +The two actions of unlocking the variable and entering the blocked wait state +are atomic, the two actions of exiting from the blocked wait state and +re-locking the variable are not. + +In its second form, C<cond_wait> takes a shared, B<unlocked> variable followed +by a shared, B<locked> variable. The second variable is unlocked and thread +execution suspended until another thread signals the first variable. + +It is important to note that the variable can be notified even if no thread +C<cond_signal> or C<cond_broadcast> on the variable. It is therefore +important to check the value of the variable and go back to waiting if the +requirement is not fulfilled. For example, to pause until a shared counter +drops to zero: + + { lock($counter); cond_wait($counter) until $counter == 0; } + +=item cond_timedwait VARIABLE, ABS_TIMEOUT + +=item cond_timedwait CONDVAR, ABS_TIMEOUT, LOCKVAR + +In its two-argument form, C<cond_timedwait> takes a B<locked> variable and an +absolute timeout in I<epoch> seconds (see L<time() in perlfunc|perlfunc/time> +for more) as parameters, unlocks the variable, and blocks until the +timeout is reached or another thread signals the variable. A false value is +returned if the timeout is reached, and a true value otherwise. In either +case, the variable is re-locked upon return. + +Like C<cond_wait>, this function may take a shared, B<locked> variable as an +additional parameter; in this case the first parameter is an B<unlocked> +condition variable protected by a distinct lock variable. + +Again like C<cond_wait>, waking up and reacquiring the lock are not atomic, +and you should always check your desired condition after this function +returns. Since the timeout is an absolute value, however, it does not have to +be recalculated with each pass: + + lock($var); + my $abs = time() + 15; + until ($ok = desired_condition($var)) { + last if !cond_timedwait($var, $abs); + } + # we got it if $ok, otherwise we timed out! + +=item cond_signal VARIABLE + +The C<cond_signal> function takes a B<locked> variable as a parameter and +unblocks one thread that's C<cond_wait>ing +on that variable. If more than one +thread is blocked in a C<cond_wait> on that variable, only one (and which one +is indeterminate) will be unblocked. + +If there are no threads blocked in a C<cond_wait> on the variable, the signal +is discarded. By always locking before +signaling, you can (with care), avoid +signaling before another thread has entered cond_wait(). + +C<cond_signal> will normally generate a warning if you attempt to use it on an +unlocked variable. On the rare occasions +where doing this may be sensible, you +can suppress the warning with: + + { no warnings 'threads'; cond_signal($foo); } + +=item cond_broadcast VARIABLE + +The C<cond_broadcast> function works similarly to C<cond_signal>. +C<cond_broadcast>, though, will unblock B<all> the threads that are blocked in +a C<cond_wait> on the locked variable, rather than only one. + +=back + +=head1 OBJECTS + +L<threads::shared> exports a version of L<bless()|perlfunc/"bless REF"> that +works on shared objects such that I<blessings> propagate across threads. + + # Create a shared 'Foo' object + my $foo :shared = shared_clone({}); + bless($foo, 'Foo'); + + # Create a shared 'Bar' object + my $bar :shared = shared_clone({}); + bless($bar, 'Bar'); + + # Put 'bar' inside 'foo' + $foo->{'bar'} = $bar; + + # Rebless the objects via a thread + threads->create(sub { + # Rebless the outer object + bless($foo, 'Yin'); + + # Cannot directly rebless the inner object + #bless($foo->{'bar'}, 'Yang'); + + # Retrieve and rebless the inner object + my $obj = $foo->{'bar'}; + bless($obj, 'Yang'); + $foo->{'bar'} = $obj; + + })->join(); + + print(ref($foo), "\n"); # Prints 'Yin' + print(ref($foo->{'bar'}), "\n"); # Prints 'Yang' + print(ref($bar), "\n"); # Also prints 'Yang' + +=head1 NOTES + +L<threads::shared> is designed to disable itself silently if threads are not +available. This allows you to write modules and packages that can be used +in both threaded and non-threaded applications. + +If you want access to threads, you must C<use threads> before you +C<use threads::shared>. L<threads> will emit a warning if you use it after +L<threads::shared>. + +=head1 WARNINGS + +=over 4 + +=item cond_broadcast() called on unlocked variable + +=item cond_signal() called on unlocked variable + +See L</"cond_signal VARIABLE">, above. + +=back + +=head1 BUGS AND LIMITATIONS + +When C<share> is used on arrays, hashes, array refs or hash refs, any data +they contain will be lost. + + my @arr = qw(foo bar baz); + share(@arr); + # @arr is now empty (i.e., == ()); + + # Create a 'foo' object + my $foo = { 'data' => 99 }; + bless($foo, 'foo'); + + # Share the object + share($foo); # Contents are now wiped out + print("ERROR: \$foo is empty\n") + if (! exists($foo->{'data'})); + +Therefore, populate such variables B<after> declaring them as shared. (Scalar +and scalar refs are not affected by this problem.) + +Blessing a shared item after it has been nested in another shared item does +not propagate the blessing to the shared reference: + + my $foo = &share({}); + my $bar = &share({}); + $bar->{foo} = $foo; + bless($foo, 'baz'); # $foo is now of class 'baz', + # but $bar->{foo} is unblessed. + +Therefore, you should bless objects before sharing them. + +It is often not wise to share an object unless the class itself has been +written to support sharing. For example, a shared object's destructor may +get called multiple times, once for each thread's scope exit, or may not +get called at all if it is embedded inside another shared object. Another +issue is that the contents of hash-based objects will be lost due to the +above mentioned limitation. See F<examples/class.pl> (in the CPAN +distribution of this module) for how to create a class that supports object +sharing. + +Destructors may not be called on objects if those objects still exist at +global destruction time. If the destructors must be called, make sure +there are no circular references and that nothing is referencing the +objects before the program ends. + +Does not support C<splice> on arrays. Does not support explicitly changing +array lengths via $#array -- use C<push> and C<pop> instead. + +Taking references to the elements of shared arrays and hashes does not +autovivify the elements, and neither does slicing a shared array/hash over +non-existent indices/keys autovivify the elements. + +C<share()> allows you to C<< share($hashref->{key}) >> and +C<< share($arrayref->[idx]) >> without giving any error message. But the +C<< $hashref->{key} >> or C<< $arrayref->[idx] >> is B<not> shared, causing +the error "lock can only be used on shared values" to occur when you attempt +to C<< lock($hashref->{key}) >> or C<< lock($arrayref->[idx]) >> in another +thread. + +Using C<refaddr()> is unreliable for testing +whether or not two shared references are equivalent (e.g., when testing for +circular references). Use L<is_shared()|/"is_shared VARIABLE">, instead: + + use threads; + use threads::shared; + use Scalar::Util qw(refaddr); + + # If ref is shared, use threads::shared's internal ID. + # Otherwise, use refaddr(). + my $addr1 = is_shared($ref1) || refaddr($ref1); + my $addr2 = is_shared($ref2) || refaddr($ref2); + + if ($addr1 == $addr2) { + # The refs are equivalent + } + +L<each()|perlfunc/"each HASH"> does not work properly on shared references +embedded in shared structures. For example: + + my %foo :shared; + $foo{'bar'} = shared_clone({'a'=>'x', 'b'=>'y', 'c'=>'z'}); + + while (my ($key, $val) = each(%{$foo{'bar'}})) { + ... + } + +Either of the following will work instead: + + my $ref = $foo{'bar'}; + while (my ($key, $val) = each(%{$ref})) { + ... + } + + foreach my $key (keys(%{$foo{'bar'}})) { + my $val = $foo{'bar'}{$key}; + ... + } + +This module supports dual-valued variables created using C<dualvar()> from +L<Scalar::Util>. However, while C<$!> acts +like a dualvar, it is implemented as a tied SV. To propagate its value, use +the follow construct, if needed: + + my $errno :shared = dualvar($!,$!); + +View existing bug reports at, and submit any new bugs, problems, patches, etc. +to: L<http://rt.cpan.org/Public/Dist/Display.html?Name=threads-shared> + +=head1 SEE ALSO + +threads::shared on MetaCPAN: +L<https://metacpan.org/release/threads-shared> + +Code repository for CPAN distribution: +L<https://github.com/Dual-Life/threads-shared> + +L<threads>, L<perlthrtut> + +L<http://www.perl.com/pub/a/2002/06/11/threads.html> and +L<http://www.perl.com/pub/a/2002/09/04/threads.html> + +Perl threads mailing list: +L<http://lists.perl.org/list/ithreads.html> + +Sample code in the I<examples> directory of this distribution on CPAN. + +=head1 AUTHOR + +Artur Bergman E<lt>sky AT crucially DOT netE<gt> + +Documentation borrowed from the old Thread.pm. + +CPAN version produced by Jerry D. Hedden E<lt>jdhedden AT cpan DOT orgE<gt>. + +=head1 LICENSE + +threads::shared is released under the same license as Perl. + +=cut |