summaryrefslogtreecommitdiffstats
path: root/contrib/perl5/ext/Thread
diff options
context:
space:
mode:
authormarkm <markm@FreeBSD.org>1998-09-09 07:00:04 +0000
committermarkm <markm@FreeBSD.org>1998-09-09 07:00:04 +0000
commit4fcbc3669aa997848e15198cc9fb856287a6788c (patch)
tree58b20e81687d6d5931f120b50802ed21225bf440 /contrib/perl5/ext/Thread
downloadFreeBSD-src-4fcbc3669aa997848e15198cc9fb856287a6788c.zip
FreeBSD-src-4fcbc3669aa997848e15198cc9fb856287a6788c.tar.gz
Initial import of Perl5. The king is dead; long live the king!
Diffstat (limited to 'contrib/perl5/ext/Thread')
-rw-r--r--contrib/perl5/ext/Thread/Makefile.PL7
-rw-r--r--contrib/perl5/ext/Thread/Notes13
-rw-r--r--contrib/perl5/ext/Thread/README20
-rw-r--r--contrib/perl5/ext/Thread/Thread.pm185
-rw-r--r--contrib/perl5/ext/Thread/Thread.xs641
-rw-r--r--contrib/perl5/ext/Thread/Thread/Queue.pm99
-rw-r--r--contrib/perl5/ext/Thread/Thread/Semaphore.pm87
-rw-r--r--contrib/perl5/ext/Thread/Thread/Signal.pm50
-rw-r--r--contrib/perl5/ext/Thread/Thread/Specific.pm29
-rw-r--r--contrib/perl5/ext/Thread/create.t17
-rw-r--r--contrib/perl5/ext/Thread/die.t16
-rw-r--r--contrib/perl5/ext/Thread/die2.t16
-rw-r--r--contrib/perl5/ext/Thread/io.t39
-rw-r--r--contrib/perl5/ext/Thread/join.t11
-rw-r--r--contrib/perl5/ext/Thread/join2.t12
-rw-r--r--contrib/perl5/ext/Thread/list.t30
-rw-r--r--contrib/perl5/ext/Thread/lock.t27
-rw-r--r--contrib/perl5/ext/Thread/queue.t36
-rw-r--r--contrib/perl5/ext/Thread/specific.t17
-rw-r--r--contrib/perl5/ext/Thread/sync.t61
-rw-r--r--contrib/perl5/ext/Thread/sync2.t69
-rw-r--r--contrib/perl5/ext/Thread/typemap24
-rw-r--r--contrib/perl5/ext/Thread/unsync.t37
-rw-r--r--contrib/perl5/ext/Thread/unsync2.t36
-rw-r--r--contrib/perl5/ext/Thread/unsync3.t50
-rw-r--r--contrib/perl5/ext/Thread/unsync4.t38
26 files changed, 1667 insertions, 0 deletions
diff --git a/contrib/perl5/ext/Thread/Makefile.PL b/contrib/perl5/ext/Thread/Makefile.PL
new file mode 100644
index 0000000..e252d4e
--- /dev/null
+++ b/contrib/perl5/ext/Thread/Makefile.PL
@@ -0,0 +1,7 @@
+use ExtUtils::MakeMaker;
+WriteMakefile(
+ NAME => 'Thread',
+ VERSION_FROM => 'Thread.pm',
+ MAN3PODS => ' '
+ );
+
diff --git a/contrib/perl5/ext/Thread/Notes b/contrib/perl5/ext/Thread/Notes
new file mode 100644
index 0000000..1505877
--- /dev/null
+++ b/contrib/perl5/ext/Thread/Notes
@@ -0,0 +1,13 @@
+Should cvcache be per CV (keyed by thread) or per thread (keyed by CV)?
+
+Maybe ought to protect all SVs by a mutex for SvREFCNT_{dec,inc},
+upgrades and so on. Then use SvMUTEX instead of CvMUTEX for CVs.
+On the other hand, people shouldn't expect concurrent operations
+on non-lexicals to be safe anyway.
+
+Probably don't need to bother keeping track of CvOWNER on clones.
+
+Either @_ needs to be made lexical or other arrangments need to be
+made so that some globs (or just *_) are per-thread.
+
+tokenbuf and buf probably ought to be global protected by a global lock.
diff --git a/contrib/perl5/ext/Thread/README b/contrib/perl5/ext/Thread/README
new file mode 100644
index 0000000..a6b22fb
--- /dev/null
+++ b/contrib/perl5/ext/Thread/README
@@ -0,0 +1,20 @@
+See the README.threads in the main perl 5.004_xx development
+distribution (x >= 50) for details of how to build and use this.
+If all else fails, read on.
+
+If your version of patch can't create a file from scratch, then you'll
+need to create an empty thread.h manually first. Perl itself will need
+to be built with -DUSE_THREADS yet. If you're using MIT pthreads or
+another threads package that needs pthread_init() to be called, then
+add -DNEED_PTHREAD_INIT. If you're using a threads library that only
+follows one of the old POSIX drafts, then you'll probably need to add
+-DOLD_PTHREADS_API. I haven't tested -DOLD_PTHREADS_API properly yet
+and I think you may still have to tweak a couple of the mutex calls
+to follow the old API.
+
+This extension is copyright Malcolm Beattie 1995-1997 and is freely
+distributable under your choice of the GNU Public License or the
+Artistic License (see the main perl distribution).
+
+Malcolm Beattie
+mbeattie@sable.ox.ac.uk
diff --git a/contrib/perl5/ext/Thread/Thread.pm b/contrib/perl5/ext/Thread/Thread.pm
new file mode 100644
index 0000000..c8bca0d
--- /dev/null
+++ b/contrib/perl5/ext/Thread/Thread.pm
@@ -0,0 +1,185 @@
+package Thread;
+require Exporter;
+require DynaLoader;
+use vars qw($VERSION @ISA @EXPORT);
+
+$VERSION = "1.0";
+
+@ISA = qw(Exporter DynaLoader);
+@EXPORT_OK = qw(yield cond_signal cond_broadcast cond_wait async);
+
+=head1 NAME
+
+Thread - multithreading
+
+=head1 SYNOPSIS
+
+ use Thread;
+
+ my $t = new Thread \&start_sub, @start_args;
+
+ $t->join;
+
+ my $tid = Thread->self->tid;
+
+ my $tlist = Thread->list;
+
+ lock($scalar);
+
+ use Thread 'async';
+
+ use Thread 'eval';
+
+=head1 DESCRIPTION
+
+The C<Thread> module provides multithreading support for perl.
+
+=head1 FUNCTIONS
+
+=over 8
+
+=item new \&start_sub
+
+=item new \&start_sub, LIST
+
+C<new> starts a new thread of execution in the referenced subroutine. The
+optional list is passed as parameters to the subroutine. Execution
+continues in both the subroutine and the code after the C<new> call.
+
+C<new Thread> returns a thread object representing the newly created
+thread.
+
+=item lock VARIABLE
+
+C<lock> places a 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. C<lock> is recursive, so multiple calls to C<lock> are
+safe--the variable will remain locked until the outermost lock on the
+variable goes out of scope.
+
+Locks on variables only affect C<lock> calls--they do I<not> affect normal
+access to a variable. (Locks on subs are different, and covered in a bit)
+If you really, I<really> want locks to block access, then go ahead and tie
+them to something and manage this yourself. This is done on purpose. While
+managing access to variables is a good thing, perl doesn't force you out of
+its living room...
+
+If a container object, such as a hash or array, is locked, all the elements
+of that container are not locked. For example, if a thread does a C<lock
+@a>, any other thread doing a C<lock($a[12])> won't block.
+
+You may also C<lock> a sub, using C<lock &sub>. Any calls to that sub from
+another thread will block until the lock is released. This behaviour is not
+equvalent to C<use attrs qw(locked)> in the sub. C<use attrs qw(locked)>
+serializes access to a subroutine, but allows different threads
+non-simultaneous access. C<lock &sub>, on the other hand, will not allow
+I<any> other thread access for the duration of the lock.
+
+Finally, C<lock> will traverse up references exactly I<one> level.
+C<lock(\$a)> is equivalent to C<lock($a)>, while C<lock(\\$a)> is not.
+
+=item async BLOCK;
+
+C<async> creates a thread to execute the block immediately following
+it. This block is treated as an anonymous sub, and so must have a
+semi-colon after the closing brace. Like C<new Thread>, C<async> returns a
+thread object.
+
+=item Thread->self
+
+The C<Thread-E<gt>self> function returns a thread object that represents
+the thread making the C<Thread-E<gt>self> call.
+
+=item Thread->list
+
+C<Thread-E<gt>list> returns a list of thread objects for all running and
+finished but un-C<join>ed threads.
+
+=item cond_wait VARIABLE
+
+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 relocked after the C<cond_wait> is satisfied.
+If there are multiple threads C<cond_wait>ing on the same variable, all but
+one will reblock waiting to reaquire the lock on the variable. (So if
+you're only using C<cond_wait> for synchronization, give up the lock as
+soon as possible)
+
+=item cond_signal VARIABLE
+
+The C<cond_signal> function takes a 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.
+
+=item cond_broadcast VARIABLE
+
+The C<cond_broadcast> function works similarly to C<cond_wait>.
+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 METHODS
+
+=over 8
+
+=item join
+
+C<join> waits for a thread to end and returns any values the thread exited
+with. C<join> will block until the thread has ended, though it won't block
+if the thread has already terminated.
+
+If the thread being C<join>ed C<die>d, the error it died with will be
+returned at this time. If you don't want the thread performing the C<join>
+to die as well, you should either wrap the C<join> in an C<eval> or use the
+C<eval> thread method instead of C<join>.
+
+=item eval
+
+The C<eval> method wraps an C<eval> around a C<join>, and so waits for a
+thread to exit, passing along any values the thread might have returned.
+Errors, of course, get placed into C<$@>.
+
+=item tid
+
+The C<tid> method returns the tid of a thread. The tid is a monotonically
+increasing integer assigned when a thread is created. The main thread of a
+program will have a tid of zero, while subsequent threads will have tids
+assigned starting with one.
+
+=head1 LIMITATIONS
+
+The sequence number used to assign tids is a simple integer, and no
+checking is done to make sure the tid isn't currently in use. If a program
+creates more than 2^32 - 1 threads in a single run, threads may be assigned
+duplicate tids. This limitation may be lifted in a future version of Perl.
+
+=head1 SEE ALSO
+
+L<attrs>, L<Thread::Queue>, L<Thread::Semaphore>, L<Thread::Specific>.
+
+=cut
+
+#
+# Methods
+#
+
+#
+# Exported functions
+#
+sub async (&) {
+ return new Thread $_[0];
+}
+
+sub eval {
+ return eval { shift->join; };
+}
+
+bootstrap Thread;
+
+1;
diff --git a/contrib/perl5/ext/Thread/Thread.xs b/contrib/perl5/ext/Thread/Thread.xs
new file mode 100644
index 0000000..48f8aa0
--- /dev/null
+++ b/contrib/perl5/ext/Thread/Thread.xs
@@ -0,0 +1,641 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+/* Magic signature for Thread's mg_private is "Th" */
+#define Thread_MAGIC_SIGNATURE 0x5468
+
+#ifdef __cplusplus
+#ifdef I_UNISTD
+#include <unistd.h>
+#endif
+#endif
+#include <fcntl.h>
+
+static int sig_pipe[2];
+
+#ifndef THREAD_RET_TYPE
+#define THREAD_RET_TYPE void *
+#define THREAD_RET_CAST(x) ((THREAD_RET_TYPE) x)
+#endif
+
+static void
+remove_thread(struct perl_thread *t)
+{
+#ifdef USE_THREADS
+ DEBUG_S(WITH_THR(PerlIO_printf(PerlIO_stderr(),
+ "%p: remove_thread %p\n", thr, t)));
+ MUTEX_LOCK(&PL_threads_mutex);
+ MUTEX_DESTROY(&t->mutex);
+ PL_nthreads--;
+ t->prev->next = t->next;
+ t->next->prev = t->prev;
+ COND_BROADCAST(&PL_nthreads_cond);
+ MUTEX_UNLOCK(&PL_threads_mutex);
+#endif
+}
+
+static THREAD_RET_TYPE
+threadstart(void *arg)
+{
+#ifdef USE_THREADS
+#ifdef FAKE_THREADS
+ Thread savethread = thr;
+ LOGOP myop;
+ dSP;
+ I32 oldscope = PL_scopestack_ix;
+ I32 retval;
+ AV *av;
+ int i;
+
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "new thread %p starting at %s\n",
+ thr, SvPEEK(TOPs)));
+ thr = (Thread) arg;
+ savemark = TOPMARK;
+ thr->prev = thr->prev_run = savethread;
+ thr->next = savethread->next;
+ thr->next_run = savethread->next_run;
+ savethread->next = savethread->next_run = thr;
+ thr->wait_queue = 0;
+ thr->private = 0;
+
+ /* Now duplicate most of perl_call_sv but with a few twists */
+ PL_op = (OP*)&myop;
+ Zero(PL_op, 1, LOGOP);
+ myop.op_flags = OPf_STACKED;
+ myop.op_next = Nullop;
+ myop.op_flags |= OPf_KNOW;
+ myop.op_flags |= OPf_WANT_LIST;
+ PL_op = pp_entersub(ARGS);
+ DEBUG_S(if (!PL_op)
+ PerlIO_printf(PerlIO_stderr(), "thread starts at Nullop\n"));
+ /*
+ * When this thread is next scheduled, we start in the right
+ * place. When the thread runs off the end of the sub, perl.c
+ * handles things, using savemark to figure out how much of the
+ * stack is the return value for any join.
+ */
+ thr = savethread; /* back to the old thread */
+ return 0;
+#else
+ Thread thr = (Thread) arg;
+ LOGOP myop;
+ djSP;
+ I32 oldmark = TOPMARK;
+ I32 oldscope = PL_scopestack_ix;
+ I32 retval;
+ SV *sv;
+ AV *av = newAV();
+ int i, ret;
+ dJMPENV;
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "new thread %p waiting to start\n",
+ thr));
+
+ /* Don't call *anything* requiring dTHR until after SET_THR() */
+ /*
+ * Wait until our creator releases us. If we didn't do this, then
+ * it would be potentially possible for out thread to carry on and
+ * do stuff before our creator fills in our "self" field. For example,
+ * if we went and created another thread which tried to JOIN with us,
+ * then we'd be in a mess.
+ */
+ MUTEX_LOCK(&thr->mutex);
+ MUTEX_UNLOCK(&thr->mutex);
+
+ /*
+ * It's safe to wait until now to set the thread-specific pointer
+ * from our pthread_t structure to our struct perl_thread, since
+ * we're the only thread who can get at it anyway.
+ */
+ SET_THR(thr);
+
+ /* Only now can we use SvPEEK (which calls sv_newmortal which does dTHR) */
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "new thread %p starting at %s\n",
+ thr, SvPEEK(TOPs)));
+
+ sv = POPs;
+ PUTBACK;
+ perl_call_sv(sv, G_ARRAY|G_EVAL);
+ SPAGAIN;
+ retval = SP - (PL_stack_base + oldmark);
+ SP = PL_stack_base + oldmark + 1;
+ if (SvCUR(thr->errsv)) {
+ MUTEX_LOCK(&thr->mutex);
+ thr->flags |= THRf_DID_DIE;
+ MUTEX_UNLOCK(&thr->mutex);
+ av_store(av, 0, &PL_sv_no);
+ av_store(av, 1, newSVsv(thr->errsv));
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p died: %s\n",
+ thr, SvPV(thr->errsv, PL_na)));
+ } else {
+ DEBUG_S(STMT_START {
+ for (i = 1; i <= retval; i++) {
+ PerlIO_printf(PerlIO_stderr(), "%p return[%d] = %s\n",
+ thr, i, SvPEEK(SP[i - 1]));
+ }
+ } STMT_END);
+ av_store(av, 0, &PL_sv_yes);
+ for (i = 1; i <= retval; i++, SP++)
+ sv_setsv(*av_fetch(av, i, TRUE), SvREFCNT_inc(*SP));
+ }
+
+ finishoff:
+#if 0
+ /* removed for debug */
+ SvREFCNT_dec(PL_curstack);
+#endif
+ SvREFCNT_dec(thr->cvcache);
+ SvREFCNT_dec(thr->threadsv);
+ SvREFCNT_dec(thr->specific);
+ SvREFCNT_dec(thr->errsv);
+ SvREFCNT_dec(thr->errhv);
+
+ /*Safefree(cxstack);*/
+ while (PL_curstackinfo->si_next)
+ PL_curstackinfo = PL_curstackinfo->si_next;
+ while (PL_curstackinfo) {
+ PERL_SI *p = PL_curstackinfo->si_prev;
+ SvREFCNT_dec(PL_curstackinfo->si_stack);
+ Safefree(PL_curstackinfo->si_cxstack);
+ Safefree(PL_curstackinfo);
+ PL_curstackinfo = p;
+ }
+ Safefree(PL_markstack);
+ Safefree(PL_scopestack);
+ Safefree(PL_savestack);
+ Safefree(PL_retstack);
+ Safefree(PL_tmps_stack);
+ Safefree(PL_ofs);
+
+ SvREFCNT_dec(PL_rs);
+ SvREFCNT_dec(PL_nrs);
+ SvREFCNT_dec(PL_statname);
+ Safefree(PL_screamfirst);
+ Safefree(PL_screamnext);
+ Safefree(PL_reg_start_tmp);
+ SvREFCNT_dec(PL_lastscream);
+ /*SvREFCNT_dec(PL_defoutgv);*/
+
+ MUTEX_LOCK(&thr->mutex);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "%p: threadstart finishing: state is %u\n",
+ thr, ThrSTATE(thr)));
+ switch (ThrSTATE(thr)) {
+ case THRf_R_JOINABLE:
+ ThrSETSTATE(thr, THRf_ZOMBIE);
+ MUTEX_UNLOCK(&thr->mutex);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "%p: R_JOINABLE thread finished\n", thr));
+ break;
+ case THRf_R_JOINED:
+ ThrSETSTATE(thr, THRf_DEAD);
+ MUTEX_UNLOCK(&thr->mutex);
+ remove_thread(thr);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "%p: R_JOINED thread finished\n", thr));
+ break;
+ case THRf_R_DETACHED:
+ ThrSETSTATE(thr, THRf_DEAD);
+ MUTEX_UNLOCK(&thr->mutex);
+ SvREFCNT_dec(av);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "%p: DETACHED thread finished\n", thr));
+ remove_thread(thr); /* This might trigger main thread to finish */
+ break;
+ default:
+ MUTEX_UNLOCK(&thr->mutex);
+ croak("panic: illegal state %u at end of threadstart", ThrSTATE(thr));
+ /* NOTREACHED */
+ }
+ return THREAD_RET_CAST(av); /* Available for anyone to join with */
+ /* us unless we're detached, in which */
+ /* case noone sees the value anyway. */
+#endif
+#else
+ return THREAD_RET_CAST(NULL);
+#endif
+}
+
+static SV *
+newthread (SV *startsv, AV *initargs, char *classname)
+{
+#ifdef USE_THREADS
+ dSP;
+ Thread savethread;
+ int i;
+ SV *sv;
+ int err;
+#ifndef THREAD_CREATE
+ static pthread_attr_t attr;
+ static int attr_inited = 0;
+ sigset_t fullmask, oldmask;
+#endif
+
+ savethread = thr;
+ thr = new_struct_thread(thr);
+ SPAGAIN;
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "%p: newthread (%p), tid is %u, preparing stack\n",
+ savethread, thr, thr->tid));
+ /* The following pushes the arg list and startsv onto the *new* stack */
+ PUSHMARK(SP);
+ /* Could easily speed up the following greatly */
+ for (i = 0; i <= AvFILL(initargs); i++)
+ XPUSHs(SvREFCNT_inc(*av_fetch(initargs, i, FALSE)));
+ XPUSHs(SvREFCNT_inc(startsv));
+ PUTBACK;
+#ifdef THREAD_CREATE
+ err = THREAD_CREATE(thr, threadstart);
+#else
+ /* On your marks... */
+ MUTEX_LOCK(&thr->mutex);
+ /* Get set... */
+ sigfillset(&fullmask);
+ if (sigprocmask(SIG_SETMASK, &fullmask, &oldmask) == -1)
+ croak("panic: sigprocmask");
+ err = 0;
+ if (!attr_inited) {
+ attr_inited = 1;
+#ifdef OLD_PTHREADS_API
+ err = pthread_attr_create(&attr);
+#else
+ err = pthread_attr_init(&attr);
+#endif
+#ifdef OLD_PTHREADS_API
+#ifdef VMS
+/* This is available with the old pthreads API, but only with */
+/* DecThreads (VMS and Digital Unix) */
+ if (err == 0)
+ err = pthread_attr_setdetach_np(&attr, ATTR_JOINABLE);
+#endif
+#else
+ if (err == 0)
+ err = pthread_attr_setdetachstate(&attr, ATTR_JOINABLE);
+#endif
+ }
+ if (err == 0)
+#ifdef OLD_PTHREADS_API
+ err = pthread_create(&thr->self, attr, threadstart, (void*) thr);
+#else
+ err = pthread_create(&thr->self, &attr, threadstart, (void*) thr);
+#endif
+ /* Go */
+ MUTEX_UNLOCK(&thr->mutex);
+#endif
+ if (err) {
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "%p: create of %p failed %d\n",
+ savethread, thr, err));
+ /* Thread creation failed--clean up */
+ SvREFCNT_dec(thr->cvcache);
+ remove_thread(thr);
+ MUTEX_DESTROY(&thr->mutex);
+ for (i = 0; i <= AvFILL(initargs); i++)
+ SvREFCNT_dec(*av_fetch(initargs, i, FALSE));
+ SvREFCNT_dec(startsv);
+ return NULL;
+ }
+#ifdef THREAD_POST_CREATE
+ THREAD_POST_CREATE(thr);
+#else
+ if (sigprocmask(SIG_SETMASK, &oldmask, 0))
+ croak("panic: sigprocmask");
+#endif
+ sv = newSViv(thr->tid);
+ sv_magic(sv, thr->oursv, '~', 0, 0);
+ SvMAGIC(sv)->mg_private = Thread_MAGIC_SIGNATURE;
+ return sv_bless(newRV_noinc(sv), gv_stashpv(classname, TRUE));
+#else
+ croak("No threads in this perl");
+ return &PL_sv_undef;
+#endif
+}
+
+static Signal_t handle_thread_signal _((int sig));
+
+static Signal_t
+handle_thread_signal(int sig)
+{
+ unsigned char c = (unsigned char) sig;
+ /*
+ * We're not really allowed to call fprintf in a signal handler
+ * so don't be surprised if this isn't robust while debugging
+ * with -DL.
+ */
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "handle_thread_signal: got signal %d\n", sig););
+ write(sig_pipe[1], &c, 1);
+}
+
+MODULE = Thread PACKAGE = Thread
+PROTOTYPES: DISABLE
+
+void
+new(classname, startsv, ...)
+ char * classname
+ SV * startsv
+ AV * av = av_make(items - 2, &ST(2));
+ PPCODE:
+ XPUSHs(sv_2mortal(newthread(startsv, av, classname)));
+
+void
+join(t)
+ Thread t
+ AV * av = NO_INIT
+ int i = NO_INIT
+ PPCODE:
+#ifdef USE_THREADS
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: joining %p (state %u)\n",
+ thr, t, ThrSTATE(t)););
+ MUTEX_LOCK(&t->mutex);
+ switch (ThrSTATE(t)) {
+ case THRf_R_JOINABLE:
+ case THRf_R_JOINED:
+ ThrSETSTATE(t, THRf_R_JOINED);
+ MUTEX_UNLOCK(&t->mutex);
+ break;
+ case THRf_ZOMBIE:
+ ThrSETSTATE(t, THRf_DEAD);
+ MUTEX_UNLOCK(&t->mutex);
+ remove_thread(t);
+ break;
+ default:
+ MUTEX_UNLOCK(&t->mutex);
+ croak("can't join with thread");
+ /* NOTREACHED */
+ }
+ JOIN(t, &av);
+
+ if (SvTRUE(*av_fetch(av, 0, FALSE))) {
+ /* Could easily speed up the following if necessary */
+ for (i = 1; i <= AvFILL(av); i++)
+ XPUSHs(sv_2mortal(*av_fetch(av, i, FALSE)));
+ } else {
+ char *mess = SvPV(*av_fetch(av, 1, FALSE), PL_na);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "%p: join propagating die message: %s\n",
+ thr, mess));
+ croak(mess);
+ }
+#endif
+
+void
+detach(t)
+ Thread t
+ CODE:
+#ifdef USE_THREADS
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: detaching %p (state %u)\n",
+ thr, t, ThrSTATE(t)););
+ MUTEX_LOCK(&t->mutex);
+ switch (ThrSTATE(t)) {
+ case THRf_R_JOINABLE:
+ ThrSETSTATE(t, THRf_R_DETACHED);
+ /* fall through */
+ case THRf_R_DETACHED:
+ DETACH(t);
+ MUTEX_UNLOCK(&t->mutex);
+ break;
+ case THRf_ZOMBIE:
+ ThrSETSTATE(t, THRf_DEAD);
+ DETACH(t);
+ MUTEX_UNLOCK(&t->mutex);
+ remove_thread(t);
+ break;
+ default:
+ MUTEX_UNLOCK(&t->mutex);
+ croak("can't detach thread");
+ /* NOTREACHED */
+ }
+#endif
+
+void
+equal(t1, t2)
+ Thread t1
+ Thread t2
+ PPCODE:
+ PUSHs((t1 == t2) ? &PL_sv_yes : &PL_sv_no);
+
+void
+flags(t)
+ Thread t
+ PPCODE:
+#ifdef USE_THREADS
+ PUSHs(sv_2mortal(newSViv(t->flags)));
+#endif
+
+void
+self(classname)
+ char * classname
+ PREINIT:
+ SV *sv;
+ PPCODE:
+#ifdef USE_THREADS
+ sv = newSViv(thr->tid);
+ sv_magic(sv, thr->oursv, '~', 0, 0);
+ SvMAGIC(sv)->mg_private = Thread_MAGIC_SIGNATURE;
+ PUSHs(sv_2mortal(sv_bless(newRV_noinc(sv),
+ gv_stashpv(classname, TRUE))));
+#endif
+
+U32
+tid(t)
+ Thread t
+ CODE:
+#ifdef USE_THREADS
+ MUTEX_LOCK(&t->mutex);
+ RETVAL = t->tid;
+ MUTEX_UNLOCK(&t->mutex);
+#else
+ RETVAL = 0;
+#endif
+ OUTPUT:
+ RETVAL
+
+void
+DESTROY(t)
+ SV * t
+ PPCODE:
+ PUSHs(&PL_sv_yes);
+
+void
+yield()
+ CODE:
+{
+#ifdef USE_THREADS
+ YIELD;
+#endif
+}
+
+void
+cond_wait(sv)
+ SV * sv
+ MAGIC * mg = NO_INIT
+CODE:
+#ifdef USE_THREADS
+ if (SvROK(sv))
+ sv = SvRV(sv);
+
+ mg = condpair_magic(sv);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: cond_wait %p\n", thr, sv));
+ MUTEX_LOCK(MgMUTEXP(mg));
+ if (MgOWNER(mg) != thr) {
+ MUTEX_UNLOCK(MgMUTEXP(mg));
+ croak("cond_wait for lock that we don't own\n");
+ }
+ MgOWNER(mg) = 0;
+ COND_WAIT(MgCONDP(mg), MgMUTEXP(mg));
+ while (MgOWNER(mg))
+ COND_WAIT(MgOWNERCONDP(mg), MgMUTEXP(mg));
+ MgOWNER(mg) = thr;
+ MUTEX_UNLOCK(MgMUTEXP(mg));
+#endif
+
+void
+cond_signal(sv)
+ SV * sv
+ MAGIC * mg = NO_INIT
+CODE:
+#ifdef USE_THREADS
+ if (SvROK(sv))
+ sv = SvRV(sv);
+
+ mg = condpair_magic(sv);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: cond_signal %p\n",thr,sv));
+ MUTEX_LOCK(MgMUTEXP(mg));
+ if (MgOWNER(mg) != thr) {
+ MUTEX_UNLOCK(MgMUTEXP(mg));
+ croak("cond_signal for lock that we don't own\n");
+ }
+ COND_SIGNAL(MgCONDP(mg));
+ MUTEX_UNLOCK(MgMUTEXP(mg));
+#endif
+
+void
+cond_broadcast(sv)
+ SV * sv
+ MAGIC * mg = NO_INIT
+CODE:
+#ifdef USE_THREADS
+ if (SvROK(sv))
+ sv = SvRV(sv);
+
+ mg = condpair_magic(sv);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: cond_broadcast %p\n",
+ thr, sv));
+ MUTEX_LOCK(MgMUTEXP(mg));
+ if (MgOWNER(mg) != thr) {
+ MUTEX_UNLOCK(MgMUTEXP(mg));
+ croak("cond_broadcast for lock that we don't own\n");
+ }
+ COND_BROADCAST(MgCONDP(mg));
+ MUTEX_UNLOCK(MgMUTEXP(mg));
+#endif
+
+void
+list(classname)
+ char * classname
+ PREINIT:
+ Thread t;
+ AV * av;
+ SV ** svp;
+ int n = 0;
+ PPCODE:
+#ifdef USE_THREADS
+ av = newAV();
+ /*
+ * Iterate until we have enough dynamic storage for all threads.
+ * We mustn't do any allocation while holding threads_mutex though.
+ */
+ MUTEX_LOCK(&PL_threads_mutex);
+ do {
+ n = PL_nthreads;
+ MUTEX_UNLOCK(&PL_threads_mutex);
+ if (AvFILL(av) < n - 1) {
+ int i = AvFILL(av);
+ for (i = AvFILL(av); i < n - 1; i++) {
+ SV *sv = newSViv(0); /* fill in tid later */
+ sv_magic(sv, 0, '~', 0, 0); /* fill in other magic later */
+ av_push(av, sv_bless(newRV_noinc(sv),
+ gv_stashpv(classname, TRUE)));
+
+ }
+ }
+ MUTEX_LOCK(&PL_threads_mutex);
+ } while (n < PL_nthreads);
+ n = PL_nthreads; /* Get the final correct value */
+
+ /*
+ * At this point, there's enough room to fill in av.
+ * Note that we are holding threads_mutex so the list
+ * won't change out from under us but all the remaining
+ * processing is "fast" (no blocking, malloc etc.)
+ */
+ t = thr;
+ svp = AvARRAY(av);
+ do {
+ SV *sv = (SV*)SvRV(*svp);
+ sv_setiv(sv, t->tid);
+ SvMAGIC(sv)->mg_obj = SvREFCNT_inc(t->oursv);
+ SvMAGIC(sv)->mg_flags |= MGf_REFCOUNTED;
+ SvMAGIC(sv)->mg_private = Thread_MAGIC_SIGNATURE;
+ t = t->next;
+ svp++;
+ } while (t != thr);
+ /* */
+ MUTEX_UNLOCK(&PL_threads_mutex);
+ /* Truncate any unneeded slots in av */
+ av_fill(av, n - 1);
+ /* Finally, push all the new objects onto the stack and drop av */
+ EXTEND(SP, n);
+ for (svp = AvARRAY(av); n > 0; n--, svp++)
+ PUSHs(*svp);
+ (void)sv_2mortal((SV*)av);
+#endif
+
+
+MODULE = Thread PACKAGE = Thread::Signal
+
+void
+kill_sighandler_thread()
+ PPCODE:
+ write(sig_pipe[1], "\0", 1);
+ PUSHs(&PL_sv_yes);
+
+void
+init_thread_signals()
+ PPCODE:
+ PL_sighandlerp = handle_thread_signal;
+ if (pipe(sig_pipe) == -1)
+ XSRETURN_UNDEF;
+ PUSHs(&PL_sv_yes);
+
+void
+await_signal()
+ PREINIT:
+ unsigned char c;
+ SSize_t ret;
+ CODE:
+ do {
+ ret = read(sig_pipe[0], &c, 1);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1)
+ croak("panic: await_signal");
+ ST(0) = sv_newmortal();
+ if (ret)
+ sv_setsv(ST(0), c ? psig_ptr[c] : &PL_sv_no);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ "await_signal returning %s\n", SvPEEK(ST(0))););
+
+MODULE = Thread PACKAGE = Thread::Specific
+
+void
+data(classname = "Thread::Specific")
+ char * classname
+ PPCODE:
+#ifdef USE_THREADS
+ if (AvFILL(thr->specific) == -1) {
+ GV *gv = gv_fetchpv("Thread::Specific::FIELDS", TRUE, SVt_PVHV);
+ av_store(thr->specific, 0, newRV((SV*)GvHV(gv)));
+ }
+ XPUSHs(sv_bless(newRV((SV*)thr->specific),gv_stashpv(classname,TRUE)));
+#endif
diff --git a/contrib/perl5/ext/Thread/Thread/Queue.pm b/contrib/perl5/ext/Thread/Thread/Queue.pm
new file mode 100644
index 0000000..6d5f82b
--- /dev/null
+++ b/contrib/perl5/ext/Thread/Thread/Queue.pm
@@ -0,0 +1,99 @@
+package Thread::Queue;
+use Thread qw(cond_wait cond_broadcast);
+
+=head1 NAME
+
+Thread::Queue - thread-safe queues
+
+=head1 SYNOPSIS
+
+ use Thread::Queue;
+ my $q = new Thread::Queue;
+ $q->enqueue("foo", "bar");
+ my $foo = $q->dequeue; # The "bar" is still in the queue.
+ my $foo = $q->dequeue_nb; # returns "bar", or undef if the queue was
+ # empty
+ my $left = $q->pending; # returns the number of items still in the queue
+
+=head1 DESCRIPTION
+
+A queue, as implemented by C<Thread::Queue> is a thread-safe data structure
+much like a list. Any number of threads can safely add elements to the end
+of the list, or remove elements from the head of the list. (Queues don't
+permit adding or removing elements from the middle of the list)
+
+=head1 FUNCTIONS AND METHODS
+
+=over 8
+
+=item new
+
+The C<new> function creates a new empty queue.
+
+=item enqueue LIST
+
+The C<enqueue> method adds a list of scalars on to the end of the queue.
+The queue will grow as needed to accomodate the list.
+
+=item dequeue
+
+The C<dequeue> method removes a scalar from the head of the queue and
+returns it. If the queue is currently empty, C<dequeue> will block the
+thread until another thread C<enqueue>s a scalar.
+
+=item dequeue_nb
+
+The C<dequeue_nb> method, like the C<dequeue> method, removes a scalar from
+the head of the queue and returns it. Unlike C<dequeue>, though,
+C<dequeue_nb> won't block if the queue is empty, instead returning
+C<undef>.
+
+=item pending
+
+The C<pending> method returns the number of items still in the queue. (If
+there can be multiple readers on the queue it's best to lock the queue
+before checking to make sure that it stays in a consistent state)
+
+=back
+
+=head1 SEE ALSO
+
+L<Thread>
+
+=cut
+
+sub new {
+ my $class = shift;
+ return bless [@_], $class;
+}
+
+sub dequeue {
+ use attrs qw(locked method);
+ my $q = shift;
+ cond_wait $q until @$q;
+ return shift @$q;
+}
+
+sub dequeue_nb {
+ use attrs qw(locked method);
+ my $q = shift;
+ if (@$q) {
+ return shift @$q;
+ } else {
+ return undef;
+ }
+}
+
+sub enqueue {
+ use attrs qw(locked method);
+ my $q = shift;
+ push(@$q, @_) and cond_broadcast $q;
+}
+
+sub pending {
+ use attrs qw(locked method);
+ my $q = shift;
+ return scalar(@$q);
+}
+
+1;
diff --git a/contrib/perl5/ext/Thread/Thread/Semaphore.pm b/contrib/perl5/ext/Thread/Thread/Semaphore.pm
new file mode 100644
index 0000000..915808c
--- /dev/null
+++ b/contrib/perl5/ext/Thread/Thread/Semaphore.pm
@@ -0,0 +1,87 @@
+package Thread::Semaphore;
+use Thread qw(cond_wait cond_broadcast);
+
+=head1 NAME
+
+Thread::Semaphore - thread-safe semaphores
+
+=head1 SYNOPSIS
+
+ use Thread::Semaphore;
+ my $s = new Thread::Semaphore;
+ $s->up; # Also known as the semaphore V -operation.
+ # The guarded section is here
+ $s->down; # Also known as the semaphore P -operation.
+
+ # The default semaphore value is 1.
+ my $s = new Thread::Semaphore($initial_value);
+ $s->up($up_value);
+ $s->down($up_value);
+
+=head1 DESCRIPTION
+
+Semaphores provide a mechanism to regulate access to resources. Semaphores,
+unlike locks, aren't tied to particular scalars, and so may be used to
+control access to anything you care to use them for.
+
+Semaphores don't limit their values to zero or one, so they can be used to
+control access to some resource that may have more than one of. (For
+example, filehandles) Increment and decrement amounts aren't fixed at one
+either, so threads can reserve or return multiple resources at once.
+
+=head1 FUNCTIONS AND METHODS
+
+=over 8
+
+=item new
+
+=item new NUMBER
+
+C<new> creates a new semaphore, and initializes its count to the passed
+number. If no number is passed, the semaphore's count is set to one.
+
+=item down
+
+=item down NUMBER
+
+The C<down> method decreases the semaphore's count by the specified number,
+or one if no number has been specified. If the semaphore's count would drop
+below zero, this method will block until such time that the semaphore's
+count is equal to or larger than the amount you're C<down>ing the
+semaphore's count by.
+
+=item up
+
+=item up NUMBER
+
+The C<up> method increases the semaphore's count by the number specified,
+or one if no number's been specified. This will unblock any thread blocked
+trying to C<down> the semaphore if the C<up> raises the semaphore count
+above what the C<down>s are trying to decrement it by.
+
+=back
+
+=cut
+
+sub new {
+ my $class = shift;
+ my $val = @_ ? shift : 1;
+ bless \$val, $class;
+}
+
+sub down {
+ use attrs qw(locked method);
+ my $s = shift;
+ my $inc = @_ ? shift : 1;
+ cond_wait $s until $$s >= $inc;
+ $$s -= $inc;
+}
+
+sub up {
+ use attrs qw(locked method);
+ my $s = shift;
+ my $inc = @_ ? shift : 1;
+ ($$s += $inc) > 0 and cond_broadcast $s;
+}
+
+1;
diff --git a/contrib/perl5/ext/Thread/Thread/Signal.pm b/contrib/perl5/ext/Thread/Thread/Signal.pm
new file mode 100644
index 0000000..f5f03db
--- /dev/null
+++ b/contrib/perl5/ext/Thread/Thread/Signal.pm
@@ -0,0 +1,50 @@
+package Thread::Signal;
+use Thread qw(async);
+
+=head1 NAME
+
+Thread::Signal - Start a thread which runs signal handlers reliably
+
+=head1 SYNOPSIS
+
+ use Thread::Signal;
+
+ $SIG{HUP} = \&some_handler;
+
+=head1 DESCRIPTION
+
+The C<Thread::Signal> module starts up a special signal handler thread.
+All signals to the process are delivered to it and it runs the
+associated C<$SIG{FOO}> handlers for them. Without this module,
+signals arriving at inopportune moments (such as when perl's internals
+are in the middle of updating critical structures) cause the perl
+code of the handler to be run unsafely which can cause memory corruption
+or worse.
+
+=head1 BUGS
+
+This module changes the semantics of signal handling slightly in that
+the signal handler is run separately from the main thread (and in
+parallel with it). This means that tricks such as calling C<die> from
+a signal handler behave differently (and, in particular, can't be
+used to exit directly from a system call).
+
+=cut
+
+if (!init_thread_signals()) {
+ require Carp;
+ Carp::croak("init_thread_signals failed: $!");
+}
+
+async {
+ my $sig;
+ while ($sig = await_signal()) {
+ &$sig();
+ }
+};
+
+END {
+ kill_sighandler_thread();
+}
+
+1;
diff --git a/contrib/perl5/ext/Thread/Thread/Specific.pm b/contrib/perl5/ext/Thread/Thread/Specific.pm
new file mode 100644
index 0000000..9c8a66a
--- /dev/null
+++ b/contrib/perl5/ext/Thread/Thread/Specific.pm
@@ -0,0 +1,29 @@
+package Thread::Specific;
+
+=head1 NAME
+
+Thread::Specific - thread-specific keys
+
+=head1 SYNOPSIS
+
+ use Thread::Specific;
+ my $k = key_create Thread::Specific;
+
+=head1 DESCRIPTION
+
+C<key_create> returns a unique thread-specific key.
+
+=cut
+
+sub import {
+ use attrs qw(locked method);
+ require fields;
+ fields->import(@_);
+}
+
+sub key_create {
+ use attrs qw(locked method);
+ return ++$FIELDS{__MAX__};
+}
+
+1;
diff --git a/contrib/perl5/ext/Thread/create.t b/contrib/perl5/ext/Thread/create.t
new file mode 100644
index 0000000..7d6d189
--- /dev/null
+++ b/contrib/perl5/ext/Thread/create.t
@@ -0,0 +1,17 @@
+use Thread;
+sub start_here {
+ my $i;
+ print "In start_here with args: @_\n";
+ for ($i = 1; $i <= 5; $i++) {
+ print "start_here: $i\n";
+ sleep 1;
+ }
+}
+
+print "Starting new thread now\n";
+$t = new Thread \&start_here, qw(foo bar baz);
+print "Started thread $t\n";
+for ($count = 1; $count <= 5; $count++) {
+ print "main: $count\n";
+ sleep 1;
+}
diff --git a/contrib/perl5/ext/Thread/die.t b/contrib/perl5/ext/Thread/die.t
new file mode 100644
index 0000000..6239405
--- /dev/null
+++ b/contrib/perl5/ext/Thread/die.t
@@ -0,0 +1,16 @@
+use Thread 'async';
+
+$t = async {
+ print "here\n";
+ die "success";
+ print "shouldn't get here\n";
+};
+
+sleep 1;
+print "joining...\n";
+eval { @r = $t->join; };
+if ($@) {
+ print "thread died with message: $@";
+} else {
+ print "thread failed to die successfully\n";
+}
diff --git a/contrib/perl5/ext/Thread/die2.t b/contrib/perl5/ext/Thread/die2.t
new file mode 100644
index 0000000..f6b6955
--- /dev/null
+++ b/contrib/perl5/ext/Thread/die2.t
@@ -0,0 +1,16 @@
+use Thread 'async';
+
+$t = async {
+ sleep 1;
+ print "here\n";
+ die "success if preceded by 'thread died...'";
+ print "shouldn't get here\n";
+};
+
+print "joining...\n";
+@r = eval { $t->join; };
+if ($@) {
+ print "thread died with message: $@";
+} else {
+ print "thread failed to die successfully\n";
+}
diff --git a/contrib/perl5/ext/Thread/io.t b/contrib/perl5/ext/Thread/io.t
new file mode 100644
index 0000000..6012008
--- /dev/null
+++ b/contrib/perl5/ext/Thread/io.t
@@ -0,0 +1,39 @@
+use Thread;
+
+sub counter {
+$count = 10;
+while ($count--) {
+ sleep 1;
+ print "ping $count\n";
+}
+}
+
+sub reader {
+ my $line;
+ while ($line = <STDIN>) {
+ print "reader: $line";
+ }
+ print "End of input in reader\n";
+ return 0;
+}
+
+print <<'EOT';
+This test starts up a thread to read and echo whatever is typed on
+the keyboard/stdin, line by line, while the main thread counts down
+to zero. The test stays running until both the main thread has
+finished counting down and the I/O thread has seen end-of-file on
+the terminal/stdin.
+EOT
+
+$r = new Thread \&counter;
+
+&reader;
+
+__END__
+
+
+$count = 10;
+while ($count--) {
+ sleep 1;
+ print "ping $count\n";
+}
diff --git a/contrib/perl5/ext/Thread/join.t b/contrib/perl5/ext/Thread/join.t
new file mode 100644
index 0000000..cba2c1c
--- /dev/null
+++ b/contrib/perl5/ext/Thread/join.t
@@ -0,0 +1,11 @@
+use Thread;
+sub foo {
+ print "In foo with args: @_\n";
+ return (7, 8, 9);
+}
+
+print "Starting thread\n";
+$t = new Thread \&foo, qw(foo bar baz);
+print "Joining with $t\n";
+@results = $t->join();
+print "Joining returned ", scalar(@results), " values: @results\n";
diff --git a/contrib/perl5/ext/Thread/join2.t b/contrib/perl5/ext/Thread/join2.t
new file mode 100644
index 0000000..99b43a5
--- /dev/null
+++ b/contrib/perl5/ext/Thread/join2.t
@@ -0,0 +1,12 @@
+use Thread;
+sub foo {
+ print "In foo with args: @_\n";
+ return (7, 8, 9);
+}
+
+print "Starting thread\n";
+$t = new Thread \&foo, qw(foo bar baz);
+sleep 2;
+print "Joining with $t\n";
+@results = $t->join();
+print "Joining returned @results\n";
diff --git a/contrib/perl5/ext/Thread/list.t b/contrib/perl5/ext/Thread/list.t
new file mode 100644
index 0000000..f13f4b2
--- /dev/null
+++ b/contrib/perl5/ext/Thread/list.t
@@ -0,0 +1,30 @@
+use Thread qw(async);
+use Thread::Semaphore;
+
+my $sem = Thread::Semaphore->new(0);
+
+$nthreads = 4;
+
+for (my $i = 0; $i < $nthreads; $i++) {
+ async {
+ my $tid = Thread->self->tid;
+ print "thread $tid started...\n";
+ $sem->down;
+ print "thread $tid finishing\n";
+ };
+}
+
+print "main: started $nthreads threads\n";
+sleep 2;
+
+my @list = Thread->list;
+printf "main: Thread->list returned %d threads\n", scalar(@list);
+
+foreach my $t (@list) {
+ print "inspecting thread $t...\n";
+ print "...deref is $$t\n";
+ print "...flags = ", $t->flags, "\n";
+ print "...tid = ", $t->tid, "\n";
+}
+print "main thread telling workers to finish off...\n";
+$sem->up($nthreads);
diff --git a/contrib/perl5/ext/Thread/lock.t b/contrib/perl5/ext/Thread/lock.t
new file mode 100644
index 0000000..fefb129
--- /dev/null
+++ b/contrib/perl5/ext/Thread/lock.t
@@ -0,0 +1,27 @@
+use Thread;
+
+$level = 0;
+
+sub worker
+{
+ my $num = shift;
+ my $i;
+ print "thread $num starting\n";
+ for ($i = 1; $i <= 20; $i++) {
+ print "thread $num iteration $i\n";
+ select(undef, undef, undef, rand(10)/100);
+ {
+ lock($lock);
+ warn "thread $num saw non-zero level = $level\n" if $level;
+ $level++;
+ print "thread $num has lock\n";
+ select(undef, undef, undef, rand(10)/100);
+ $level--;
+ }
+ print "thread $num released lock\n";
+ }
+}
+
+for ($t = 1; $t <= 5; $t++) {
+ new Thread \&worker, $t;
+}
diff --git a/contrib/perl5/ext/Thread/queue.t b/contrib/perl5/ext/Thread/queue.t
new file mode 100644
index 0000000..4672ba6
--- /dev/null
+++ b/contrib/perl5/ext/Thread/queue.t
@@ -0,0 +1,36 @@
+use Thread;
+use Thread::Queue;
+
+$q = new Thread::Queue;
+
+sub reader {
+ my $tid = Thread->self->tid;
+ my $i = 0;
+ while (1) {
+ $i++;
+ print "reader (tid $tid): waiting for element $i...\n";
+ my $el = $q->dequeue;
+ print "reader (tid $tid): dequeued element $i: value $el\n";
+ select(undef, undef, undef, rand(2));
+ if ($el == -1) {
+ # end marker
+ print "reader (tid $tid) returning\n";
+ return;
+ }
+ }
+}
+
+my $nthreads = 3;
+
+for (my $i = 0; $i < $nthreads; $i++) {
+ Thread->new(\&reader, $i);
+}
+
+for (my $i = 1; $i <= 10; $i++) {
+ my $el = int(rand(100));
+ select(undef, undef, undef, rand(2));
+ print "writer: enqueuing value $el\n";
+ $q->enqueue($el);
+}
+
+$q->enqueue((-1) x $nthreads); # one end marker for each thread
diff --git a/contrib/perl5/ext/Thread/specific.t b/contrib/perl5/ext/Thread/specific.t
new file mode 100644
index 0000000..da130b1
--- /dev/null
+++ b/contrib/perl5/ext/Thread/specific.t
@@ -0,0 +1,17 @@
+use Thread;
+
+use Thread::Specific qw(foo);
+
+sub count {
+ my $tid = Thread->self->tid;
+ my Thread::Specific $tsd = Thread::Specific::data;
+ for (my $i = 0; $i < 5; $i++) {
+ $tsd->{foo} = $i;
+ print "thread $tid count: $tsd->{foo}\n";
+ select(undef, undef, undef, rand(2));
+ }
+};
+
+for(my $t = 0; $t < 5; $t++) {
+ new Thread \&count;
+}
diff --git a/contrib/perl5/ext/Thread/sync.t b/contrib/perl5/ext/Thread/sync.t
new file mode 100644
index 0000000..9c2e589
--- /dev/null
+++ b/contrib/perl5/ext/Thread/sync.t
@@ -0,0 +1,61 @@
+use Thread;
+
+$level = 0;
+
+sub single_file {
+ use attrs 'locked';
+ my $arg = shift;
+ $level++;
+ print "Level $level for $arg\n";
+ print "(something is wrong)\n" if $level < 0 || $level > 1;
+ sleep 1;
+ $level--;
+ print "Back to level $level\n";
+}
+
+sub start_bar {
+ my $i;
+ print "start bar\n";
+ for $i (1..3) {
+ print "bar $i\n";
+ single_file("bar $i");
+ sleep 1 if rand > 0.5;
+ }
+ print "end bar\n";
+ return 1;
+}
+
+sub start_foo {
+ my $i;
+ print "start foo\n";
+ for $i (1..3) {
+ print "foo $i\n";
+ single_file("foo $i");
+ sleep 1 if rand > 0.5;
+ }
+ print "end foo\n";
+ return 1;
+}
+
+sub start_baz {
+ my $i;
+ print "start baz\n";
+ for $i (1..3) {
+ print "baz $i\n";
+ single_file("baz $i");
+ sleep 1 if rand > 0.5;
+ }
+ print "end baz\n";
+ return 1;
+}
+
+$| = 1;
+srand($$^$^T);
+
+$foo = new Thread \&start_foo;
+$bar = new Thread \&start_bar;
+$baz = new Thread \&start_baz;
+$foo->join();
+$bar->join();
+$baz->join();
+print "main: threads finished, exiting\n";
diff --git a/contrib/perl5/ext/Thread/sync2.t b/contrib/perl5/ext/Thread/sync2.t
new file mode 100644
index 0000000..0901da4
--- /dev/null
+++ b/contrib/perl5/ext/Thread/sync2.t
@@ -0,0 +1,69 @@
+use Thread;
+
+$global = undef;
+
+sub single_file {
+ use attrs 'locked';
+ my $who = shift;
+ my $i;
+
+ print "Uh oh: $who entered while locked by $global\n" if $global;
+ $global = $who;
+ print "[";
+ for ($i = 0; $i < int(10 * rand); $i++) {
+ print $who;
+ select(undef, undef, undef, 0.1);
+ }
+ print "]";
+ $global = undef;
+}
+
+sub start_a {
+ my ($i, $j);
+ for ($j = 0; $j < 10; $j++) {
+ single_file("A");
+ for ($i = 0; $i < int(10 * rand); $i++) {
+ print "a";
+ select(undef, undef, undef, 0.1);
+ }
+ }
+}
+
+sub start_b {
+ my ($i, $j);
+ for ($j = 0; $j < 10; $j++) {
+ single_file("B");
+ for ($i = 0; $i < int(10 * rand); $i++) {
+ print "b";
+ select(undef, undef, undef, 0.1);
+ }
+ }
+}
+
+sub start_c {
+ my ($i, $j);
+ for ($j = 0; $j < 10; $j++) {
+ single_file("C");
+ for ($i = 0; $i < int(10 * rand); $i++) {
+ print "c";
+ select(undef, undef, undef, 0.1);
+ }
+ }
+}
+
+$| = 1;
+srand($$^$^T);
+
+print <<'EOT';
+Each pair of square brackets [...] should contain a repeated sequence of
+a unique upper case letter. Lower case letters may appear randomly both
+in and out of the brackets.
+EOT
+$foo = new Thread \&start_a;
+$bar = new Thread \&start_b;
+$baz = new Thread \&start_c;
+print "\nmain: joining...\n";
+#$foo->join;
+#$bar->join;
+#$baz->join;
+print "\ndone\n";
diff --git a/contrib/perl5/ext/Thread/typemap b/contrib/perl5/ext/Thread/typemap
new file mode 100644
index 0000000..21eb6c3
--- /dev/null
+++ b/contrib/perl5/ext/Thread/typemap
@@ -0,0 +1,24 @@
+Thread T_XSCPTR
+
+INPUT
+T_XSCPTR
+ STMT_START {
+ MAGIC *mg;
+ SV *sv = ($arg);
+
+ if (!sv_isobject(sv))
+ croak(\"$var is not an object\");
+ sv = (SV*)SvRV(sv);
+ if (!SvRMAGICAL(sv) || !(mg = mg_find(sv, '~'))
+ || mg->mg_private != ${ntype}_MAGIC_SIGNATURE)
+ croak(\"XSUB ${func_name}: $var is a forged ${ntype} object\");
+ $var = ($type) SvPVX(mg->mg_obj);
+ DEBUG_S(PerlIO_printf(PerlIO_stderr(),
+ \"XSUB ${func_name}: %p\\n\", $var);)
+ } STMT_END
+T_IVREF
+ if (SvROK($arg))
+ $var = ($type) SvIV((SV*)SvRV($arg));
+ else
+ croak(\"$var is not a reference\")
+
diff --git a/contrib/perl5/ext/Thread/unsync.t b/contrib/perl5/ext/Thread/unsync.t
new file mode 100644
index 0000000..f0a51ef
--- /dev/null
+++ b/contrib/perl5/ext/Thread/unsync.t
@@ -0,0 +1,37 @@
+use Thread;
+
+$| = 1;
+
+if (@ARGV) {
+ srand($ARGV[0]);
+} else {
+ my $seed = $$ ^ $^T;
+ print "Randomising to $seed\n";
+ srand($seed);
+}
+
+sub whoami {
+ my ($depth, $a, $b, $c) = @_;
+ my $i;
+ print "whoami ($depth): $a $b $c\n";
+ sleep 1;
+ whoami($depth - 1, $a, $b, $c) if $depth > 0;
+}
+
+sub start_foo {
+ my $r = 3 + int(10 * rand);
+ print "start_foo: r is $r\n";
+ whoami($r, "start_foo", "foo1", "foo2");
+ print "start_foo: finished\n";
+}
+
+sub start_bar {
+ my $r = 3 + int(10 * rand);
+ print "start_bar: r is $r\n";
+ whoami($r, "start_bar", "bar1", "bar2");
+ print "start_bar: finished\n";
+}
+
+$foo = new Thread \&start_foo;
+$bar = new Thread \&start_bar;
+print "main: exiting\n";
diff --git a/contrib/perl5/ext/Thread/unsync2.t b/contrib/perl5/ext/Thread/unsync2.t
new file mode 100644
index 0000000..fb955ac
--- /dev/null
+++ b/contrib/perl5/ext/Thread/unsync2.t
@@ -0,0 +1,36 @@
+use Thread;
+
+$| = 1;
+
+srand($$^$^T);
+
+sub printargs {
+ my $thread = shift;
+ my $arg;
+ my $i;
+ while ($arg = shift) {
+ my $delay = int(rand(500));
+ $i++;
+ print "$thread arg $i is $arg\n";
+ 1 while $delay--;
+ }
+}
+
+sub start_thread {
+ my $thread = shift;
+ my $count = 10;
+ while ($count--) {
+ my(@args) = ($thread) x int(rand(10));
+ print "$thread $count calling printargs @args\n";
+ printargs($thread, @args);
+ }
+}
+
+new Thread (\&start_thread, "A");
+new Thread (\&start_thread, "B");
+#new Thread (\&start_thread, "C");
+#new Thread (\&start_thread, "D");
+#new Thread (\&start_thread, "E");
+#new Thread (\&start_thread, "F");
+
+print "main: exiting\n";
diff --git a/contrib/perl5/ext/Thread/unsync3.t b/contrib/perl5/ext/Thread/unsync3.t
new file mode 100644
index 0000000..e03e9c8
--- /dev/null
+++ b/contrib/perl5/ext/Thread/unsync3.t
@@ -0,0 +1,50 @@
+use Thread;
+
+$| = 1;
+
+srand($$^$^T);
+
+sub whoami {
+ my $thread = shift;
+ print $thread;
+}
+
+sub uppercase {
+ my $count = 100;
+ while ($count--) {
+ my $i = int(rand(1000));
+ 1 while $i--;
+ print "A";
+ $i = int(rand(1000));
+ 1 while $i--;
+ whoami("B");
+ }
+}
+
+sub lowercase {
+ my $count = 100;
+ while ($count--) {
+ my $i = int(rand(1000));
+ 1 while $i--;
+ print "x";
+ $i = int(rand(1000));
+ 1 while $i--;
+ whoami("y");
+ }
+}
+
+sub numbers {
+ my $count = 100;
+ while ($count--) {
+ my $i = int(rand(1000));
+ 1 while $i--;
+ print 1;
+ $i = int(rand(1000));
+ 1 while $i--;
+ whoami(2);
+ }
+}
+
+new Thread \&numbers;
+new Thread \&uppercase;
+new Thread \&lowercase;
diff --git a/contrib/perl5/ext/Thread/unsync4.t b/contrib/perl5/ext/Thread/unsync4.t
new file mode 100644
index 0000000..494ad2b
--- /dev/null
+++ b/contrib/perl5/ext/Thread/unsync4.t
@@ -0,0 +1,38 @@
+use Thread;
+
+$| = 1;
+
+srand($$^$^T);
+
+sub printargs {
+ my(@copyargs) = @_;
+ my $thread = shift @copyargs;
+ my $arg;
+ my $i;
+ while ($arg = shift @copyargs) {
+ my $delay = int(rand(500));
+ $i++;
+ print "$thread arg $i is $arg\n";
+ 1 while $delay--;
+ }
+}
+
+sub start_thread {
+ my(@threadargs) = @_;
+ my $thread = $threadargs[0];
+ my $count = 10;
+ while ($count--) {
+ my(@args) = ($thread) x int(rand(10));
+ print "$thread $count calling printargs @args\n";
+ printargs($thread, @args);
+ }
+}
+
+new Thread (\&start_thread, "A");
+new Thread (\&start_thread, "B");
+new Thread (\&start_thread, "C");
+new Thread (\&start_thread, "D");
+new Thread (\&start_thread, "E");
+new Thread (\&start_thread, "F");
+
+print "main: exiting\n";
OpenPOWER on IntegriCloud