X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/313e3311ff463cfb1de02a3e944a3a32a6497023..1e8e823624ada1d9231e47a66cb2b9e3ab42701a:/ext/threads/shared/shared.xs diff --git a/ext/threads/shared/shared.xs b/ext/threads/shared/shared.xs index 9ef9101..911d9ca 100644 --- a/ext/threads/shared/shared.xs +++ b/ext/threads/shared/shared.xs @@ -1,599 +1,1307 @@ +/* shared.xs + * + * Copyright (c) 2001-2002, Larry Wall + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + * + * "Hand any two wizards a piece of rope and they would instinctively pull in + * opposite directions." + * --Sourcery + * + * Contributed by Arthur Bergman arthur@contiller.se + * pulled in the (an)other direction by Nick Ing-Simmons nick@ing-simmons.net + */ +#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" +#ifdef USE_ITHREADS -void shared_sv_attach_sv (SV* sv, shared_sv* shared) { - HV* shared_hv = get_hv("threads::shared::shared", FALSE); - SV* id = newSViv((IV)shared); - STRLEN length = sv_len(id); - SV* tiedobject; - SV** tiedobject_ = hv_fetch(shared_hv, SvPV(id,length), length, 0); - if(tiedobject_) { - tiedobject = (*tiedobject_); - SvROK_on(sv); - SvRV(sv) = SvRV(tiedobject); +#define SHAREDSvPTR(a) ((a)->sv) - } else { - croak("die\n"); - } +/* + * The shared things need an intepreter to live in ... + */ +PerlInterpreter *PL_sharedsv_space; /* The shared sv space */ +/* To access shared space we fake aTHX in this scope and thread's context */ + +/* bug #24255: we include ENTER+SAVETMPS/FREETMPS+LEAVE with + * SHARED_CONTEXT/CALLER_CONTEXT macros, so that any mortals etc created + * while in the shared interpreter context don't languish */ + +#define SHARED_CONTEXT \ + STMT_START { \ + PERL_SET_CONTEXT((aTHX = PL_sharedsv_space)); \ + ENTER; \ + SAVETMPS; \ + } STMT_END + +/* So we need a way to switch back to the caller's context... */ +/* So we declare _another_ copy of the aTHX variable ... */ +#define dTHXc PerlInterpreter *caller_perl = aTHX + +/* and use it to switch back */ +#define CALLER_CONTEXT \ + STMT_START { \ + FREETMPS; \ + LEAVE; \ + PERL_SET_CONTEXT((aTHX = caller_perl)); \ + } STMT_END + +/* + * Only one thread at a time is allowed to mess with shared space. + */ + +typedef struct +{ + perl_mutex mutex; + PerlInterpreter *owner; + I32 locks; + perl_cond cond; +#ifdef DEBUG_LOCKS + char * file; + int line; +#endif +} recursive_lock_t; + +recursive_lock_t PL_sharedsv_lock; /* Mutex protecting the shared sv space */ + +void +recursive_lock_init(pTHX_ recursive_lock_t *lock) +{ + Zero(lock,1,recursive_lock_t); + MUTEX_INIT(&lock->mutex); + COND_INIT(&lock->cond); } +void +recursive_lock_destroy(pTHX_ recursive_lock_t *lock) +{ + MUTEX_DESTROY(&lock->mutex); + COND_DESTROY(&lock->cond); +} -int shared_sv_fetch_mg (pTHX_ SV* sv, MAGIC *mg) { - shared_sv* shared = (shared_sv*) SvIV(mg->mg_obj); - SHAREDSvLOCK(shared); - if(mg->mg_private != shared->index) { - if(SvROK(SHAREDSvGET(shared))) { - shared_sv* target = (shared_sv*) SvIV(SvRV(SHAREDSvGET(shared))); - shared_sv_attach_sv(sv, target); - } else { - sv_setsv(sv, SHAREDSvGET(shared)); +void +recursive_lock_release(pTHX_ recursive_lock_t *lock) +{ + MUTEX_LOCK(&lock->mutex); + if (lock->owner != aTHX) { + MUTEX_UNLOCK(&lock->mutex); + } + else { + if (--lock->locks == 0) { + lock->owner = NULL; + COND_SIGNAL(&lock->cond); + } + } + MUTEX_UNLOCK(&lock->mutex); +} + +void +recursive_lock_acquire(pTHX_ recursive_lock_t *lock,char *file,int line) +{ + assert(aTHX); + MUTEX_LOCK(&lock->mutex); + if (lock->owner == aTHX) { + lock->locks++; + } + else { + while (lock->owner) { +#ifdef DEBUG_LOCKS + Perl_warn(aTHX_ " %p waiting - owned by %p %s:%d\n", + aTHX, lock->owner, lock->file, lock->line); +#endif + COND_WAIT(&lock->cond,&lock->mutex); } - mg->mg_private = shared->index; + lock->locks = 1; + lock->owner = aTHX; +#ifdef DEBUG_LOCKS + lock->file = file; + lock->line = line; +#endif } - SHAREDSvUNLOCK(shared); + MUTEX_UNLOCK(&lock->mutex); + SAVEDESTRUCTOR_X(recursive_lock_release,lock); +} + +#define ENTER_LOCK STMT_START { \ + ENTER; \ + recursive_lock_acquire(aTHX_ &PL_sharedsv_lock, __FILE__, __LINE__); \ + } STMT_END + +#define LEAVE_LOCK LEAVE + + +/* A common idiom is to acquire access and switch in ... */ +#define SHARED_EDIT STMT_START { \ + ENTER_LOCK; \ + SHARED_CONTEXT; \ + } STMT_END + +/* then switch out and release access. */ +#define SHARED_RELEASE STMT_START { \ + CALLER_CONTEXT; \ + LEAVE_LOCK; \ + } STMT_END + + +/* + + Shared SV + Shared SV is a structure for keeping the backend storage + of shared svs. + + Shared-ness really only needs the SV * - the rest is for locks. + (Which suggests further space optimization ... ) + +*/ + +typedef struct { + SV *sv; /* The actual SV - in shared space */ + recursive_lock_t lock; + perl_cond user_cond; /* For user-level conditions */ +} shared_sv; + +/* The SV in shared-space has a back-pointer to the shared_sv + struct associated with it PERL_MAGIC_ext. + + The vtable used has just one entry - when the SV goes away + we free the memory for the above. + + */ + +int +sharedsv_shared_mg_free(pTHX_ SV *sv, MAGIC *mg) +{ + shared_sv *shared = (shared_sv *) mg->mg_ptr; + assert( aTHX == PL_sharedsv_space ); + if (shared) { + recursive_lock_destroy(aTHX_ &shared->lock); + COND_DESTROY(&shared->user_cond); + PerlMemShared_free(shared); + mg->mg_ptr = NULL; + } return 0; } -int shared_sv_store_mg (pTHX_ SV* sv, MAGIC *mg) { - shared_sv* shared = (shared_sv*) SvIV(mg->mg_obj); - SHAREDSvLOCK(shared); - if(SvROK(SHAREDSvGET(shared))) - Perl_sharedsv_thrcnt_dec(aTHX_ (shared_sv*) SvIV(SvRV(SHAREDSvGET(shared)))); - if(SvROK(sv)) { - shared_sv* target = Perl_sharedsv_find(aTHX_ SvRV(sv)); - if(!target) { - sv_setsv(sv,SHAREDSvGET(shared)); - SHAREDSvUNLOCK(shared); - Perl_croak(aTHX_ "You cannot assign a non shared reference to a shared scalar"); - } - SHAREDSvEDIT(shared); - Perl_sv_free(PL_sharedsv_space,SHAREDSvGET(shared)); - SHAREDSvGET(shared) = newRV_noinc(newSViv((IV)target)); - } else { - SHAREDSvEDIT(shared); - sv_setsv(SHAREDSvGET(shared), sv); - } - shared->index++; - mg->mg_private = shared->index; - SHAREDSvRELEASE(shared); - if(SvROK(SHAREDSvGET(shared))) - Perl_sharedsv_thrcnt_inc(aTHX_ (shared_sv*) SvIV(SvRV(SHAREDSvGET(shared)))); - SHAREDSvUNLOCK(shared); - return 0; +MGVTBL sharedsv_shared_vtbl = { + 0, /* get */ + 0, /* set */ + 0, /* len */ + 0, /* clear */ + sharedsv_shared_mg_free, /* free */ + 0, /* copy */ + 0, /* dup */ +}; + +/* Access to shared things is heavily based on MAGIC - in mg.h/mg.c/sv.c sense */ + +/* In any thread that has access to a shared thing there is a "proxy" + for it in its own space which has 'MAGIC' associated which accesses + the shared thing. + */ + +MGVTBL sharedsv_scalar_vtbl; /* scalars have this vtable */ +MGVTBL sharedsv_array_vtbl; /* hashes and arrays have this - like 'tie' */ +MGVTBL sharedsv_elem_vtbl; /* elements of hashes and arrays have this + _AS WELL AS_ the scalar magic */ + +/* The sharedsv_elem_vtbl associates the element with the array/hash and + the sharedsv_scalar_vtbl associates it with the value + */ + + +/* Accessor to convert threads::shared::tie objects back shared_sv * */ +shared_sv * +SV_to_sharedsv(pTHX_ SV *sv) +{ + shared_sv *shared = 0; + if (SvROK(sv)) + { + shared = INT2PTR(shared_sv *, SvIV(SvRV(sv))); + } + return shared; } -int shared_sv_destroy_mg (pTHX_ SV* sv, MAGIC *mg) { - shared_sv* shared = (shared_sv*) SvIV(mg->mg_obj); - if(!shared) - return 0; - Perl_sharedsv_thrcnt_dec(aTHX_ shared); +=for apidoc sharedsv_find + +Given a private side SV tries to find if the SV has a shared backend, +by looking for the magic. + +=cut + +shared_sv * +Perl_sharedsv_find(pTHX_ SV *sv) +{ + MAGIC *mg; + if (SvTYPE(sv) >= SVt_PVMG) { + switch(SvTYPE(sv)) { + case SVt_PVAV: + case SVt_PVHV: + if ((mg = mg_find(sv, PERL_MAGIC_tied)) + && mg->mg_virtual == &sharedsv_array_vtbl) { + return (shared_sv *) mg->mg_ptr; + } + break; + default: + /* This should work for elements as well as they + * have scalar magic as well as their element magic + */ + if ((mg = mg_find(sv, PERL_MAGIC_shared_scalar)) + && mg->mg_virtual == &sharedsv_scalar_vtbl) { + return (shared_sv *) mg->mg_ptr; + } + break; + } + } + /* Just for tidyness of API also handle tie objects */ + if (SvROK(sv) && sv_derived_from(sv, "threads::shared::tie")) { + return SV_to_sharedsv(aTHX_ sv); + } + return NULL; } -MGVTBL svtable = {MEMBER_TO_FPTR(shared_sv_fetch_mg), - MEMBER_TO_FPTR(shared_sv_store_mg), - 0, - 0, - MEMBER_TO_FPTR(shared_sv_destroy_mg) -}; +/* + * Almost all the pain is in this routine. + * + */ -MODULE = threads::shared PACKAGE = threads::shared +shared_sv * +Perl_sharedsv_associate(pTHX_ SV **psv, SV *ssv, shared_sv *data) +{ + dTHXc; + MAGIC *mg = 0; + SV *sv = (psv) ? *psv : Nullsv; + /* If we are asked for an private ops we need a thread */ + assert ( aTHX != PL_sharedsv_space ); -PROTOTYPES: DISABLE + /* To avoid need for recursive locks require caller to hold lock */ + assert ( PL_sharedsv_lock.owner == aTHX ); + /* First try and get existing global data structure */ -SV* -ptr(ref) - SV* ref - CODE: - RETVAL = newSViv(SvIV(SvRV(ref))); - OUTPUT: - RETVAL + /* Try shared SV as 1st choice */ + if (!data && ssv && SvTYPE(ssv) >= SVt_PVMG) { + if( (mg = mg_find(ssv, PERL_MAGIC_ext)) ){ + data = (shared_sv *) mg->mg_ptr; + } + } + /* Next see if private SV is associated with something */ + if (!data && sv) { + data = Perl_sharedsv_find(aTHX_ sv); + } -SV* -_thrcnt(ref) - SV* ref - CODE: - shared_sv* shared = Perl_sharedsv_find(aTHX, ref); - if(!shared) - croak("thrcnt can only be used on shared values"); - SHAREDSvLOCK(shared); - RETVAL = newSViv(SvREFCNT(SHAREDSvGET(shared))); - SHAREDSvUNLOCK(shared); - OUTPUT: - RETVAL + /* If neither of those then create a new one */ + if (!data) { + SHARED_CONTEXT; + if (!ssv) { + ssv = newSV(0); + SvREFCNT(ssv) = 0; + } + data = PerlMemShared_malloc(sizeof(shared_sv)); + Zero(data,1,shared_sv); + SHAREDSvPTR(data) = ssv; + /* Tag shared side SV with data pointer */ + sv_magicext(ssv, ssv, PERL_MAGIC_ext, &sharedsv_shared_vtbl, + (char *)data, 0); + recursive_lock_init(aTHX_ &data->lock); + COND_INIT(&data->user_cond); + CALLER_CONTEXT; + } + + if (!ssv) + ssv = SHAREDSvPTR(data); + if (!SHAREDSvPTR(data)) + SHAREDSvPTR(data) = ssv; + + /* If we know type upgrade shared side SV */ + if (sv && SvTYPE(ssv) < SvTYPE(sv)) { + SHARED_CONTEXT; + sv_upgrade(ssv, SvTYPE(*psv)); + if (SvTYPE(ssv) == SVt_PVAV) /* #24061 */ + AvREAL_on(ssv); + CALLER_CONTEXT; + } + /* Now if requested allocate private SV */ + if (psv && !sv) { + *psv = sv = newSV(0); + } + + /* Finally if private SV exists check and add magic */ + if (sv) { + MAGIC *mg = 0; + if (SvTYPE(sv) < SvTYPE(ssv)) { + sv_upgrade(sv, SvTYPE(ssv)); + } + switch(SvTYPE(sv)) { + case SVt_PVAV: + case SVt_PVHV: + if (!(mg = mg_find(sv, PERL_MAGIC_tied)) + || mg->mg_virtual != &sharedsv_array_vtbl + || (shared_sv *) mg->mg_ptr != data) { + SV *obj = newSV(0); + sv_setref_iv(obj, "threads::shared::tie",PTR2IV(data)); + if (mg) { + sv_unmagic(sv, PERL_MAGIC_tied); + } + mg = sv_magicext(sv, obj, PERL_MAGIC_tied, &sharedsv_array_vtbl, + (char *) data, 0); + mg->mg_flags |= (MGf_COPY|MGf_DUP); + SvREFCNT_inc(ssv); + SvREFCNT_dec(obj); + if(SvOBJECT(ssv)) { + STRLEN len; + char* stash_ptr = SvPV((SV*) SvSTASH(ssv), len); + HV* stash = gv_stashpvn(stash_ptr, len, TRUE); + SvOBJECT_on(sv); + SvSTASH(sv) = (HV*)SvREFCNT_inc(stash); + } + } + break; + + default: + if ((SvTYPE(sv) < SVt_PVMG) + || !(mg = mg_find(sv, PERL_MAGIC_shared_scalar)) + || mg->mg_virtual != &sharedsv_scalar_vtbl + || (shared_sv *) mg->mg_ptr != data) { + if (mg) { + sv_unmagic(sv, PERL_MAGIC_shared_scalar); + } + mg = sv_magicext(sv, Nullsv, PERL_MAGIC_shared_scalar, + &sharedsv_scalar_vtbl, (char *)data, 0); + mg->mg_flags |= (MGf_COPY|MGf_DUP); + SvREFCNT_inc(ssv); + } + break; + } + assert ( Perl_sharedsv_find(aTHX_ *psv) == data ); + } + return data; +} void -thrcnt_inc(ref) - SV* ref - CODE: - shared_sv* shared; - if(SvROK(ref)) - ref = SvRV(ref); - shared = Perl_sharedsv_find(aTHX, ref); - if(!shared) - croak("thrcnt can only be used on shared values"); - Perl_sharedsv_thrcnt_inc(aTHX_ shared); +Perl_sharedsv_free(pTHX_ shared_sv *shared) +{ + if (shared) { + dTHXc; + SHARED_EDIT; + SvREFCNT_dec(SHAREDSvPTR(shared)); + SHARED_RELEASE; + } +} +void +Perl_sharedsv_share(pTHX_ SV *sv) +{ + switch(SvTYPE(sv)) { + case SVt_PVGV: + Perl_croak(aTHX_ "Cannot share globs yet"); + break; -MODULE = threads::shared PACKAGE = threads::shared::sv + case SVt_PVCV: + Perl_croak(aTHX_ "Cannot share subs yet"); + break; -SV* -new(class, value) - SV* class - SV* value - CODE: - shared_sv* shared = Perl_sharedsv_new(aTHX); - MAGIC* shared_magic; - SV* obj = newSViv((IV)shared); - SHAREDSvEDIT(shared); - SHAREDSvGET(shared) = newSVsv(value); - SHAREDSvRELEASE(shared); - sv_magic(value, 0, PERL_MAGIC_ext, "threads::shared", 16); - shared_magic = mg_find(value, PERL_MAGIC_ext); - shared_magic->mg_virtual = &svtable; - shared_magic->mg_obj = newSViv((IV)shared); - shared_magic->mg_flags |= MGf_REFCOUNTED; - shared_magic->mg_private = 0; - SvMAGICAL_on(value); - RETVAL = obj; - OUTPUT: - RETVAL - - -MODULE = threads::shared PACKAGE = threads::shared::av - -SV* -new(class, value) - SV* class - SV* value - CODE: - shared_sv* shared = Perl_sharedsv_new(aTHX); - SV* obj = newSViv((IV)shared); - SHAREDSvEDIT(shared); - SHAREDSvGET(shared) = (SV*) newAV(); - SHAREDSvRELEASE(shared); - RETVAL = obj; - OUTPUT: - RETVAL + default: + ENTER_LOCK; + Perl_sharedsv_associate(aTHX_ &sv, 0, 0); + LEAVE_LOCK; + SvSETMAGIC(sv); + break; + } +} + +#if defined(WIN32) || defined(OS2) +# define ABS2RELMILLI(abs) \ + do { \ + abs -= (double)time(NULL) \ + if (abs > 0) { abs *= 1000; } \ + else { abs = 0; } \ + } while (0) +#endif /* WIN32 || OS2 */ + +bool +Perl_sharedsv_cond_timedwait(perl_cond *cond, perl_mutex *mut, double abs) +{ +#if defined(NETWARE) || defined(FAKE_THREADS) || defined(I_MACH_CTHREADS) + Perl_croak_nocontext("cond_timedwait not supported on this platform"); +#else +# ifdef WIN32 + int got_it = 0; + + ABS2RELMILLI(abs); + + cond->waiters++; + MUTEX_UNLOCK(mut); + /* See comments in win32/win32thread.h COND_WAIT vis-a-vis race */ + switch (WaitForSingleObject(cond->sem, abs)) { + case WAIT_OBJECT_0: got_it = 1; break; + case WAIT_TIMEOUT: break; + default: + /* WAIT_FAILED? WAIT_ABANDONED? others? */ + Perl_croak_nocontext("panic: cond_timedwait (%ld)",GetLastError()); + break; + } + MUTEX_LOCK(mut); + c->waiters--; + return got_it; +# else +# ifdef OS2 + int rc, got_it = 0; + STRLEN n_a; + + ABS2RELMILLI(abs); + + if ((rc = DosResetEventSem(*cond,&n_a)) && (rc != ERROR_ALREADY_RESET)) + Perl_rc = rc, croak_with_os2error("panic: cond_timedwait-reset"); + MUTEX_UNLOCK(mut); + if (CheckOSError(DosWaitEventSem(*cond,abs)) + && (rc != ERROR_INTERRUPT)) + croak_with_os2error("panic: cond_timedwait"); + if (rc == ERROR_INTERRUPT) errno = EINTR; + MUTEX_LOCK(mut); + return got_it; +# else /* hope you're I_PTHREAD! */ + struct timespec ts; + int got_it = 0; + + ts.tv_sec = (long)abs; + abs -= (NV)ts.tv_sec; + ts.tv_nsec = (long)(abs * 1000000000.0); + + switch (pthread_cond_timedwait(cond, mut, &ts)) { + case 0: got_it = 1; break; + case ETIMEDOUT: break; + default: + Perl_croak_nocontext("panic: cond_timedwait"); + break; + } + return got_it; +# endif /* OS2 */ +# endif /* WIN32 */ +#endif /* NETWARE || FAKE_THREADS || I_MACH_CTHREADS */ +} + +/* MAGIC (in mg.h sense) hooks */ + +int +sharedsv_scalar_mg_get(pTHX_ SV *sv, MAGIC *mg) +{ + shared_sv *shared = (shared_sv *) mg->mg_ptr; + assert(shared); + + ENTER_LOCK; + if (SHAREDSvPTR(shared)) { + if (SvROK(SHAREDSvPTR(shared))) { + SV *obj = Nullsv; + Perl_sharedsv_associate(aTHX_ &obj, SvRV(SHAREDSvPTR(shared)), NULL); + sv_setsv_nomg(sv, &PL_sv_undef); + SvRV(sv) = obj; + SvROK_on(sv); + + } + else { + sv_setsv_nomg(sv, SHAREDSvPTR(shared)); + } + } + LEAVE_LOCK; + return 0; +} void -STORE(self, index, value) - SV* self - SV* index - SV* value - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - SV* aentry; - SV** aentry_; - SHAREDSvLOCK(shared); - aentry_ = av_fetch((AV*) SHAREDSvGET(shared), SvIV(index), 0); - if(aentry_ && SvIV((*aentry_))) { - aentry = (*aentry_); - slot = (shared_sv*) SvIV(aentry); - if(SvROK(SHAREDSvGET(slot))) - Perl_sharedsv_thrcnt_dec(aTHX_ (shared_sv*) SvIV(SvRV(SHAREDSvGET(slot)))); - SHAREDSvEDIT(slot); - sv_setsv(SHAREDSvGET(slot), value); - SHAREDSvRELEASE(slot); - } else { - slot = Perl_sharedsv_new(aTHX); - SHAREDSvEDIT(shared); - SHAREDSvGET(slot) = newSVsv(value); - aentry = newSViv((IV)slot); - av_store((AV*) SHAREDSvGET(shared), SvIV(index), aentry); - SHAREDSvRELEASE(shared); +sharedsv_scalar_store(pTHX_ SV *sv, shared_sv *shared) +{ + dTHXc; + bool allowed = TRUE; + if (SvROK(sv)) { + shared_sv* target = Perl_sharedsv_find(aTHX_ SvRV(sv)); + if (target) { + SV *tmp; + SHARED_CONTEXT; + tmp = newRV(SHAREDSvPTR(target)); + sv_setsv_nomg(SHAREDSvPTR(shared), tmp); + SvREFCNT_dec(tmp); + if(SvOBJECT(SvRV(sv))) { + SV* fake_stash = newSVpv(HvNAME(SvSTASH(SvRV(sv))),0); + SvOBJECT_on(SHAREDSvPTR(target)); + SvSTASH(SHAREDSvPTR(target)) = (HV*)fake_stash; + } + CALLER_CONTEXT; + } + else { + allowed = FALSE; + } + } + else { + SvTEMP_off(sv); + SHARED_CONTEXT; + sv_setsv_nomg(SHAREDSvPTR(shared), sv); + if(SvOBJECT(sv)) { + SV* fake_stash = newSVpv(HvNAME(SvSTASH(sv)),0); + SvOBJECT_on(SHAREDSvPTR(shared)); + SvSTASH(SHAREDSvPTR(shared)) = (HV*)fake_stash; } - SHAREDSvUNLOCK(shared); + CALLER_CONTEXT; + } + if (!allowed) { + Perl_croak(aTHX_ "Invalid value for shared scalar"); + } +} -SV* -FETCH(self, index) - SV* self - SV* index - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - SV* aentry; - SV** aentry_; - SV* retval; - SHAREDSvLOCK(shared); - aentry_ = av_fetch((AV*) SHAREDSvGET(shared), SvIV(index),0); - if(aentry_) { - aentry = (*aentry_); - if(SvTYPE(aentry) == SVt_NULL) { - retval = &PL_sv_undef; - } else { - slot = (shared_sv*) SvIV(aentry); - retval = newSVsv(SHAREDSvGET(slot)); - } - } else { - retval = &PL_sv_undef; +int +sharedsv_scalar_mg_set(pTHX_ SV *sv, MAGIC *mg) +{ + shared_sv *shared; + ENTER_LOCK; + /* We call associate to potentially upgrade shared side SV */ + shared = Perl_sharedsv_associate(aTHX_ &sv, Nullsv, (shared_sv *) mg->mg_ptr); + assert(shared); + sharedsv_scalar_store(aTHX_ sv, shared); + LEAVE_LOCK; + return 0; +} + +int +sharedsv_scalar_mg_free(pTHX_ SV *sv, MAGIC *mg) +{ + shared_sv *shared = (shared_sv *) mg->mg_ptr; +#if 0 + assert (SvREFCNT(SHAREDSvPTR(shared)) < 1000); +#endif + Perl_sharedsv_free(aTHX_ shared); + return 0; +} + +int +sharedsv_scalar_mg_clear(pTHX_ SV *sv, MAGIC *mg) +{ + return 0; +} + +/* + * Called during cloning of new threads + */ +int +sharedsv_scalar_mg_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) +{ + shared_sv *shared = (shared_sv *) mg->mg_ptr; + if (shared) { + SvREFCNT_inc(SHAREDSvPTR(shared)); + } + return 0; +} + +MGVTBL sharedsv_scalar_vtbl = { + sharedsv_scalar_mg_get, /* get */ + sharedsv_scalar_mg_set, /* set */ + 0, /* len */ + sharedsv_scalar_mg_clear, /* clear */ + sharedsv_scalar_mg_free, /* free */ + 0, /* copy */ + sharedsv_scalar_mg_dup /* dup */ +}; + +/* Now the arrays/hashes stuff */ +int +sharedsv_elem_mg_FETCH(pTHX_ SV *sv, MAGIC *mg) +{ + dTHXc; + shared_sv *shared = SV_to_sharedsv(aTHX_ mg->mg_obj); + shared_sv *target = Perl_sharedsv_find(aTHX_ sv); + SV** svp; + + assert ( shared ); + assert ( SHAREDSvPTR(shared) ); + + ENTER_LOCK; + if (SvTYPE(SHAREDSvPTR(shared)) == SVt_PVAV) { + assert ( mg->mg_ptr == 0 ); + SHARED_CONTEXT; + svp = av_fetch((AV*) SHAREDSvPTR(shared), mg->mg_len, 0); + } + else { + char *key = mg->mg_ptr; + STRLEN len = mg->mg_len; + assert ( mg->mg_ptr != 0 ); + if (mg->mg_len == HEf_SVKEY) { + key = SvPV((SV *) mg->mg_ptr, len); } - SHAREDSvUNLOCK(shared); - RETVAL = retval; - OUTPUT: - RETVAL + SHARED_CONTEXT; + svp = hv_fetch((HV*) SHAREDSvPTR(shared), key, len, 0); + } + CALLER_CONTEXT; + if (svp) { + /* Exists in the array */ + target = Perl_sharedsv_associate(aTHX_ &sv, *svp, target); + sv_setsv(sv, *svp); + } + else { + /* Not in the array */ + sv_setsv(sv, &PL_sv_undef); + } + LEAVE_LOCK; + return 0; +} + +int +sharedsv_elem_mg_STORE(pTHX_ SV *sv, MAGIC *mg) +{ + dTHXc; + shared_sv *shared = SV_to_sharedsv(aTHX_ mg->mg_obj); + shared_sv *target; + SV **svp; + /* Theory - SV itself is magically shared - and we have ordered the + magic such that by the time we get here it has been stored + to its shared counterpart + */ + ENTER_LOCK; + assert(shared); + assert(SHAREDSvPTR(shared)); + if (SvTYPE(SHAREDSvPTR(shared)) == SVt_PVAV) { + assert ( mg->mg_ptr == 0 ); + SHARED_CONTEXT; + svp = av_fetch((AV*) SHAREDSvPTR(shared), mg->mg_len, 1); + } + else { + char *key = mg->mg_ptr; + STRLEN len = mg->mg_len; + assert ( mg->mg_ptr != 0 ); + if (mg->mg_len == HEf_SVKEY) + key = SvPV((SV *) mg->mg_ptr, len); + SHARED_CONTEXT; + svp = hv_fetch((HV*) SHAREDSvPTR(shared), key, len, 1); + } + CALLER_CONTEXT; + target = Perl_sharedsv_associate(aTHX_ &sv, *svp, 0); + sharedsv_scalar_store(aTHX_ sv, target); + LEAVE_LOCK; + return 0; +} + +int +sharedsv_elem_mg_DELETE(pTHX_ SV *sv, MAGIC *mg) +{ + dTHXc; + MAGIC *shmg; + shared_sv *shared = SV_to_sharedsv(aTHX_ mg->mg_obj); + ENTER_LOCK; + sharedsv_elem_mg_FETCH(aTHX_ sv, mg); + if ((shmg = mg_find(sv, PERL_MAGIC_shared_scalar))) + sharedsv_scalar_mg_get(aTHX_ sv, shmg); + if (SvTYPE(SHAREDSvPTR(shared)) == SVt_PVAV) { + SHARED_CONTEXT; + av_delete((AV*) SHAREDSvPTR(shared), mg->mg_len, G_DISCARD); + } + else { + char *key = mg->mg_ptr; + STRLEN len = mg->mg_len; + assert ( mg->mg_ptr != 0 ); + if (mg->mg_len == HEf_SVKEY) + key = SvPV((SV *) mg->mg_ptr, len); + SHARED_CONTEXT; + hv_delete((HV*) SHAREDSvPTR(shared), key, len, G_DISCARD); + } + CALLER_CONTEXT; + LEAVE_LOCK; + return 0; +} + +int +sharedsv_elem_mg_free(pTHX_ SV *sv, MAGIC *mg) +{ + Perl_sharedsv_free(aTHX_ SV_to_sharedsv(aTHX_ mg->mg_obj)); + return 0; +} + +int +sharedsv_elem_mg_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) +{ + shared_sv *shared = SV_to_sharedsv(aTHX_ mg->mg_obj); + SvREFCNT_inc(SHAREDSvPTR(shared)); + mg->mg_flags |= MGf_DUP; + return 0; +} + +MGVTBL sharedsv_elem_vtbl = { + sharedsv_elem_mg_FETCH, /* get */ + sharedsv_elem_mg_STORE, /* set */ + 0, /* len */ + sharedsv_elem_mg_DELETE, /* clear */ + sharedsv_elem_mg_free, /* free */ + 0, /* copy */ + sharedsv_elem_mg_dup /* dup */ +}; + +U32 +sharedsv_array_mg_FETCHSIZE(pTHX_ SV *sv, MAGIC *mg) +{ + dTHXc; + shared_sv *shared = (shared_sv *) mg->mg_ptr; + U32 val; + SHARED_EDIT; + if (SvTYPE(SHAREDSvPTR(shared)) == SVt_PVAV) { + val = av_len((AV*) SHAREDSvPTR(shared)); + } + else { + /* not actually defined by tie API but ... */ + val = HvKEYS((HV*) SHAREDSvPTR(shared)); + } + SHARED_RELEASE; + return val; +} + +int +sharedsv_array_mg_CLEAR(pTHX_ SV *sv, MAGIC *mg) +{ + dTHXc; + shared_sv *shared = (shared_sv *) mg->mg_ptr; + SHARED_EDIT; + if (SvTYPE(SHAREDSvPTR(shared)) == SVt_PVAV) { + av_clear((AV*) SHAREDSvPTR(shared)); + } + else { + hv_clear((HV*) SHAREDSvPTR(shared)); + } + SHARED_RELEASE; + return 0; +} + +int +sharedsv_array_mg_free(pTHX_ SV *sv, MAGIC *mg) +{ + Perl_sharedsv_free(aTHX_ (shared_sv *) mg->mg_ptr); + return 0; +} + +/* + * This is called when perl is about to access an element of + * the array - + */ +int +sharedsv_array_mg_copy(pTHX_ SV *sv, MAGIC* mg, + SV *nsv, const char *name, int namlen) +{ + shared_sv *shared = (shared_sv *) mg->mg_ptr; + MAGIC *nmg = sv_magicext(nsv,mg->mg_obj, + toLOWER(mg->mg_type),&sharedsv_elem_vtbl, + name, namlen); + ENTER_LOCK; + SvREFCNT_inc(SHAREDSvPTR(shared)); + LEAVE_LOCK; + nmg->mg_flags |= MGf_DUP; + return 1; +} + +int +sharedsv_array_mg_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) +{ + shared_sv *shared = (shared_sv *) mg->mg_ptr; + SvREFCNT_inc(SHAREDSvPTR(shared)); + mg->mg_flags |= MGf_DUP; + return 0; +} + +MGVTBL sharedsv_array_vtbl = { + 0, /* get */ + 0, /* set */ + sharedsv_array_mg_FETCHSIZE, /* len */ + sharedsv_array_mg_CLEAR, /* clear */ + sharedsv_array_mg_free, /* free */ + sharedsv_array_mg_copy, /* copy */ + sharedsv_array_mg_dup /* dup */ +}; + +=for apidoc sharedsv_unlock + +Recursively unlocks a shared sv. + +=cut void -PUSH(self, ...) - SV* self - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - int i; - SHAREDSvLOCK(shared); +Perl_sharedsv_unlock(pTHX_ shared_sv* ssv) +{ + recursive_lock_release(aTHX_ &ssv->lock); +} + +=for apidoc sharedsv_lock + +Recursive locks on a sharedsv. +Locks are dynamically scoped at the level of the first lock. + +=cut + +void +Perl_sharedsv_lock(pTHX_ shared_sv* ssv) +{ + if (!ssv) + return; + recursive_lock_acquire(aTHX_ &ssv->lock, __FILE__, __LINE__); +} + +/* handles calls from lock() builtin via PL_lockhook */ + +void +Perl_sharedsv_locksv(pTHX_ SV *sv) +{ + shared_sv* shared; + + if(SvROK(sv)) + sv = SvRV(sv); + shared = Perl_sharedsv_find(aTHX_ sv); + if(!shared) + croak("lock can only be used on shared values"); + Perl_sharedsv_lock(aTHX_ shared); +} + +=head1 Shared SV Functions + +=for apidoc sharedsv_init + +Saves a space for keeping SVs wider than an interpreter, + +=cut + +void +Perl_sharedsv_init(pTHX) +{ + dTHXc; + /* This pair leaves us in shared context ... */ + PL_sharedsv_space = perl_alloc(); + perl_construct(PL_sharedsv_space); + CALLER_CONTEXT; + recursive_lock_init(aTHX_ &PL_sharedsv_lock); + PL_lockhook = &Perl_sharedsv_locksv; + PL_sharehook = &Perl_sharedsv_share; +} + +#endif /* USE_ITHREADS */ + +MODULE = threads::shared PACKAGE = threads::shared::tie + +PROTOTYPES: DISABLE + +#ifdef USE_ITHREADS + +void +PUSH(shared_sv *shared, ...) +CODE: + dTHXc; + int i; for(i = 1; i < items; i++) { - shared_sv* slot = Perl_sharedsv_new(aTHX); - SV* tmp = ST(i); - SHAREDSvEDIT(slot); - SHAREDSvGET(slot) = newSVsv(tmp); - av_push((AV*) SHAREDSvGET(shared), newSViv((IV)slot)); - SHAREDSvRELEASE(slot); + SV* tmp = newSVsv(ST(i)); + shared_sv *target; + ENTER_LOCK; + target = Perl_sharedsv_associate(aTHX_ &tmp, Nullsv, 0); + sharedsv_scalar_store(aTHX_ tmp, target); + SHARED_CONTEXT; + av_push((AV*) SHAREDSvPTR(shared), SHAREDSvPTR(target)); + SvREFCNT_inc(SHAREDSvPTR(target)); + SHARED_RELEASE; + SvREFCNT_dec(tmp); } - SHAREDSvUNLOCK(shared); void -UNSHIFT(self, ...) - SV* self - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - int i; - SHAREDSvLOCK(shared); - SHAREDSvEDIT(shared); - av_unshift((AV*)SHAREDSvGET(shared), items - 1); - SHAREDSvRELEASE(shared); +UNSHIFT(shared_sv *shared, ...) +CODE: + dTHXc; + int i; + ENTER_LOCK; + SHARED_CONTEXT; + av_unshift((AV*)SHAREDSvPTR(shared), items - 1); + CALLER_CONTEXT; for(i = 1; i < items; i++) { - shared_sv* slot = Perl_sharedsv_new(aTHX); - SV* tmp = ST(i); - SHAREDSvEDIT(slot); - SHAREDSvGET(slot) = newSVsv(tmp); - av_store((AV*) SHAREDSvGET(shared), i - 1, newSViv((IV)slot)); - SHAREDSvRELEASE(slot); + SV* tmp = newSVsv(ST(i)); + shared_sv *target = Perl_sharedsv_associate(aTHX_ &tmp, Nullsv, 0); + sharedsv_scalar_store(aTHX_ tmp, target); + SHARED_CONTEXT; + av_store((AV*) SHAREDSvPTR(shared), i - 1, SHAREDSvPTR(target)); + SvREFCNT_inc(SHAREDSvPTR(target)); + CALLER_CONTEXT; + SvREFCNT_dec(tmp); } - SHAREDSvUNLOCK(shared); + LEAVE_LOCK; -SV* -POP(self) - SV* self - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - SV* retval; - SHAREDSvLOCK(shared); - SHAREDSvEDIT(shared); - retval = av_pop((AV*)SHAREDSvGET(shared)); - SHAREDSvRELEASE(shared); - if(retval && SvIV(retval)) { - slot = (shared_sv*) SvIV(retval); - retval = newSVsv(SHAREDSvGET(slot)); - Perl_sharedsv_thrcnt_dec(aTHX_ slot); - } else { - retval = &PL_sv_undef; +void +POP(shared_sv *shared) +CODE: + dTHXc; + SV* sv; + ENTER_LOCK; + SHARED_CONTEXT; + sv = av_pop((AV*)SHAREDSvPTR(shared)); + CALLER_CONTEXT; + ST(0) = sv_newmortal(); + Perl_sharedsv_associate(aTHX_ &ST(0), sv, 0); + SvREFCNT_dec(sv); + LEAVE_LOCK; + XSRETURN(1); + +void +SHIFT(shared_sv *shared) +CODE: + dTHXc; + SV* sv; + ENTER_LOCK; + SHARED_CONTEXT; + sv = av_shift((AV*)SHAREDSvPTR(shared)); + CALLER_CONTEXT; + ST(0) = sv_newmortal(); + Perl_sharedsv_associate(aTHX_ &ST(0), sv, 0); + SvREFCNT_dec(sv); + LEAVE_LOCK; + XSRETURN(1); + +void +EXTEND(shared_sv *shared, IV count) +CODE: + dTHXc; + SHARED_EDIT; + av_extend((AV*)SHAREDSvPTR(shared), count); + SHARED_RELEASE; + +void +STORESIZE(shared_sv *shared,IV count) +CODE: + dTHXc; + SHARED_EDIT; + av_fill((AV*) SHAREDSvPTR(shared), count); + SHARED_RELEASE; + + + + +void +EXISTS(shared_sv *shared, SV *index) +CODE: + dTHXc; + bool exists; + if (SvTYPE(SHAREDSvPTR(shared)) == SVt_PVAV) { + SHARED_EDIT; + exists = av_exists((AV*) SHAREDSvPTR(shared), SvIV(index)); } - SHAREDSvUNLOCK(shared); - RETVAL = retval; - OUTPUT: - RETVAL + else { + STRLEN len; + char *key = SvPV(index,len); + SHARED_EDIT; + exists = hv_exists((HV*) SHAREDSvPTR(shared), key, len); + } + SHARED_RELEASE; + ST(0) = (exists) ? &PL_sv_yes : &PL_sv_no; + XSRETURN(1); -SV* -SHIFT(self) - SV* self - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - SV* retval; - SHAREDSvLOCK(shared); - SHAREDSvEDIT(shared); - retval = av_shift((AV*)SHAREDSvGET(shared)); - SHAREDSvRELEASE(shared); - if(retval && SvIV(retval)) { - slot = (shared_sv*) SvIV(retval); - retval = newSVsv(SHAREDSvGET(slot)); - Perl_sharedsv_thrcnt_dec(aTHX_ slot); +void +FIRSTKEY(shared_sv *shared) +CODE: + dTHXc; + char* key = NULL; + I32 len = 0; + HE* entry; + ENTER_LOCK; + SHARED_CONTEXT; + hv_iterinit((HV*) SHAREDSvPTR(shared)); + entry = hv_iternext((HV*) SHAREDSvPTR(shared)); + if (entry) { + key = hv_iterkey(entry,&len); + CALLER_CONTEXT; + ST(0) = sv_2mortal(newSVpv(key, len)); } else { - retval = &PL_sv_undef; + CALLER_CONTEXT; + ST(0) = &PL_sv_undef; } - SHAREDSvUNLOCK(shared); - RETVAL = retval; - OUTPUT: - RETVAL + LEAVE_LOCK; + XSRETURN(1); void -CLEAR(self) - SV* self - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - SV** svp; - I32 i; - SHAREDSvLOCK(shared); - svp = AvARRAY((AV*)SHAREDSvGET(shared)); - i = AvFILLp((AV*)SHAREDSvGET(shared)); - while ( i >= 0) { - if(SvIV(svp[i])) { - Perl_sharedsv_thrcnt_dec(aTHX_ (shared_sv*) SvIV(svp[i])); - } - i--; +NEXTKEY(shared_sv *shared, SV *oldkey) +CODE: + dTHXc; + char* key = NULL; + I32 len = 0; + HE* entry; + ENTER_LOCK; + SHARED_CONTEXT; + entry = hv_iternext((HV*) SHAREDSvPTR(shared)); + if (entry) { + key = hv_iterkey(entry,&len); + CALLER_CONTEXT; + ST(0) = sv_2mortal(newSVpv(key, len)); + } else { + CALLER_CONTEXT; + ST(0) = &PL_sv_undef; } - SHAREDSvEDIT(shared); - av_clear((AV*)SHAREDSvGET(shared)); - SHAREDSvRELEASE(shared); - SHAREDSvUNLOCK(shared); - -void -EXTEND(self, count) - SV* self - SV* count - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - SHAREDSvEDIT(shared); - av_extend((AV*)SHAREDSvGET(shared), (I32) SvIV(count)); - SHAREDSvRELEASE(shared); - + LEAVE_LOCK; + XSRETURN(1); +MODULE = threads::shared PACKAGE = threads::shared +PROTOTYPES: ENABLE -SV* -EXISTS(self, index) - SV* self - SV* index - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - I32 exists; - SHAREDSvLOCK(shared); - exists = av_exists((AV*) SHAREDSvGET(shared), (I32) SvIV(index)); - if(exists) { - RETVAL = &PL_sv_yes; - } else { - RETVAL = &PL_sv_no; +void +_id(SV *ref) + PROTOTYPE: \[$@%] +CODE: + shared_sv *shared; + ref = SvRV(ref); + if(SvROK(ref)) + ref = SvRV(ref); + if( (shared = Perl_sharedsv_find(aTHX_ ref)) ){ + ST(0) = sv_2mortal(newSViv(PTR2IV(shared))); + XSRETURN(1); } - SHAREDSvUNLOCK(shared); + XSRETURN_UNDEF; + void -STORESIZE(self,count) - SV* self - SV* count - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - SHAREDSvEDIT(shared); - av_fill((AV*) SHAREDSvGET(shared), (I32) SvIV(count)); - SHAREDSvRELEASE(shared); +_refcnt(SV *ref) + PROTOTYPE: \[$@%] +CODE: + shared_sv *shared; + ref = SvRV(ref); + if(SvROK(ref)) + ref = SvRV(ref); + if( (shared = Perl_sharedsv_find(aTHX_ ref)) ){ + if (SHAREDSvPTR(shared)) { + ST(0) = sv_2mortal(newSViv(SvREFCNT(SHAREDSvPTR(shared)))); + XSRETURN(1); + } + else { + Perl_warn(aTHX_ "%" SVf " s=%p has no shared SV",ST(0),shared); + } + } + else { + Perl_warn(aTHX_ "%" SVf " is not shared",ST(0)); + } + XSRETURN_UNDEF; SV* -FETCHSIZE(self) - SV* self +share(SV *ref) + PROTOTYPE: \[$@%] CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - SHAREDSvLOCK(shared); - RETVAL = newSViv(av_len((AV*) SHAREDSvGET(shared)) + 1); - SHAREDSvUNLOCK(shared); - OUTPUT: + if(!SvROK(ref)) + Perl_croak(aTHX_ "Argument to share needs to be passed as ref"); + ref = SvRV(ref); + if(SvROK(ref)) + ref = SvRV(ref); + Perl_sharedsv_share(aTHX_ ref); + RETVAL = newRV(ref); + OUTPUT: RETVAL -SV* -DELETE(self,index) - SV* self - SV* index +void +lock_enabled(SV *ref) + PROTOTYPE: \[$@%] CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - SHAREDSvLOCK(shared); - if(av_exists((AV*) SHAREDSvGET(shared), (I32) SvIV(index))) { - SV* tmp; - SHAREDSvEDIT(shared); - tmp = av_delete((AV*)SHAREDSvGET(shared), (I32) SvIV(index),0); - SHAREDSvRELEASE(shared); - if(SvIV(tmp)) { - slot = (shared_sv*) SvIV(tmp); - RETVAL = newSVsv(SHAREDSvGET(slot)); - Perl_sharedsv_thrcnt_dec(aTHX_ slot); - } else { - RETVAL = &PL_sv_undef; - } - } else { - RETVAL = &PL_sv_undef; - } - SHAREDSvUNLOCK(shared); - OUTPUT: - RETVAL + shared_sv* shared; + if(!SvROK(ref)) + Perl_croak(aTHX_ "Argument to lock needs to be passed as ref"); + ref = SvRV(ref); + if(SvROK(ref)) + ref = SvRV(ref); + shared = Perl_sharedsv_find(aTHX_ ref); + if(!shared) + croak("lock can only be used on shared values"); + Perl_sharedsv_lock(aTHX_ shared); + +void +cond_wait_enabled(SV *ref_cond, SV *ref_lock = 0) + PROTOTYPE: \[$@%];\[$@%] + PREINIT: + shared_sv* shared; + perl_cond* user_condition; + int locks; + int same = 0; -AV* -SPLICE(self, offset, length, ...) - SV* self - SV* offset - SV* length - CODE: - croak("Splice is not implmented for shared arrays"); - -MODULE = threads::shared PACKAGE = threads::shared::hv - -SV* -new(class, value) - SV* class - SV* value CODE: - shared_sv* shared = Perl_sharedsv_new(aTHX); - SV* obj = newSViv((IV)shared); - SHAREDSvEDIT(shared); - SHAREDSvGET(shared) = (SV*) newHV(); - SHAREDSvRELEASE(shared); - RETVAL = obj; - OUTPUT: - RETVAL + if (!ref_lock || ref_lock == ref_cond) same = 1; -void -STORE(self, key, value) - SV* self - SV* key - SV* value - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - SV* hentry; - SV** hentry_; - STRLEN len; - char* ckey = SvPV(key, len); - SHAREDSvLOCK(shared); - hentry_ = hv_fetch((HV*) SHAREDSvGET(shared), ckey, len, 0); - if(hentry_ && SvIV((*hentry_))) { - hentry = (*hentry_); - slot = (shared_sv*) SvIV(hentry); - if(SvROK(SHAREDSvGET(slot))) - Perl_sharedsv_thrcnt_dec(aTHX_ (shared_sv*) SvIV(SvRV(SHAREDSvGET(slot)))); - SHAREDSvEDIT(slot); - sv_setsv(SHAREDSvGET(slot), value); - SHAREDSvRELEASE(slot); - } else { - slot = Perl_sharedsv_new(aTHX); - SHAREDSvEDIT(shared); - SHAREDSvGET(slot) = newSVsv(value); - hentry = newSViv((IV)slot); - hv_store((HV*) SHAREDSvGET(shared), ckey,len , hentry, 0); - SHAREDSvRELEASE(shared); - } - SHAREDSvUNLOCK(shared); + if(!SvROK(ref_cond)) + Perl_croak(aTHX_ "Argument to cond_wait needs to be passed as ref"); + ref_cond = SvRV(ref_cond); + if(SvROK(ref_cond)) + ref_cond = SvRV(ref_cond); + shared = Perl_sharedsv_find(aTHX_ ref_cond); + if(!shared) + croak("cond_wait can only be used on shared values"); + user_condition = &shared->user_cond; + if (! same) { + if (!SvROK(ref_lock)) + Perl_croak(aTHX_ "cond_wait lock needs to be passed as ref"); + ref_lock = SvRV(ref_lock); + if (SvROK(ref_lock)) ref_lock = SvRV(ref_lock); + shared = Perl_sharedsv_find(aTHX_ ref_lock); + if (!shared) + croak("cond_wait lock must be a shared value"); + } + if(shared->lock.owner != aTHX) + croak("You need a lock before you can cond_wait"); + /* Stealing the members of the lock object worries me - NI-S */ + MUTEX_LOCK(&shared->lock.mutex); + shared->lock.owner = NULL; + locks = shared->lock.locks; + shared->lock.locks = 0; -SV* -FETCH(self, key) - SV* self - SV* key - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - SV* hentry; - SV** hentry_; - SV* retval; - STRLEN len; - char* ckey = SvPV(key, len); - SHAREDSvLOCK(shared); - hentry_ = hv_fetch((HV*) SHAREDSvGET(shared), ckey, len,0); - if(hentry_) { - hentry = (*hentry_); - if(SvTYPE(hentry) == SVt_NULL) { - retval = &PL_sv_undef; - } else { - slot = (shared_sv*) SvIV(hentry); - retval = newSVsv(SHAREDSvGET(slot)); - } - } else { - retval = &PL_sv_undef; - } - SHAREDSvUNLOCK(shared); - RETVAL = retval; - OUTPUT: - RETVAL + /* since we are releasing the lock here we need to tell other + people that is ok to go ahead and use it */ + COND_SIGNAL(&shared->lock.cond); + COND_WAIT(user_condition, &shared->lock.mutex); + while(shared->lock.owner != NULL) { + /* OK -- must reacquire the lock */ + COND_WAIT(&shared->lock.cond, &shared->lock.mutex); + } + shared->lock.owner = aTHX; + shared->lock.locks = locks; + MUTEX_UNLOCK(&shared->lock.mutex); + +int +cond_timedwait_enabled(SV *ref_cond, double abs, SV *ref_lock = 0) + PROTOTYPE: \[$@%]$;\[$@%] + PREINIT: + shared_sv* shared; + perl_cond* user_condition; + int locks; + int same = 0; -void -CLEAR(self) - SV* self CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - HE* entry; - SHAREDSvLOCK(shared); - Perl_hv_iterinit(PL_sharedsv_space, (HV*) SHAREDSvGET(shared)); - entry = Perl_hv_iternext(PL_sharedsv_space, (HV*) SHAREDSvGET(shared)); - while(entry) { - slot = (shared_sv*) SvIV(Perl_hv_iterval(PL_sharedsv_space, (HV*) SHAREDSvGET(shared), entry)); - Perl_sharedsv_thrcnt_dec(aTHX_ slot); - entry = Perl_hv_iternext(PL_sharedsv_space,(HV*) SHAREDSvGET(shared)); + if (!ref_lock || ref_cond == ref_lock) same = 1; + + if(!SvROK(ref_cond)) + Perl_croak(aTHX_ "Argument to cond_timedwait needs to be passed as ref"); + ref_cond = SvRV(ref_cond); + if(SvROK(ref_cond)) + ref_cond = SvRV(ref_cond); + shared = Perl_sharedsv_find(aTHX_ ref_cond); + if(!shared) + croak("cond_timedwait can only be used on shared values"); + + user_condition = &shared->user_cond; + if (! same) { + if (!SvROK(ref_lock)) + Perl_croak(aTHX_ "cond_timedwait lock needs to be passed as ref"); + ref_lock = SvRV(ref_lock); + if (SvROK(ref_lock)) ref_lock = SvRV(ref_lock); + shared = Perl_sharedsv_find(aTHX_ ref_lock); + if (!shared) + croak("cond_timedwait lock must be a shared value"); } - SHAREDSvEDIT(shared); - hv_clear((HV*) SHAREDSvGET(shared)); - SHAREDSvRELEASE(shared); - SHAREDSvUNLOCK(shared); + if(shared->lock.owner != aTHX) + croak("You need a lock before you can cond_wait"); -SV* -FIRSTKEY(self) - SV* self - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - char* key = NULL; - I32 len; - HE* entry; - SHAREDSvLOCK(shared); - Perl_hv_iterinit(PL_sharedsv_space, (HV*) SHAREDSvGET(shared)); - entry = Perl_hv_iternext(PL_sharedsv_space, (HV*) SHAREDSvGET(shared)); - if(entry) { - key = Perl_hv_iterkey(PL_sharedsv_space, entry,&len); - RETVAL = newSVpv(key, len); - } else { - RETVAL = &PL_sv_undef; + MUTEX_LOCK(&shared->lock.mutex); + shared->lock.owner = NULL; + locks = shared->lock.locks; + shared->lock.locks = 0; + /* since we are releasing the lock here we need to tell other + people that is ok to go ahead and use it */ + COND_SIGNAL(&shared->lock.cond); + RETVAL = Perl_sharedsv_cond_timedwait(user_condition, &shared->lock.mutex, abs); + while (shared->lock.owner != NULL) { + /* OK -- must reacquire the lock... */ + COND_WAIT(&shared->lock.cond, &shared->lock.mutex); } - SHAREDSvUNLOCK(shared); + shared->lock.owner = aTHX; + shared->lock.locks = locks; + MUTEX_UNLOCK(&shared->lock.mutex); + + if (RETVAL == 0) + XSRETURN_UNDEF; OUTPUT: RETVAL +void +cond_signal_enabled(SV *ref) + PROTOTYPE: \[$@%] + CODE: + shared_sv* shared; + if(!SvROK(ref)) + Perl_croak(aTHX_ "Argument to cond_signal needs to be passed as ref"); + ref = SvRV(ref); + if(SvROK(ref)) + ref = SvRV(ref); + shared = Perl_sharedsv_find(aTHX_ ref); + if (ckWARN(WARN_THREADS) && shared->lock.owner != aTHX) + Perl_warner(aTHX_ packWARN(WARN_THREADS), + "cond_signal() called on unlocked variable"); + if(!shared) + croak("cond_signal can only be used on shared values"); + COND_SIGNAL(&shared->user_cond); -SV* -NEXTKEY(self, oldkey) - SV* self - SV* oldkey - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - char* key = NULL; - I32 len; - HE* entry; - SHAREDSvLOCK(shared); - entry = Perl_hv_iternext(PL_sharedsv_space, (HV*) SHAREDSvGET(shared)); - if(entry) { - key = Perl_hv_iterkey(PL_sharedsv_space, entry,&len); - RETVAL = newSVpv(key, len); - } else { - RETVAL = &PL_sv_undef; - } - SHAREDSvUNLOCK(shared); - OUTPUT: - RETVAL +void +cond_broadcast_enabled(SV *ref) + PROTOTYPE: \[$@%] + CODE: + shared_sv* shared; + if(!SvROK(ref)) + Perl_croak(aTHX_ "Argument to cond_broadcast needs to be passed as ref"); + ref = SvRV(ref); + if(SvROK(ref)) + ref = SvRV(ref); + shared = Perl_sharedsv_find(aTHX_ ref); + if(!shared) + croak("cond_broadcast can only be used on shared values"); + if (ckWARN(WARN_THREADS) && shared->lock.owner != aTHX) + Perl_warner(aTHX_ packWARN(WARN_THREADS), + "cond_broadcast() called on unlocked variable"); + COND_BROADCAST(&shared->user_cond); SV* -EXISTS(self, key) - SV* self - SV* key +bless(SV* ref, ...); + PROTOTYPE: $;$ CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - STRLEN len; - char* ckey = SvPV(key, len); - SHAREDSvLOCK(shared); - if(hv_exists((HV*)SHAREDSvGET(shared), ckey, len)) { - RETVAL = &PL_sv_yes; - } else { - RETVAL = &PL_sv_no; + { + HV* stash; + shared_sv* shared; + if (items == 1) + stash = CopSTASH(PL_curcop); + else { + SV* ssv = ST(1); + STRLEN len; + char *ptr; + + if (ssv && !SvGMAGICAL(ssv) && !SvAMAGIC(ssv) && SvROK(ssv)) + Perl_croak(aTHX_ "Attempt to bless into a reference"); + ptr = SvPV(ssv,len); + if (ckWARN(WARN_MISC) && len == 0) + Perl_warner(aTHX_ packWARN(WARN_MISC), + "Explicit blessing to '' (assuming package main)"); + stash = gv_stashpvn(ptr, len, TRUE); + } + SvREFCNT_inc(ref); + (void)sv_bless(ref, stash); + RETVAL = ref; + shared = Perl_sharedsv_find(aTHX_ ref); + if(shared) { + dTHXc; + ENTER_LOCK; + SHARED_CONTEXT; + { + SV* fake_stash = newSVpv(HvNAME(stash),0); + (void)sv_bless(SHAREDSvPTR(shared),(HV*)fake_stash); + } + CALLER_CONTEXT; + LEAVE_LOCK; + } } - SHAREDSvUNLOCK(shared); - OUTPUT: - RETVAL + OUTPUT: + RETVAL + +#endif /* USE_ITHREADS */ + +BOOT: +{ +#ifdef USE_ITHREADS + Perl_sharedsv_init(aTHX); +#endif /* USE_ITHREADS */ +} + + -SV* -DELETE(self, key) - SV* self - SV* key - CODE: - shared_sv* shared = (shared_sv*) SvIV(SvRV(self)); - shared_sv* slot; - STRLEN len; - char* ckey = SvPV(key, len); - SV* tmp; - SHAREDSvLOCK(shared); - SHAREDSvEDIT(shared); - tmp = hv_delete((HV*) SHAREDSvGET(shared), ckey, len,0); - SHAREDSvRELEASE(shared); - if(tmp) { - slot = SvIV(tmp); - RETVAL = newSVsv(SHAREDSvGET(slot)); - Perl_sharedsv_thrcnt_dec(aTHX_ slot); - } else { - RETVAL = &PL_sv_undef; - } - SHAREDSvUNLOCK(shared); - OUTPUT: - RETVAL