summaryrefslogtreecommitdiffstats
path: root/upstream/debian-unstable/man3/threads::shared.3perl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:43:11 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:43:11 +0000
commitfc22b3d6507c6745911b9dfcc68f1e665ae13dbc (patch)
treece1e3bce06471410239a6f41282e328770aa404a /upstream/debian-unstable/man3/threads::shared.3perl
parentInitial commit. (diff)
downloadmanpages-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.3perl574
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.