#define PERL_NO_GET_CONTEXT
#define NO_XSLOCKS
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#define NEED_PL_signals
#define NEED_sv_2pv_flags
#include "ppport.h"
#include "threads.h"
#ifndef sv_dup_inc
# define sv_dup_inc(s,t) SvREFCNT_inc(sv_dup(s,t))
#endif
#ifndef PERL_UNUSED_RESULT
# if defined(__GNUC__) && defined(HASATTRIBUTE_WARN_UNUSED_RESULT)
# define PERL_UNUSED_RESULT(v) STMT_START { __typeof__(v) z = (v); (void)sizeof(z); } STMT_END
# else
# define PERL_UNUSED_RESULT(v) ((void)(v))
# endif
#endif
#ifndef CLANG_DIAG_IGNORE
# define CLANG_DIAG_IGNORE(x)
# define CLANG_DIAG_RESTORE
#endif
#ifndef CLANG_DIAG_IGNORE_STMT
# define CLANG_DIAG_IGNORE_STMT(x) CLANG_DIAG_IGNORE(x) NOOP
# define CLANG_DIAG_RESTORE_STMT CLANG_DIAG_RESTORE NOOP
# define CLANG_DIAG_IGNORE_DECL(x) CLANG_DIAG_IGNORE(x) dNOOP
# define CLANG_DIAG_RESTORE_DECL CLANG_DIAG_RESTORE dNOOP
#endif
#ifdef USE_ITHREADS
#ifdef __amigaos4__
# undef YIELD
# define YIELD sleep(0)
#endif
#ifdef WIN32
# include <windows.h>
# ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
# define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
# endif
# include <win32thread.h>
#else
# ifdef OS2
typedef
perl_os_thread pthread_t;
# else
# include <pthread.h>
# endif
# include <thread.h>
# define PERL_THREAD_SETSPECIFIC(k,v) pthread_setspecific(k,v)
# ifdef OLD_PTHREADS_API
# define PERL_THREAD_DETACH(t) pthread_detach(&(t))
# else
# define PERL_THREAD_DETACH(t) pthread_detach((t))
# endif
#endif
#if !defined(HAS_GETPAGESIZE) && defined(I_SYS_PARAM)
# include <sys/param.h>
#endif
#define PERL_ITHR_DETACHED 1 /* Thread has been detached */
#define PERL_ITHR_JOINED 2 /* Thread is being / has been joined */
#define PERL_ITHR_FINISHED 4 /* Thread has finished execution */
#define PERL_ITHR_THREAD_EXIT_ONLY 8 /* exit() only exits current thread */
#define PERL_ITHR_NONVIABLE 16 /* Thread creation failed */
#define PERL_ITHR_DIED 32 /* Thread finished by dying */
#define PERL_ITHR_UNCALLABLE (PERL_ITHR_DETACHED|PERL_ITHR_JOINED)
typedef
struct
_ithread {
struct
_ithread *next;
struct
_ithread *prev;
PerlInterpreter *interp;
UV tid;
perl_mutex mutex;
int
count;
int
state;
int
gimme;
SV *init_function;
AV *params;
#ifdef WIN32
DWORD
thr;
HANDLE
handle;
#else
pthread_t thr;
#endif
IV stack_size;
SV *err;
char
*err_class;
#ifndef WIN32
sigset_t initial_sigmask;
#endif
} ithread;
#define MY_CXT_KEY "threads::_cxt" XS_VERSION
typedef
struct
{
ithread *context;
} my_cxt_t;
START_MY_CXT
#define MY_POOL_KEY "threads::_pool" XS_VERSION
typedef
struct
{
ithread main_thread;
perl_mutex create_destruct_mutex;
UV tid_counter;
IV joinable_threads;
IV running_threads;
IV detached_threads;
IV total_threads;
IV default_stack_size;
IV page_size;
} my_pool_t;
#define dMY_POOL \
SV *my_pool_sv = *hv_fetch(PL_modglobal, MY_POOL_KEY, \
sizeof
(MY_POOL_KEY)-1, TRUE); \
my_pool_t *my_poolp = INT2PTR(my_pool_t*, SvUV(my_pool_sv))
#define MY_POOL (*my_poolp)
#if defined(WIN32) || (defined(__amigaos4__) && defined(__NEWLIB__))
# undef THREAD_SIGNAL_BLOCKING
#else
# define THREAD_SIGNAL_BLOCKING
#endif
#ifdef THREAD_SIGNAL_BLOCKING
STATIC
int
S_block_most_signals(sigset_t *oldmask)
{
sigset_t newmask;
sigfillset(&newmask);
#ifdef SIGILL
sigdelset(&newmask, SIGILL);
#endif
#ifdef SIGBUS
sigdelset(&newmask, SIGBUS);
#endif
#ifdef SIGSEGV
sigdelset(&newmask, SIGSEGV);
#endif
#if defined(VMS)
return
sigprocmask(SIG_BLOCK, &newmask, oldmask);
#else
return
pthread_sigmask(SIG_BLOCK, &newmask, oldmask);
#endif /* VMS */
}
STATIC
int
S_set_sigmask(sigset_t *newmask)
{
#if defined(VMS)
return
sigprocmask(SIG_SETMASK, newmask, NULL);
#else
return
pthread_sigmask(SIG_SETMASK, newmask, NULL);
#endif /* VMS */
}
#endif /* WIN32 */
STATIC
void
S_ithread_set(pTHX_ ithread *
thread
)
{
dMY_CXT;
MY_CXT.context =
thread
;
}
STATIC ithread *
S_ithread_get(pTHX)
{
dMY_CXT;
return
(MY_CXT.context);
}
STATIC
void
S_ithread_clear(pTHX_ ithread *
thread
)
{
PerlInterpreter *interp;
#ifndef WIN32
sigset_t origmask;
#endif
assert
(((
thread
->state & PERL_ITHR_FINISHED) &&
(
thread
->state & PERL_ITHR_UNCALLABLE))
||
(
thread
->state & PERL_ITHR_NONVIABLE));
#ifdef THREAD_SIGNAL_BLOCKING
S_block_most_signals(&origmask);
#endif
#if PERL_VERSION_GE(5, 37, 5)
int
save_veto = PL_veto_switch_non_tTHX_context;
#endif
interp =
thread
->interp;
if
(interp) {
dTHXa(interp);
#if PERL_VERSION_GE(5, 37, 5)
PL_veto_switch_non_tTHX_context =
true
;
#endif
PERL_SET_CONTEXT(interp);
S_ithread_set(aTHX_
thread
);
SvREFCNT_dec(
thread
->params);
thread
->params = NULL;
if
(
thread
->err) {
SvREFCNT_dec_NN(
thread
->err);
thread
->err = Nullsv;
}
perl_destruct(interp);
perl_free(interp);
thread
->interp = NULL;
}
PERL_SET_CONTEXT(aTHX);
#if PERL_VERSION_GE(5, 37, 5)
PL_veto_switch_non_tTHX_context = save_veto;
#endif
#ifdef THREAD_SIGNAL_BLOCKING
S_set_sigmask(&origmask);
#endif
}
STATIC
void
S_ithread_free(pTHX_ ithread *
thread
)
PERL_TSA_RELEASE(
thread
->mutex)
{
#ifdef WIN32
HANDLE
handle;
#endif
dMY_POOL;
if
(! (
thread
->state & PERL_ITHR_NONVIABLE)) {
assert
(
thread
->count > 0);
if
(--
thread
->count > 0) {
MUTEX_UNLOCK(&
thread
->mutex);
return
;
}
assert
((
thread
->state & PERL_ITHR_FINISHED) &&
(
thread
->state & PERL_ITHR_UNCALLABLE));
}
MUTEX_UNLOCK(&
thread
->mutex);
assert
(
thread
->tid != 0);
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
assert
(
thread
->prev &&
thread
->next);
thread
->next->prev =
thread
->prev;
thread
->prev->next =
thread
->next;
thread
->next = NULL;
thread
->prev = NULL;
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
MUTEX_LOCK(&
thread
->mutex);
S_ithread_clear(aTHX_
thread
);
#ifdef WIN32
handle =
thread
->handle;
thread
->handle = NULL;
#endif
MUTEX_UNLOCK(&
thread
->mutex);
MUTEX_DESTROY(&
thread
->mutex);
#ifdef WIN32
if
(handle) {
CloseHandle(handle);
}
#endif
PerlMemShared_free(
thread
);
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
MY_POOL.total_threads--;
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
}
static
void
S_ithread_count_inc(pTHX_ ithread *
thread
)
PERL_TSA_EXCLUDES(
thread
->mutex)
{
MUTEX_LOCK(&
thread
->mutex);
thread
->count++;
MUTEX_UNLOCK(&
thread
->mutex);
}
STATIC
int
S_exit_warning(pTHX)
{
int
veto_cleanup, warn;
dMY_POOL;
IV running_threads;
IV joinable_threads;
IV detached_threads;
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
running_threads = MY_POOL.running_threads;
joinable_threads = MY_POOL.joinable_threads;
detached_threads = MY_POOL.detached_threads;
veto_cleanup = (MY_POOL.total_threads > 0);
warn = (running_threads || joinable_threads);
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
if
(warn) {
if
(ckWARN_d(WARN_THREADS)) {
Perl_warn(aTHX_
"Perl exited with active threads:\n\t%"
IVdf
" running and unjoined\n\t%"
IVdf
" finished and unjoined\n\t%"
IVdf
" running and detached\n"
,
running_threads,
joinable_threads,
detached_threads);
}
}
return
(veto_cleanup);
}
STATIC
int
Perl_ithread_hook(pTHX)
{
dMY_POOL;
return
((aTHX == MY_POOL.main_thread.interp) ? S_exit_warning(aTHX) : 0);
}
STATIC
int
ithread_mg_get(pTHX_ SV *sv, MAGIC *mg)
{
ithread *
thread
= (ithread *)mg->mg_ptr;
SvIV_set(sv, PTR2IV(
thread
));
SvIOK_on(sv);
return
(0);
}
STATIC
int
ithread_mg_free(pTHX_ SV *sv, MAGIC *mg)
{
ithread *
thread
= (ithread *)mg->mg_ptr;
PERL_UNUSED_ARG(sv);
MUTEX_LOCK(&
thread
->mutex);
S_ithread_free(aTHX_
thread
);
return
(0);
}
STATIC
int
ithread_mg_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param)
{
PERL_UNUSED_ARG(param);
S_ithread_count_inc(aTHX_ (ithread *)mg->mg_ptr);
return
(0);
}
STATIC
const
MGVTBL ithread_vtbl = {
ithread_mg_get,
0,
0,
0,
ithread_mg_free,
0,
ithread_mg_dup,
#if PERL_VERSION_GT(5,8,8)
0
#endif
};
STATIC IV
S_good_stack_size(pTHX_ IV stack_size)
{
dMY_POOL;
if
(! stack_size) {
return
(MY_POOL.default_stack_size);
}
#ifdef PTHREAD_STACK_MIN
if
(stack_size < PTHREAD_STACK_MIN) {
if
(ckWARN(WARN_THREADS)) {
Perl_warn(aTHX_
"Using minimum thread stack size of %"
IVdf, (IV)PTHREAD_STACK_MIN);
}
return
(PTHREAD_STACK_MIN);
}
#endif
if
(MY_POOL.page_size <= 0) {
#if defined(HAS_SYSCONF) && (defined(_SC_PAGESIZE) || defined(_SC_MMAP_PAGE_SIZE))
SETERRNO(0, SS_NORMAL);
# ifdef _SC_PAGESIZE
MY_POOL.page_size = sysconf(_SC_PAGESIZE);
# else
MY_POOL.page_size = sysconf(_SC_MMAP_PAGE_SIZE);
# endif
if
((
long
)MY_POOL.page_size < 0) {
if
(
errno
) {
SV *
const
error = get_sv(
"@"
, 0);
(
void
)SvUPGRADE(error, SVt_PV);
Perl_croak(aTHX_
"PANIC: sysconf: %s"
, SvPV_nolen(error));
}
else
{
Perl_croak(aTHX_
"PANIC: sysconf: pagesize unknown"
);
}
}
#else
# ifdef HAS_GETPAGESIZE
MY_POOL.page_size = getpagesize();
# else
# if defined(I_SYS_PARAM) && defined(PAGESIZE)
MY_POOL.page_size = PAGESIZE;
# else
MY_POOL.page_size = 8192;
# endif
# endif
if
(MY_POOL.page_size <= 0) {
Perl_croak(aTHX_
"PANIC: bad pagesize %"
IVdf, (IV)MY_POOL.page_size);
}
#endif
}
stack_size = ((stack_size + (MY_POOL.page_size - 1)) / MY_POOL.page_size) * MY_POOL.page_size;
return
(stack_size);
}
static
int
S_jmpenv_run(pTHX_
int
action, ithread *
thread
,
int
*len_p,
int
*exit_app_p,
int
*exit_code_p)
{
dJMPENV;
volatile
I32 oldscope = PL_scopestack_ix;
int
jmp_rc = 0;
JMPENV_PUSH(jmp_rc);
if
(jmp_rc == 0) {
if
(action == 0) {
*len_p = (
int
)call_sv(
thread
->init_function,
thread
->gimme|G_EVAL);
}
else
if
(action == 1) {
Perl_warn(aTHX_
"Thread %"
UVuf
" terminated abnormally: %"
SVf,
thread
->tid, ERRSV);
}
else
{
S_exit_warning(aTHX);
}
}
else
if
(jmp_rc == 2) {
*exit_app_p = 1;
*exit_code_p = STATUS_CURRENT;
while
(PL_scopestack_ix > oldscope) {
LEAVE;
}
}
JMPENV_POP;
return
jmp_rc;
}
#ifdef WIN32
PERL_STACK_REALIGN
STATIC THREAD_RET_TYPE
S_ithread_run(
LPVOID
arg)
#else
STATIC
void
*
S_ithread_run(
void
* arg)
#endif
{
ithread *
thread
= (ithread *)arg;
int
exit_app = 0;
int
exit_code = 0;
int
died = 0;
dTHXa(
thread
->interp);
dMY_POOL;
MUTEX_LOCK(&
thread
->mutex);
MUTEX_UNLOCK(&
thread
->mutex);
PERL_SET_CONTEXT(
thread
->interp);
S_ithread_set(aTHX_
thread
);
#ifdef THREAD_SIGNAL_BLOCKING
S_set_sigmask(&
thread
->initial_sigmask);
#endif
#if PERL_VERSION_GE(5, 27, 9)
thread_locale_init();
#endif
PL_perl_destruct_level = 2;
{
AV *params =
thread
->params;
int
len = (
int
)av_len(params)+1;
int
ii;
int
jmp_rc;
#ifdef PERL_RC_STACK
assert
(rpp_stack_is_rc());
#endif
ENTER;
SAVETMPS;
PUSHMARK(PL_stack_sp);
for
(ii=0; ii < len; ii++) {
SV *sv = av_shift(params);
#ifdef PERL_RC_STACK
rpp_xpush_1(sv);
#else
dSP;
XPUSHs(sv);
PUTBACK;
#endif
}
jmp_rc = S_jmpenv_run(aTHX_ 0,
thread
, &len, &exit_app, &exit_code);
#ifdef THREAD_SIGNAL_BLOCKING
S_block_most_signals(NULL);
#endif
for
(ii=len-1; ii >= 0; ii--) {
SV *sv = *PL_stack_sp;
if
(jmp_rc == 0 && (
thread
->gimme & G_WANT) != G_VOID) {
av_store(params, ii, SvREFCNT_inc(sv));
}
#ifdef PERL_RC_STACK
rpp_popfree_1();
#else
PL_stack_sp--;
#endif
}
FREETMPS;
LEAVE;
if
(SvTRUE(ERRSV)) {
died = PERL_ITHR_DIED;
thread
->err = newSVsv(ERRSV);
if
(sv_isobject(
thread
->err)) {
thread
->err_class = HvNAME(SvSTASH(SvRV(
thread
->err)));
sv_bless(
thread
->err, gv_stashpv(
"main"
, 0));
}
if
(ckWARN_d(WARN_THREADS)) {
(
void
)S_jmpenv_run(aTHX_ 1,
thread
, NULL,
&exit_app, &exit_code);
}
}
SvREFCNT_dec(
thread
->init_function);
thread
->init_function = Nullsv;
}
PerlIO_flush((PerlIO *)NULL);
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
MUTEX_LOCK(&
thread
->mutex);
thread
->state |= (PERL_ITHR_FINISHED | died);
if
(
thread
->state & PERL_ITHR_THREAD_EXIT_ONLY) {
exit_app = 0;
}
if
(
thread
->state & PERL_ITHR_DETACHED) {
MY_POOL.detached_threads--;
}
else
{
MY_POOL.running_threads--;
MY_POOL.joinable_threads++;
}
MUTEX_UNLOCK(&
thread
->mutex);
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
#if PERL_VERSION_GE(5, 27, 9)
thread_locale_term();
#endif
if
(exit_app) {
(
void
)S_jmpenv_run(aTHX_ 2,
thread
, NULL, &exit_app, &exit_code);
my_exit(exit_code);
}
aTHX = MY_POOL.main_thread.interp;
MUTEX_LOCK(&
thread
->mutex);
S_ithread_free(aTHX_
thread
);
#ifdef WIN32
return
((
DWORD
)0);
#else
return
(0);
#endif
}
STATIC SV *
S_ithread_to_SV(pTHX_ SV *obj, ithread *
thread
,
char
*classname,
bool
inc)
{
SV *sv;
MAGIC *mg;
if
(inc)
S_ithread_count_inc(aTHX_
thread
);
if
(! obj) {
obj = newSV(0);
}
sv = newSVrv(obj, classname);
sv_setiv(sv, PTR2IV(
thread
));
mg = sv_magicext(sv, Nullsv, PERL_MAGIC_shared_scalar, &ithread_vtbl, (
char
*)
thread
, 0);
mg->mg_flags |= MGf_DUP;
SvREADONLY_on(sv);
return
(obj);
}
STATIC ithread *
S_SV_to_ithread(pTHX_ SV *sv)
{
if
(SvROK(sv)) {
return
(INT2PTR(ithread *, SvIV(SvRV(sv))));
}
return
(S_ithread_get(aTHX));
}
STATIC ithread *
S_ithread_create(
PerlInterpreter *parent_perl,
my_pool_t *my_pool,
SV *init_function,
IV stack_size,
int
gimme,
int
exit_opt,
int
params_start,
int
num_params)
PERL_TSA_RELEASE(my_pool->create_destruct_mutex)
{
dTHXa(parent_perl);
ithread *
thread
;
ithread *current_thread = S_ithread_get(aTHX);
AV *params;
SV **array;
#if PERL_VERSION_LE(5,8,7)
SV **tmps_tmp = PL_tmps_stack;
IV tmps_ix = PL_tmps_ix;
#endif
#ifndef WIN32
int
rc_stack_size = 0;
int
rc_thread_create = 0;
#endif
{
PERL_SET_CONTEXT(my_pool->main_thread.interp);
thread
= (ithread *)PerlMemShared_malloc(
sizeof
(ithread));
}
PERL_SET_CONTEXT(aTHX);
if
(!
thread
) {
MUTEX_UNLOCK(&my_pool->create_destruct_mutex);
{
int
fd = PerlIO_fileno(Perl_error_log);
if
(fd >= 0) {
static
const
char
oomp[] =
"Out of memory in perl:threads:ithread_create\n"
;
PERL_UNUSED_RESULT(PerlLIO_write(fd, oomp,
sizeof
oomp - 1));
}
}
my_exit(1);
}
Zero(
thread
, 1, ithread);
thread
->next = &my_pool->main_thread;
thread
->prev = my_pool->main_thread.prev;
my_pool->main_thread.prev =
thread
;
thread
->prev->next =
thread
;
my_pool->total_threads++;
thread
->count = 3;
MUTEX_INIT(&
thread
->mutex);
MUTEX_LOCK(&
thread
->mutex);
thread
->tid = my_pool->tid_counter++;
thread
->stack_size = S_good_stack_size(aTHX_ stack_size);
thread
->gimme = gimme;
thread
->state = exit_opt;
PerlIO_flush((PerlIO *)NULL);
S_ithread_set(aTHX_
thread
);
SAVEBOOL(PL_srand_called);
PL_srand_called = FALSE;
#ifdef THREAD_SIGNAL_BLOCKING
S_block_most_signals(&
thread
->initial_sigmask);
#endif
#ifdef WIN32
thread
->interp = perl_clone(aTHX, CLONEf_KEEP_PTR_TABLE | CLONEf_CLONE_HOST);
#else
thread
->interp = perl_clone(aTHX, CLONEf_KEEP_PTR_TABLE);
#endif
{
#if PERL_VERSION_GE(5,13,2)
CLONE_PARAMS *clone_param = Perl_clone_params_new(aTHX,
thread
->interp);
#else
CLONE_PARAMS clone_param_s;
CLONE_PARAMS *clone_param = &clone_param_s;
#endif
dTHXa(
thread
->interp);
MY_CXT_CLONE;
#if PERL_VERSION_LT(5,13,2)
clone_param->flags = 0;
#endif
SvREFCNT_dec(PL_endav);
PL_endav = NULL;
if
(SvPOK(init_function)) {
thread
->init_function = newSV(0);
sv_copypv(
thread
->init_function, init_function);
}
else
{
thread
->init_function = sv_dup_inc(init_function, clone_param);
}
thread
->params = params = newAV();
av_extend(params, num_params - 1);
AvFILLp(params) = num_params - 1;
array = AvARRAY(params);
if
(num_params) {
#if PERL_VERSION_GE(5,9,0)
Copy(parent_perl->Istack_base + params_start, array, num_params, SV *);
#else
Copy(parent_perl->Tstack_base + params_start, array, num_params, SV *);
#endif
while
(num_params--) {
*array = sv_dup_inc(*array, clone_param);
++array;
}
}
#if PERL_VERSION_GE(5,13,2)
Perl_clone_params_del(clone_param);
#endif
#if PERL_VERSION_LT(5,8,8)
while
(tmps_ix > 0) {
SV* sv = (SV*)ptr_table_fetch(PL_ptr_table, tmps_tmp[tmps_ix]);
tmps_ix--;
if
(sv && SvREFCNT(sv) == 0) {
SvREFCNT_inc_void(sv);
SvREFCNT_dec(sv);
}
}
#endif
SvTEMP_off(
thread
->init_function);
ptr_table_free(PL_ptr_table);
PL_ptr_table = NULL;
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
}
S_ithread_set(aTHX_ current_thread);
PERL_SET_CONTEXT(aTHX);
#ifdef WIN32
thread
->handle = CreateThread(NULL,
(
DWORD
)
thread
->stack_size,
S_ithread_run,
(
LPVOID
)
thread
,
STACK_SIZE_PARAM_IS_A_RESERVATION,
&
thread
->thr);
#else
{
STATIC pthread_attr_t attr;
STATIC
int
attr_inited = 0;
STATIC
int
attr_joinable = PTHREAD_CREATE_JOINABLE;
if
(! attr_inited) {
pthread_attr_init(&attr);
attr_inited = 1;
}
# ifdef PTHREAD_ATTR_SETDETACHSTATE
PTHREAD_ATTR_SETDETACHSTATE(&attr, attr_joinable);
# endif
# ifdef _POSIX_THREAD_ATTR_STACKSIZE
if
(
thread
->stack_size > 0) {
rc_stack_size = pthread_attr_setstacksize(&attr, (
size_t
)
thread
->stack_size);
}
# endif
if
(! rc_stack_size) {
# ifdef OLD_PTHREADS_API
rc_thread_create = pthread_create(&
thread
->thr,
attr,
S_ithread_run,
(
void
*)
thread
);
# else
# if defined(HAS_PTHREAD_ATTR_SETSCOPE) && defined(PTHREAD_SCOPE_SYSTEM)
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
# endif
rc_thread_create = pthread_create(&
thread
->thr,
&attr,
S_ithread_run,
(
void
*)
thread
);
# endif
}
#ifdef THREAD_SIGNAL_BLOCKING
S_set_sigmask(&
thread
->initial_sigmask);
#endif
# ifdef _POSIX_THREAD_ATTR_STACKSIZE
{
size_t
stacksize;
#ifdef HPUX1020
stacksize = pthread_attr_getstacksize(attr);
#else
if
(! pthread_attr_getstacksize(&attr, &stacksize))
#endif
if
(stacksize > 0) {
thread
->stack_size = (IV)stacksize;
}
}
# endif
}
#endif
#ifdef WIN32
if
(
thread
->handle == NULL) {
#else
if
(rc_stack_size || rc_thread_create) {
#endif
MUTEX_UNLOCK(&my_pool->create_destruct_mutex);
thread
->state |= PERL_ITHR_NONVIABLE;
S_ithread_free(aTHX_
thread
);
#ifndef WIN32
if
(ckWARN_d(WARN_THREADS)) {
if
(rc_stack_size) {
Perl_warn(aTHX_
"Thread creation failed: pthread_attr_setstacksize(%"
IVdf
") returned %d"
,
thread
->stack_size, rc_stack_size);
}
else
{
Perl_warn(aTHX_
"Thread creation failed: pthread_create returned %d"
, rc_thread_create);
}
}
#endif
return
(NULL);
}
my_pool->running_threads++;
MUTEX_UNLOCK(&my_pool->create_destruct_mutex);
return
(
thread
);
CLANG_DIAG_IGNORE(-Wthread-safety)
}
CLANG_DIAG_RESTORE
#endif /* USE_ITHREADS */
MODULE = threads PACKAGE = threads PREFIX = ithread_
PROTOTYPES: DISABLE
#ifdef USE_ITHREADS
void
ithread_create(...)
PREINIT:
char
*classname;
ithread *
thread
;
SV *function_to_call;
HV *specs;
IV stack_size;
int
context;
int
exit_opt;
SV *thread_exit_only;
char
*str;
int
idx;
dMY_POOL;
CODE:
if
((items >= 2) && SvROK(ST(1)) && SvTYPE(SvRV(ST(1)))==SVt_PVHV) {
if
(--items < 2) {
Perl_croak(aTHX_
"Usage: threads->create(\\%%specs, function, ...)"
);
}
specs = (HV*)SvRV(ST(1));
idx = 1;
}
else
{
if
(items < 2) {
Perl_croak(aTHX_
"Usage: threads->create(function, ...)"
);
}
specs = NULL;
idx = 0;
}
if
(sv_isobject(ST(0))) {
classname = HvNAME(SvSTASH(SvRV(ST(0))));
thread
= INT2PTR(ithread *, SvIV(SvRV(ST(0))));
MUTEX_LOCK(&
thread
->mutex);
stack_size =
thread
->stack_size;
exit_opt =
thread
->state & PERL_ITHR_THREAD_EXIT_ONLY;
MUTEX_UNLOCK(&
thread
->mutex);
}
else
{
classname = (
char
*)SvPV_nolen(ST(0));
stack_size = MY_POOL.default_stack_size;
thread_exit_only = get_sv(
"threads::thread_exit_only"
, GV_ADD);
exit_opt = (SvTRUE(thread_exit_only))
? PERL_ITHR_THREAD_EXIT_ONLY : 0;
}
function_to_call = ST(idx+1);
context = -1;
if
(specs) {
SV **svp;
if
((svp = hv_fetchs(specs,
"stack"
, 0))) {
stack_size = SvIV(*svp);
}
else
if
((svp = hv_fetchs(specs,
"stacksize"
, 0))) {
stack_size = SvIV(*svp);
}
else
if
((svp = hv_fetchs(specs,
"stack_size"
, 0))) {
stack_size = SvIV(*svp);
}
if
((svp = hv_fetchs(specs,
"context"
, 0))) {
str = (
char
*)SvPV_nolen(*svp);
switch
(*str) {
case
'a'
:
case
'A'
:
case
'l'
:
case
'L'
:
context = G_LIST;
break
;
case
's'
:
case
'S'
:
context = G_SCALAR;
break
;
case
'v'
:
case
'V'
:
context = G_VOID;
break
;
default
:
Perl_croak(aTHX_
"Invalid context: %s"
, str);
}
}
else
if
((svp = hv_fetchs(specs,
"array"
, 0))) {
if
(SvTRUE(*svp)) {
context = G_LIST;
}
}
else
if
((svp = hv_fetchs(specs,
"list"
, 0))) {
if
(SvTRUE(*svp)) {
context = G_LIST;
}
}
else
if
((svp = hv_fetchs(specs,
"scalar"
, 0))) {
if
(SvTRUE(*svp)) {
context = G_SCALAR;
}
}
else
if
((svp = hv_fetchs(specs,
"void"
, 0))) {
if
(SvTRUE(*svp)) {
context = G_VOID;
}
}
if
((svp = hv_fetchs(specs,
"exit"
, 0))) {
str = (
char
*)SvPV_nolen(*svp);
exit_opt = (*str ==
't'
|| *str ==
'T'
)
? PERL_ITHR_THREAD_EXIT_ONLY : 0;
}
}
if
(context == -1) {
context = GIMME_V;
}
else
{
context |= (GIMME_V & (~(G_LIST|G_SCALAR|G_VOID)));
}
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
thread
= S_ithread_create(aTHX_ &MY_POOL,
function_to_call,
stack_size,
context,
exit_opt,
ax + idx + 2,
items > 2 ? items - 2 : 0);
if
(!
thread
) {
XSRETURN_UNDEF;
}
PERL_SRAND_OVERRIDE_NEXT_PARENT();
ST(0) = sv_2mortal(S_ithread_to_SV(aTHX_ Nullsv,
thread
, classname, FALSE));
CLANG_DIAG_IGNORE_STMT(-Wthread-safety);
MUTEX_UNLOCK(&
thread
->mutex);
CLANG_DIAG_RESTORE_STMT;
void
ithread_list(...)
PREINIT:
char
*classname;
ithread *
thread
;
int
list_context;
IV count = 0;
int
want_running = 0;
int
state;
dMY_POOL;
PPCODE:
if
(SvROK(ST(0))) {
Perl_croak(aTHX_
"Usage: threads->list(...)"
);
}
classname = (
char
*)SvPV_nolen(ST(0));
list_context = (GIMME_V == G_LIST);
if
(items > 1) {
want_running = SvTRUE(ST(1));
}
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
for
(
thread
= MY_POOL.main_thread.next;
thread
!= &MY_POOL.main_thread;
thread
=
thread
->next)
{
MUTEX_LOCK(&
thread
->mutex);
state =
thread
->state;
MUTEX_UNLOCK(&
thread
->mutex);
if
(state & PERL_ITHR_UNCALLABLE) {
continue
;
}
if
(items > 1) {
if
(want_running) {
if
(state & PERL_ITHR_FINISHED) {
continue
;
}
}
else
{
if
(! (state & PERL_ITHR_FINISHED)) {
continue
;
}
}
}
if
(list_context) {
XPUSHs(sv_2mortal(S_ithread_to_SV(aTHX_ Nullsv,
thread
, classname, TRUE)));
}
count++;
}
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
if
(! list_context) {
XSRETURN_IV(count);
}
void
ithread_self(...)
PREINIT:
char
*classname;
ithread *
thread
;
CODE:
if
((items != 1) || SvROK(ST(0))) {
Perl_croak(aTHX_
"Usage: threads->self()"
);
}
classname = (
char
*)SvPV_nolen(ST(0));
thread
= S_ithread_get(aTHX);
ST(0) = sv_2mortal(S_ithread_to_SV(aTHX_ Nullsv,
thread
, classname, TRUE));
void
ithread_tid(...)
PREINIT:
ithread *
thread
;
CODE:
PERL_UNUSED_VAR(items);
thread
= S_SV_to_ithread(aTHX_ ST(0));
XST_mUV(0,
thread
->tid);
void
ithread_join(...)
PREINIT:
ithread *
thread
;
ithread *current_thread;
int
join_err;
AV *params = NULL;
int
len;
int
ii;
#ifndef WIN32
int
rc_join;
void
*retval;
#endif
dMY_POOL;
PPCODE:
if
((items != 1) || ! sv_isobject(ST(0))) {
Perl_croak(aTHX_
"Usage: $thr->join()"
);
}
thread
= S_SV_to_ithread(aTHX_ ST(0));
current_thread = S_ithread_get(aTHX);
MUTEX_LOCK(&
thread
->mutex);
if
((join_err = (
thread
->state & PERL_ITHR_UNCALLABLE))) {
MUTEX_UNLOCK(&
thread
->mutex);
Perl_croak(aTHX_ (join_err & PERL_ITHR_DETACHED)
?
"Cannot join a detached thread"
:
"Thread already joined"
);
}
else
if
(
thread
->tid == current_thread->tid) {
MUTEX_UNLOCK(&
thread
->mutex);
Perl_croak(aTHX_
"Cannot join self"
);
}
thread
->state |= PERL_ITHR_JOINED;
MUTEX_UNLOCK(&
thread
->mutex);
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
MY_POOL.joinable_threads--;
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
#ifdef WIN32
if
(WaitForSingleObject(
thread
->handle, INFINITE) != WAIT_OBJECT_0) {
Perl_croak(aTHX_
"PANIC: underlying join failed"
);
};
#else
if
((rc_join = pthread_join(
thread
->thr, &retval)) != 0) {
errno
= rc_join;
Perl_croak(aTHX_
"PANIC: underlying join failed"
);
};
#endif
MUTEX_LOCK(&
thread
->mutex);
if
((
thread
->gimme & G_WANT) != G_VOID) {
#if PERL_VERSION_LT(5,13,2)
AV *params_copy;
PerlInterpreter *other_perl;
CLONE_PARAMS clone_params;
params_copy =
thread
->params;
other_perl =
thread
->interp;
clone_params.stashes = newAV();
clone_params.flags = CLONEf_JOIN_IN;
PL_ptr_table = ptr_table_new();
S_ithread_set(aTHX_
thread
);
ptr_table_store(PL_ptr_table, &other_perl->Isv_undef, &PL_sv_undef);
ptr_table_store(PL_ptr_table, &other_perl->Isv_no, &PL_sv_no);
ptr_table_store(PL_ptr_table, &other_perl->Isv_yes, &PL_sv_yes);
params = (AV *)sv_dup((SV*)params_copy, &clone_params);
S_ithread_set(aTHX_ current_thread);
SvREFCNT_dec(clone_params.stashes);
SvREFCNT_inc_void(params);
ptr_table_free(PL_ptr_table);
PL_ptr_table = NULL;
#else
AV *params_copy;
PerlInterpreter *other_perl =
thread
->interp;
CLONE_PARAMS *clone_params = Perl_clone_params_new(other_perl, aTHX);
params_copy =
thread
->params;
clone_params->flags |= CLONEf_JOIN_IN;
PL_ptr_table = ptr_table_new();
S_ithread_set(aTHX_
thread
);
ptr_table_store(PL_ptr_table, &other_perl->Isv_undef, &PL_sv_undef);
ptr_table_store(PL_ptr_table, &other_perl->Isv_no, &PL_sv_no);
ptr_table_store(PL_ptr_table, &other_perl->Isv_yes, &PL_sv_yes);
# ifdef PL_sv_zero
ptr_table_store(PL_ptr_table, &other_perl->Isv_zero, &PL_sv_zero);
# endif
params = (AV *)sv_dup((SV*)params_copy, clone_params);
S_ithread_set(aTHX_ current_thread);
Perl_clone_params_del(clone_params);
SvREFCNT_inc_void(params);
ptr_table_free(PL_ptr_table);
PL_ptr_table = NULL;
#endif
}
if
(! (
thread
->state & PERL_ITHR_DIED)) {
S_ithread_clear(aTHX_
thread
);
}
S_ithread_free(aTHX_
thread
);
if
(! params) {
XSRETURN_UNDEF;
}
len = (
int
)AvFILL(params);
for
(ii=0; ii <= len; ii++) {
SV* param = av_shift(params);
XPUSHs(sv_2mortal(param));
}
SvREFCNT_dec(params);
void
ithread_yield(...)
CODE:
PERL_UNUSED_VAR(items);
YIELD;
void
ithread_detach(...)
PREINIT:
ithread *
thread
;
int
detach_err;
dMY_POOL;
CODE:
PERL_UNUSED_VAR(items);
thread
= S_SV_to_ithread(aTHX_ ST(0));
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
MUTEX_LOCK(&
thread
->mutex);
if
(! (detach_err = (
thread
->state & PERL_ITHR_UNCALLABLE))) {
thread
->state |= PERL_ITHR_DETACHED;
#ifdef WIN32
#else
PERL_THREAD_DETACH(
thread
->thr);
#endif
if
(
thread
->state & PERL_ITHR_FINISHED) {
MY_POOL.joinable_threads--;
}
else
{
MY_POOL.running_threads--;
MY_POOL.detached_threads++;
}
}
MUTEX_UNLOCK(&
thread
->mutex);
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
if
(detach_err) {
Perl_croak(aTHX_ (detach_err & PERL_ITHR_DETACHED)
?
"Thread already detached"
:
"Cannot detach a joined thread"
);
}
MUTEX_LOCK(&
thread
->mutex);
if
((
thread
->state & PERL_ITHR_FINISHED) &&
! (
thread
->state & PERL_ITHR_DIED))
{
S_ithread_clear(aTHX_
thread
);
}
S_ithread_free(aTHX_
thread
);
void
ithread_kill(...)
PREINIT:
ithread *
thread
;
char
*sig_name;
IV
signal
;
int
no_handler = 1;
CODE:
if
(PL_signals & PERL_SIGNALS_UNSAFE_FLAG) {
Perl_croak(aTHX_
"Cannot signal threads without safe signals"
);
}
if
((items != 2) || ! sv_isobject(ST(0))) {
Perl_croak(aTHX_
"Usage: $thr->kill('SIG...')"
);
}
sig_name = SvPV_nolen(ST(1));
if
(isALPHA(*sig_name)) {
if
(*sig_name ==
'S'
&& sig_name[1] ==
'I'
&& sig_name[2] ==
'G'
) {
sig_name += 3;
}
if
((
signal
= whichsig(sig_name)) < 0) {
Perl_croak(aTHX_
"Unrecognized signal name: %s"
, sig_name);
}
}
else
{
signal
= SvIV(ST(1));
}
thread
= S_SV_to_ithread(aTHX_ ST(0));
MUTEX_LOCK(&
thread
->mutex);
if
(
thread
->interp && ! (
thread
->state & PERL_ITHR_FINISHED)) {
dTHXa(
thread
->interp);
if
(PL_psig_pend && PL_psig_ptr[
signal
]) {
PL_psig_pend[
signal
]++;
PL_sig_pending = 1;
no_handler = 0;
}
}
else
{
no_handler = 0;
}
MUTEX_UNLOCK(&
thread
->mutex);
if
(no_handler) {
Perl_croak(aTHX_
"Signal %s received in thread %"
UVuf
", but no signal handler set."
,
sig_name,
thread
->tid);
}
ST(0) = ST(0);
void
ithread_DESTROY(...)
CODE:
PERL_UNUSED_VAR(items);
sv_unmagic(SvRV(ST(0)), PERL_MAGIC_shared_scalar);
void
ithread_equal(...)
PREINIT:
int
are_equal = 0;
CODE:
PERL_UNUSED_VAR(items);
if
(sv_isobject(ST(0)) && sv_isobject(ST(1))) {
ithread *thr1 = INT2PTR(ithread *, SvIV(SvRV(ST(0))));
ithread *thr2 = INT2PTR(ithread *, SvIV(SvRV(ST(1))));
are_equal = (thr1->tid == thr2->tid);
}
if
(are_equal) {
XST_mYES(0);
}
else
{
XST_mIV(0, 0);
}
void
ithread_object(...)
PREINIT:
char
*classname;
SV *arg;
UV tid;
ithread *
thread
;
int
state;
int
have_obj = 0;
dMY_POOL;
CODE:
if
(SvROK(ST(0))) {
Perl_croak(aTHX_
"Usage: threads->object($tid)"
);
}
classname = (
char
*)SvPV_nolen(ST(0));
if
(items < 2) {
XSRETURN_UNDEF;
}
arg = ST(1);
SvGETMAGIC(arg);
if
(! SvOK(arg)) {
XSRETURN_UNDEF;
}
tid = SvUV(arg);
thread
= S_ithread_get(aTHX);
if
(
thread
->tid == tid) {
ST(0) = sv_2mortal(S_ithread_to_SV(aTHX_ Nullsv,
thread
, classname, TRUE));
have_obj = 1;
}
else
{
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
for
(
thread
= MY_POOL.main_thread.next;
thread
!= &MY_POOL.main_thread;
thread
=
thread
->next)
{
if
(
thread
->tid == tid) {
MUTEX_LOCK(&
thread
->mutex);
state =
thread
->state;
MUTEX_UNLOCK(&
thread
->mutex);
if
(! (state & PERL_ITHR_UNCALLABLE)) {
ST(0) = sv_2mortal(S_ithread_to_SV(aTHX_ Nullsv,
thread
, classname, TRUE));
have_obj = 1;
}
break
;
}
}
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
}
if
(! have_obj) {
XSRETURN_UNDEF;
}
void
ithread__handle(...);
PREINIT:
ithread *
thread
;
CODE:
PERL_UNUSED_VAR(items);
thread
= S_SV_to_ithread(aTHX_ ST(0));
#ifdef WIN32
XST_mUV(0, PTR2UV(&
thread
->handle));
#else
XST_mUV(0, PTR2UV(&
thread
->thr));
#endif
void
ithread_get_stack_size(...)
PREINIT:
IV stack_size;
dMY_POOL;
CODE:
PERL_UNUSED_VAR(items);
if
(sv_isobject(ST(0))) {
ithread *
thread
= INT2PTR(ithread *, SvIV(SvRV(ST(0))));
stack_size =
thread
->stack_size;
}
else
{
stack_size = MY_POOL.default_stack_size;
}
XST_mIV(0, stack_size);
void
ithread_set_stack_size(...)
PREINIT:
IV old_size;
dMY_POOL;
CODE:
if
(items != 2) {
Perl_croak(aTHX_
"Usage: threads->set_stack_size($size)"
);
}
if
(sv_isobject(ST(0))) {
Perl_croak(aTHX_
"Cannot change stack size of an existing thread"
);
}
if
(! looks_like_number(ST(1))) {
Perl_croak(aTHX_
"Stack size must be numeric"
);
}
old_size = MY_POOL.default_stack_size;
MY_POOL.default_stack_size = S_good_stack_size(aTHX_ SvIV(ST(1)));
XST_mIV(0, old_size);
void
ithread_is_running(...)
PREINIT:
ithread *
thread
;
CODE:
if
((items != 1) || ! sv_isobject(ST(0))) {
Perl_croak(aTHX_
"Usage: $thr->is_running()"
);
}
thread
= INT2PTR(ithread *, SvIV(SvRV(ST(0))));
MUTEX_LOCK(&
thread
->mutex);
ST(0) = (
thread
->state & PERL_ITHR_FINISHED) ? &PL_sv_no : &PL_sv_yes;
MUTEX_UNLOCK(&
thread
->mutex);
void
ithread_is_detached(...)
PREINIT:
ithread *
thread
;
CODE:
PERL_UNUSED_VAR(items);
thread
= S_SV_to_ithread(aTHX_ ST(0));
MUTEX_LOCK(&
thread
->mutex);
ST(0) = (
thread
->state & PERL_ITHR_DETACHED) ? &PL_sv_yes : &PL_sv_no;
MUTEX_UNLOCK(&
thread
->mutex);
void
ithread_is_joinable(...)
PREINIT:
ithread *
thread
;
CODE:
if
((items != 1) || ! sv_isobject(ST(0))) {
Perl_croak(aTHX_
"Usage: $thr->is_joinable()"
);
}
thread
= INT2PTR(ithread *, SvIV(SvRV(ST(0))));
MUTEX_LOCK(&
thread
->mutex);
ST(0) = ((
thread
->state & PERL_ITHR_FINISHED) &&
! (
thread
->state & PERL_ITHR_UNCALLABLE))
? &PL_sv_yes : &PL_sv_no;
MUTEX_UNLOCK(&
thread
->mutex);
void
ithread_wantarray(...)
PREINIT:
ithread *
thread
;
CODE:
PERL_UNUSED_VAR(items);
thread
= S_SV_to_ithread(aTHX_ ST(0));
ST(0) = ((
thread
->gimme & G_WANT) == G_LIST) ? &PL_sv_yes :
((
thread
->gimme & G_WANT) == G_VOID) ? &PL_sv_undef
: &PL_sv_no;
void
ithread_set_thread_exit_only(...)
PREINIT:
ithread *
thread
;
CODE:
if
(items != 2) {
Perl_croak(aTHX_
"Usage: ->set_thread_exit_only(boolean)"
);
}
thread
= S_SV_to_ithread(aTHX_ ST(0));
MUTEX_LOCK(&
thread
->mutex);
if
(SvTRUE(ST(1))) {
thread
->state |= PERL_ITHR_THREAD_EXIT_ONLY;
}
else
{
thread
->state &= ~PERL_ITHR_THREAD_EXIT_ONLY;
}
MUTEX_UNLOCK(&
thread
->mutex);
void
ithread_error(...)
PREINIT:
ithread *
thread
;
SV *err = NULL;
CODE:
if
((items != 1) || ! sv_isobject(ST(0))) {
Perl_croak(aTHX_
"Usage: $thr->err()"
);
}
thread
= INT2PTR(ithread *, SvIV(SvRV(ST(0))));
MUTEX_LOCK(&
thread
->mutex);
if
(
thread
->state & PERL_ITHR_DIED) {
#if PERL_VERSION_LT(5,13,2)
PerlInterpreter *other_perl;
CLONE_PARAMS clone_params;
ithread *current_thread;
other_perl =
thread
->interp;
clone_params.stashes = newAV();
clone_params.flags = CLONEf_JOIN_IN;
PL_ptr_table = ptr_table_new();
current_thread = S_ithread_get(aTHX);
S_ithread_set(aTHX_
thread
);
ptr_table_store(PL_ptr_table, &other_perl->Isv_undef, &PL_sv_undef);
ptr_table_store(PL_ptr_table, &other_perl->Isv_no, &PL_sv_no);
ptr_table_store(PL_ptr_table, &other_perl->Isv_yes, &PL_sv_yes);
err = sv_dup(
thread
->err, &clone_params);
S_ithread_set(aTHX_ current_thread);
SvREFCNT_dec(clone_params.stashes);
SvREFCNT_inc_void(err);
if
(
thread
->err_class) {
sv_bless(err, gv_stashpv(
thread
->err_class, 1));
}
ptr_table_free(PL_ptr_table);
PL_ptr_table = NULL;
#else
PerlInterpreter *other_perl =
thread
->interp;
CLONE_PARAMS *clone_params = Perl_clone_params_new(other_perl, aTHX);
ithread *current_thread;
clone_params->flags |= CLONEf_JOIN_IN;
PL_ptr_table = ptr_table_new();
current_thread = S_ithread_get(aTHX);
S_ithread_set(aTHX_
thread
);
ptr_table_store(PL_ptr_table, &other_perl->Isv_undef, &PL_sv_undef);
ptr_table_store(PL_ptr_table, &other_perl->Isv_no, &PL_sv_no);
ptr_table_store(PL_ptr_table, &other_perl->Isv_yes, &PL_sv_yes);
# ifdef PL_sv_zero
ptr_table_store(PL_ptr_table, &other_perl->Isv_zero, &PL_sv_zero);
# endif
err = sv_dup(
thread
->err, clone_params);
S_ithread_set(aTHX_ current_thread);
Perl_clone_params_del(clone_params);
SvREFCNT_inc_void(err);
if
(
thread
->err_class) {
sv_bless(err, gv_stashpv(
thread
->err_class, 1));
}
ptr_table_free(PL_ptr_table);
PL_ptr_table = NULL;
#endif
}
MUTEX_UNLOCK(&
thread
->mutex);
if
(! err) {
XSRETURN_UNDEF;
}
ST(0) = sv_2mortal(err);
#endif /* USE_ITHREADS */
BOOT:
{
#ifdef USE_ITHREADS
SV *my_pool_sv = *hv_fetch(PL_modglobal, MY_POOL_KEY,
sizeof
(MY_POOL_KEY)-1, TRUE);
my_pool_t *my_poolp = (my_pool_t*)SvPVX(newSV(
sizeof
(my_pool_t)-1));
MY_CXT_INIT;
Zero(my_poolp, 1, my_pool_t);
sv_setuv(my_pool_sv, PTR2UV(my_poolp));
PL_perl_destruct_level = 2;
MUTEX_INIT(&MY_POOL.create_destruct_mutex);
MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
PL_threadhook = &Perl_ithread_hook;
MY_POOL.tid_counter = 1;
# ifdef THREAD_CREATE_NEEDS_STACK
MY_POOL.default_stack_size = THREAD_CREATE_NEEDS_STACK;
# endif
MUTEX_INIT(&MY_POOL.main_thread.mutex);
MY_POOL.main_thread.next = &MY_POOL.main_thread;
MY_POOL.main_thread.prev = &MY_POOL.main_thread;
MY_POOL.main_thread.count = 1;
MY_POOL.main_thread.interp = aTHX;
MY_POOL.main_thread.state = PERL_ITHR_DETACHED;
MY_POOL.main_thread.stack_size = MY_POOL.default_stack_size;
# ifdef WIN32
MY_POOL.main_thread.thr = GetCurrentThreadId();
# else
MY_POOL.main_thread.thr = pthread_self();
# endif
S_ithread_set(aTHX_ &MY_POOL.main_thread);
MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
#endif /* USE_ITHREADS */
}