diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:43:11 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:43:11 +0000 |
commit | fc22b3d6507c6745911b9dfcc68f1e665ae13dbc (patch) | |
tree | ce1e3bce06471410239a6f41282e328770aa404a /upstream/debian-unstable/man3/threads::shared.3perl | |
parent | Initial commit. (diff) | |
download | manpages-l10n-fc22b3d6507c6745911b9dfcc68f1e665ae13dbc.tar.xz manpages-l10n-fc22b3d6507c6745911b9dfcc68f1e665ae13dbc.zip |
Adding upstream version 4.22.0.upstream/4.22.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'upstream/debian-unstable/man3/threads::shared.3perl')
-rw-r--r-- | upstream/debian-unstable/man3/threads::shared.3perl | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/upstream/debian-unstable/man3/threads::shared.3perl b/upstream/debian-unstable/man3/threads::shared.3perl new file mode 100644 index 00000000..1f91bb0f --- /dev/null +++ b/upstream/debian-unstable/man3/threads::shared.3perl @@ -0,0 +1,574 @@ +.\" -*- mode: troff; coding: utf-8 -*- +.\" Automatically generated by Pod::Man 5.01 (Pod::Simple 3.43) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>. +.ie n \{\ +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "threads::shared 3perl" +.TH threads::shared 3perl 2024-01-12 "perl v5.38.2" "Perl Programmers Reference Guide" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH NAME +threads::shared \- Perl extension for sharing data structures between threads +.SH VERSION +.IX Header "VERSION" +This document describes threads::shared version 1.68 +.SH SYNOPSIS +.IX Header "SYNOPSIS" +.Vb 2 +\& 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({\*(Aqfoo\*(Aq => [qw/foo bar baz/]}); +\& +\& $hsh{\*(Aqfoo\*(Aq} = $scalar_value; +\& $hsh{\*(Aqbar\*(Aq} = $shared_ref_value; +\& $hsh{\*(Aqbaz\*(Aq} = shared_clone($non_shared_ref_value); +\& $hsh{\*(Aqquz\*(Aq} = 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); +.Ve +.SH DESCRIPTION +.IX Header "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 threads module. +.PP +This module supports the sharing of the following data types only: scalars +and scalar refs, arrays and array refs, and hashes and hash refs. +.SH EXPORT +.IX Header "EXPORT" +The following functions are exported by this module: \f(CW\*(C`share\*(C'\fR, +\&\f(CW\*(C`shared_clone\*(C'\fR, \f(CW\*(C`is_shared\*(C'\fR, \f(CW\*(C`cond_wait\*(C'\fR, \f(CW\*(C`cond_timedwait\*(C'\fR, \f(CW\*(C`cond_signal\*(C'\fR +and \f(CW\*(C`cond_broadcast\*(C'\fR +.PP +Note that if this module is imported when 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. +.SH FUNCTIONS +.IX Header "FUNCTIONS" +.IP "share VARIABLE" 4 +.IX Item "share VARIABLE" +\&\f(CW\*(C`share\*(C'\fR takes a variable and marks it as shared: +.Sp +.Vb 4 +\& my ($scalar, @array, %hash); +\& share($scalar); +\& share(@array); +\& share(%hash); +.Ve +.Sp +\&\f(CW\*(C`share\*(C'\fR will return the shared rvalue, but always as a reference. +.Sp +Variables can also be marked as shared at compile time by using the +\&\f(CW\*(C`:shared\*(C'\fR attribute: +.Sp +.Vb 1 +\& my ($var, %hash, @array) :shared; +.Ve +.Sp +Shared variables can only store scalars, refs of shared variables, or +refs of shared data (discussed in next section): +.Sp +.Vb 2 +\& my ($var, %hash, @array) :shared; +\& my $bork; +\& +\& # Storing scalars +\& $var = 1; +\& $hash{\*(Aqfoo\*(Aq} = \*(Aqbar\*(Aq; +\& $array[0] = 1.5; +\& +\& # Storing shared refs +\& $var = \e%hash; +\& $hash{\*(Aqary\*(Aq} = \e@array; +\& $array[1] = \e$var; +\& +\& # The following are errors: +\& # $var = \e$bork; # ref of non\-shared variable +\& # $hash{\*(Aqbork\*(Aq} = []; # non\-shared array ref +\& # push(@array, { \*(Aqx\*(Aq => 1 }); # non\-shared hash ref +.Ve +.IP "shared_clone REF" 4 +.IX Item "shared_clone REF" +\&\f(CW\*(C`shared_clone\*(C'\fR 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). +.Sp +.Vb 1 +\& my $cpy = shared_clone({\*(Aqfoo\*(Aq => [qw/foo bar baz/]}); +.Ve +.Sp +Object status (i.e., the class an object is blessed into) is also cloned. +.Sp +.Vb 4 +\& my $obj = {\*(Aqfoo\*(Aq => [qw/foo bar baz/]}; +\& bless($obj, \*(AqFoo\*(Aq); +\& my $cpy = shared_clone($obj); +\& print(ref($cpy), "\en"); # Outputs \*(AqFoo\*(Aq +.Ve +.Sp +For cloning empty array or hash refs, the following may also be used: +.Sp +.Vb 2 +\& $var = &share([]); # Same as $var = shared_clone([]); +\& $var = &share({}); # Same as $var = shared_clone({}); +.Ve +.Sp +Not all Perl data types can be cloned (e.g., globs, code refs). By default, +\&\f(CW\*(C`shared_clone\*(C'\fR will croak if it encounters such items. To change +this behaviour to a warning, then set the following: +.Sp +.Vb 1 +\& $threads::shared::clone_warn = 1; +.Ve +.Sp +In this case, \f(CW\*(C`undef\*(C'\fR will be substituted for the item to be cloned. If +set to zero: +.Sp +.Vb 1 +\& $threads::shared::clone_warn = 0; +.Ve +.Sp +then the \f(CW\*(C`undef\*(C'\fR substitution will be performed silently. +.IP "is_shared VARIABLE" 4 +.IX Item "is_shared VARIABLE" +\&\f(CW\*(C`is_shared\*(C'\fR checks if the specified variable is shared or not. If shared, +returns the variable's internal ID (similar to +\&\f(CWrefaddr()\fR (see Scalar::Util). Otherwise, returns \f(CW\*(C`undef\*(C'\fR. +.Sp +.Vb 5 +\& if (is_shared($var)) { +\& print("\e$var is shared\en"); +\& } else { +\& print("\e$var is not shared\en"); +\& } +.Ve +.Sp +When used on an element of an array or hash, \f(CW\*(C`is_shared\*(C'\fR checks if the +specified element belongs to a shared array or hash. (It does not check +the contents of that element.) +.Sp +.Vb 4 +\& my %hash :shared; +\& if (is_shared(%hash)) { +\& print("\e%hash is shared\en"); +\& } +\& +\& $hash{\*(Aqelem\*(Aq} = 1; +\& if (is_shared($hash{\*(Aqelem\*(Aq})) { +\& print("\e$hash{\*(Aqelem\*(Aq} is in a shared hash\en"); +\& } +.Ve +.IP "lock VARIABLE" 4 +.IX Item "lock VARIABLE" +\&\f(CW\*(C`lock\*(C'\fR places a \fBadvisory\fR lock on a variable until the lock goes out of +scope. If the variable is locked by another thread, the \f(CW\*(C`lock\*(C'\fR call will +block until it's available. Multiple calls to \f(CW\*(C`lock\*(C'\fR 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. +.Sp +\&\f(CW\*(C`lock\*(C'\fR follows references exactly \fIone\fR level: +.Sp +.Vb 3 +\& my %hash :shared; +\& my $ref = \e%hash; +\& lock($ref); # This is equivalent to lock(%hash) +.Ve +.Sp +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. +.Sp +.Vb 7 +\& my $var :shared; +\& { +\& lock($var); +\& # $var is locked from here to the end of the block +\& ... +\& } +\& # $var is now unlocked +.Ve +.Sp +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. +.Sp +You cannot lock the individual elements of a container variable: +.Sp +.Vb 4 +\& my %hash :shared; +\& $hash{\*(Aqfoo\*(Aq} = \*(Aqbar\*(Aq; +\& #lock($hash{\*(Aqfoo\*(Aq}); # Error +\& lock(%hash); # Works +.Ve +.Sp +If you need more fine-grained control over shared variable access, see +Thread::Semaphore. +.IP "cond_wait VARIABLE" 4 +.IX Item "cond_wait VARIABLE" +.PD 0 +.IP "cond_wait CONDVAR, LOCKVAR" 4 +.IX Item "cond_wait CONDVAR, LOCKVAR" +.PD +The \f(CW\*(C`cond_wait\*(C'\fR function takes a \fBlocked\fR variable as a parameter, unlocks +the variable, and blocks until another thread does a \f(CW\*(C`cond_signal\*(C'\fR or +\&\f(CW\*(C`cond_broadcast\*(C'\fR for that same locked variable. The variable that +\&\f(CW\*(C`cond_wait\*(C'\fR blocked on is re-locked after the \f(CW\*(C`cond_wait\*(C'\fR is satisfied. If +there are multiple threads \f(CW\*(C`cond_wait\*(C'\fRing on the same variable, all but one +will re-block waiting to reacquire the +lock on the variable. (So if you're only +using \f(CW\*(C`cond_wait\*(C'\fR 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. +.Sp +In its second form, \f(CW\*(C`cond_wait\*(C'\fR takes a shared, \fBunlocked\fR variable followed +by a shared, \fBlocked\fR variable. The second variable is unlocked and thread +execution suspended until another thread signals the first variable. +.Sp +It is important to note that the variable can be notified even if no thread +\&\f(CW\*(C`cond_signal\*(C'\fR or \f(CW\*(C`cond_broadcast\*(C'\fR 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: +.Sp +.Vb 1 +\& { lock($counter); cond_wait($counter) until $counter == 0; } +.Ve +.IP "cond_timedwait VARIABLE, ABS_TIMEOUT" 4 +.IX Item "cond_timedwait VARIABLE, ABS_TIMEOUT" +.PD 0 +.IP "cond_timedwait CONDVAR, ABS_TIMEOUT, LOCKVAR" 4 +.IX Item "cond_timedwait CONDVAR, ABS_TIMEOUT, LOCKVAR" +.PD +In its two-argument form, \f(CW\*(C`cond_timedwait\*(C'\fR takes a \fBlocked\fR variable and an +absolute timeout in \fIepoch\fR seconds (see \fBtime()\fR in perlfunc +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. +.Sp +Like \f(CW\*(C`cond_wait\*(C'\fR, this function may take a shared, \fBlocked\fR variable as an +additional parameter; in this case the first parameter is an \fBunlocked\fR +condition variable protected by a distinct lock variable. +.Sp +Again like \f(CW\*(C`cond_wait\*(C'\fR, 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: +.Sp +.Vb 6 +\& 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! +.Ve +.IP "cond_signal VARIABLE" 4 +.IX Item "cond_signal VARIABLE" +The \f(CW\*(C`cond_signal\*(C'\fR function takes a \fBlocked\fR variable as a parameter and +unblocks one thread that's \f(CW\*(C`cond_wait\*(C'\fRing +on that variable. If more than one +thread is blocked in a \f(CW\*(C`cond_wait\*(C'\fR on that variable, only one (and which one +is indeterminate) will be unblocked. +.Sp +If there are no threads blocked in a \f(CW\*(C`cond_wait\*(C'\fR on the variable, the signal +is discarded. By always locking before +signaling, you can (with care), avoid +signaling before another thread has entered \fBcond_wait()\fR. +.Sp +\&\f(CW\*(C`cond_signal\*(C'\fR 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: +.Sp +.Vb 1 +\& { no warnings \*(Aqthreads\*(Aq; cond_signal($foo); } +.Ve +.IP "cond_broadcast VARIABLE" 4 +.IX Item "cond_broadcast VARIABLE" +The \f(CW\*(C`cond_broadcast\*(C'\fR function works similarly to \f(CW\*(C`cond_signal\*(C'\fR. +\&\f(CW\*(C`cond_broadcast\*(C'\fR, though, will unblock \fBall\fR the threads that are blocked in +a \f(CW\*(C`cond_wait\*(C'\fR on the locked variable, rather than only one. +.SH OBJECTS +.IX Header "OBJECTS" +threads::shared exports a version of \fBbless()\fR that +works on shared objects such that \fIblessings\fR propagate across threads. +.PP +.Vb 3 +\& # Create a shared \*(AqFoo\*(Aq object +\& my $foo :shared = shared_clone({}); +\& bless($foo, \*(AqFoo\*(Aq); +\& +\& # Create a shared \*(AqBar\*(Aq object +\& my $bar :shared = shared_clone({}); +\& bless($bar, \*(AqBar\*(Aq); +\& +\& # Put \*(Aqbar\*(Aq inside \*(Aqfoo\*(Aq +\& $foo\->{\*(Aqbar\*(Aq} = $bar; +\& +\& # Rebless the objects via a thread +\& threads\->create(sub { +\& # Rebless the outer object +\& bless($foo, \*(AqYin\*(Aq); +\& +\& # Cannot directly rebless the inner object +\& #bless($foo\->{\*(Aqbar\*(Aq}, \*(AqYang\*(Aq); +\& +\& # Retrieve and rebless the inner object +\& my $obj = $foo\->{\*(Aqbar\*(Aq}; +\& bless($obj, \*(AqYang\*(Aq); +\& $foo\->{\*(Aqbar\*(Aq} = $obj; +\& +\& })\->join(); +\& +\& print(ref($foo), "\en"); # Prints \*(AqYin\*(Aq +\& print(ref($foo\->{\*(Aqbar\*(Aq}), "\en"); # Prints \*(AqYang\*(Aq +\& print(ref($bar), "\en"); # Also prints \*(AqYang\*(Aq +.Ve +.SH NOTES +.IX Header "NOTES" +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. +.PP +If you want access to threads, you must \f(CW\*(C`use threads\*(C'\fR before you +\&\f(CW\*(C`use threads::shared\*(C'\fR. threads will emit a warning if you use it after +threads::shared. +.SH WARNINGS +.IX Header "WARNINGS" +.IP "\fBcond_broadcast()\fR called on unlocked variable" 4 +.IX Item "cond_broadcast() called on unlocked variable" +.PD 0 +.IP "\fBcond_signal()\fR called on unlocked variable" 4 +.IX Item "cond_signal() called on unlocked variable" +.PD +See "cond_signal VARIABLE", above. +.SH "BUGS AND LIMITATIONS" +.IX Header "BUGS AND LIMITATIONS" +When \f(CW\*(C`share\*(C'\fR is used on arrays, hashes, array refs or hash refs, any data +they contain will be lost. +.PP +.Vb 3 +\& my @arr = qw(foo bar baz); +\& share(@arr); +\& # @arr is now empty (i.e., == ()); +\& +\& # Create a \*(Aqfoo\*(Aq object +\& my $foo = { \*(Aqdata\*(Aq => 99 }; +\& bless($foo, \*(Aqfoo\*(Aq); +\& +\& # Share the object +\& share($foo); # Contents are now wiped out +\& print("ERROR: \e$foo is empty\en") +\& if (! exists($foo\->{\*(Aqdata\*(Aq})); +.Ve +.PP +Therefore, populate such variables \fBafter\fR declaring them as shared. (Scalar +and scalar refs are not affected by this problem.) +.PP +Blessing a shared item after it has been nested in another shared item does +not propagate the blessing to the shared reference: +.PP +.Vb 5 +\& my $foo = &share({}); +\& my $bar = &share({}); +\& $bar\->{foo} = $foo; +\& bless($foo, \*(Aqbaz\*(Aq); # $foo is now of class \*(Aqbaz\*(Aq, +\& # but $bar\->{foo} is unblessed. +.Ve +.PP +Therefore, you should bless objects before sharing them. +.PP +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 \fIexamples/class.pl\fR (in the CPAN +distribution of this module) for how to create a class that supports object +sharing. +.PP +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. +.PP +Does not support \f(CW\*(C`splice\*(C'\fR on arrays. Does not support explicitly changing +array lengths via $#array \-\- use \f(CW\*(C`push\*(C'\fR and \f(CW\*(C`pop\*(C'\fR instead. +.PP +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. +.PP +\&\f(CWshare()\fR allows you to \f(CWshare($hashref\->{key})\fR and +\&\f(CWshare($arrayref\->[idx])\fR without giving any error message. But the +\&\f(CW\*(C`$hashref\->{key}\*(C'\fR or \f(CW\*(C`$arrayref\->[idx]\*(C'\fR is \fBnot\fR shared, causing +the error "lock can only be used on shared values" to occur when you attempt +to \f(CWlock($hashref\->{key})\fR or \f(CWlock($arrayref\->[idx])\fR in another +thread. +.PP +Using \f(CWrefaddr()\fR is unreliable for testing +whether or not two shared references are equivalent (e.g., when testing for +circular references). Use \fBis_shared()\fR, instead: +.PP +.Vb 3 +\& use threads; +\& use threads::shared; +\& use Scalar::Util qw(refaddr); +\& +\& # If ref is shared, use threads::shared\*(Aqs 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 +\& } +.Ve +.PP +\&\fBeach()\fR does not work properly on shared references +embedded in shared structures. For example: +.PP +.Vb 2 +\& my %foo :shared; +\& $foo{\*(Aqbar\*(Aq} = shared_clone({\*(Aqa\*(Aq=>\*(Aqx\*(Aq, \*(Aqb\*(Aq=>\*(Aqy\*(Aq, \*(Aqc\*(Aq=>\*(Aqz\*(Aq}); +\& +\& while (my ($key, $val) = each(%{$foo{\*(Aqbar\*(Aq}})) { +\& ... +\& } +.Ve +.PP +Either of the following will work instead: +.PP +.Vb 4 +\& my $ref = $foo{\*(Aqbar\*(Aq}; +\& while (my ($key, $val) = each(%{$ref})) { +\& ... +\& } +\& +\& foreach my $key (keys(%{$foo{\*(Aqbar\*(Aq}})) { +\& my $val = $foo{\*(Aqbar\*(Aq}{$key}; +\& ... +\& } +.Ve +.PP +This module supports dual-valued variables created using \f(CWdualvar()\fR from +Scalar::Util. However, while \f(CW$!\fR acts +like a dualvar, it is implemented as a tied SV. To propagate its value, use +the follow construct, if needed: +.PP +.Vb 1 +\& my $errno :shared = dualvar($!,$!); +.Ve +.PP +View existing bug reports at, and submit any new bugs, problems, patches, etc. +to: <http://rt.cpan.org/Public/Dist/Display.html?Name=threads\-shared> +.SH "SEE ALSO" +.IX Header "SEE ALSO" +threads::shared on MetaCPAN: +<https://metacpan.org/release/threads\-shared> +.PP +Code repository for CPAN distribution: +<https://github.com/Dual\-Life/threads\-shared> +.PP +threads, perlthrtut +.PP +<http://www.perl.com/pub/a/2002/06/11/threads.html> and +<http://www.perl.com/pub/a/2002/09/04/threads.html> +.PP +Perl threads mailing list: +<http://lists.perl.org/list/ithreads.html> +.PP +Sample code in the \fIexamples\fR directory of this distribution on CPAN. +.SH AUTHOR +.IX Header "AUTHOR" +Artur Bergman <sky AT crucially DOT net> +.PP +Documentation borrowed from the old Thread.pm. +.PP +CPAN version produced by Jerry D. Hedden <jdhedden AT cpan DOT org>. +.SH LICENSE +.IX Header "LICENSE" +threads::shared is released under the same license as Perl. |