#define __PACKAGE__ "Variable::Magic"
-#define R(S) fprintf(stderr, "R(" #S ") = %d\n", SvREFCNT(S))
-
-#define PERL_VERSION_GE(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
-
-#define PERL_VERSION_LE(R, V, S) (PERL_REVISION < (R) || (PERL_REVISION == (R) && (PERL_VERSION < (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION <= (S))))))
-
#ifndef VMG_PERL_PATCHLEVEL
# ifdef PERL_PATCHNUM
# define VMG_PERL_PATCHLEVEL PERL_PATCHNUM
# endif
#endif
-#define VMG_HAS_PERL_OR(P, R, V, S) ((VMG_PERL_PATCHLEVEL >= (P)) || (!VMG_PERL_PATCHLEVEL && PERL_VERSION_GE((R), (V), (S))))
+#define VMG_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
-#define VMG_HAS_PERL_AND(P, R, V, S) (PERL_VERSION_GE((R), (V), (S)) && (!VMG_PERL_PATCHLEVEL || (VMG_PERL_PATCHLEVEL >= (P))))
+#define VMG_HAS_PERL_BRANCH(R, V, S) (PERL_REVISION == (R) && PERL_VERSION == (V) && PERL_SUBVERSION >= (S))
-/* --- Compatibility ------------------------------------------------------- */
+#define VMG_HAS_PERL_MAINT(R, V, S, P) (PERL_REVISION == (R) && PERL_VERSION == (V) && (VMG_PERL_PATCHLEVEL >= (P) || (!VMG_PERL_PATCHLEVEL && PERL_SUBVERSION >= (S))))
-#ifndef Newx
-# define Newx(v, n, c) New(0, v, n, c)
+/* --- Threads and multiplicity -------------------------------------------- */
+
+#ifndef NOOP
+# define NOOP
#endif
-#ifndef SvMAGIC_set
-# define SvMAGIC_set(sv, val) (SvMAGIC(sv) = (val))
+#ifndef dNOOP
+# define dNOOP
#endif
-#ifndef mPUSHi
-# define mPUSHi(I) PUSHs(sv_2mortal(newSViv(I)))
+#ifndef VMG_MULTIPLICITY
+# if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
+# define VMG_MULTIPLICITY 1
+# else
+# define VMG_MULTIPLICITY 0
+# endif
+#endif
+#if VMG_MULTIPLICITY && !defined(tTHX)
+# define tTHX PerlInterpreter*
#endif
-#ifndef dMY_CXT
-# define dMY_CXT
+#if VMG_MULTIPLICITY && defined(USE_ITHREADS) && defined(dMY_CXT) && defined(MY_CXT) && defined(START_MY_CXT) && defined(MY_CXT_INIT) && (defined(MY_CXT_CLONE) || defined(dMY_CXT_SV))
+# define VMG_THREADSAFE 1
+# ifndef MY_CXT_CLONE
+# define MY_CXT_CLONE \
+ dMY_CXT_SV; \
+ my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
+ Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
+ sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
+# endif
+#else
+# define VMG_THREADSAFE 0
+# undef dMY_CXT
+# define dMY_CXT dNOOP
# undef MY_CXT
-# define MY_CXT vmg_globaldata
+# define MY_CXT vmg_globaldata
# undef START_MY_CXT
# define START_MY_CXT STATIC my_cxt_t MY_CXT;
# undef MY_CXT_INIT
-# define MY_CXT_INIT
+# define MY_CXT_INIT NOOP
# undef MY_CXT_CLONE
-# undef aMY_CXT
-# undef pMY_CXT
-# define VMG_THREADSAFE 0
-#else
-# define VMG_THREADSAFE 1
+# define MY_CXT_CLONE NOOP
+#endif
+
+#if VMG_THREADSAFE
+
+STATIC SV *vmg_clone(pTHX_ SV *sv, tTHX owner) {
+#define vmg_clone(P, O) vmg_clone(aTHX_ (P), (O))
+ CLONE_PARAMS param;
+ param.stashes = NULL; /* don't need it unless sv is a PVHV */
+ param.flags = 0;
+ param.proto_perl = owner;
+ return sv_dup(sv, ¶m);
+}
+
+#endif /* VMG_THREADSAFE */
+
+/* --- Compatibility ------------------------------------------------------- */
+
+#ifndef Newx
+# define Newx(v, n, c) New(0, v, n, c)
+#endif
+
+#ifndef SvMAGIC_set
+# define SvMAGIC_set(sv, val) (SvMAGIC(sv) = (val))
+#endif
+
+#ifndef mPUSHi
+# define mPUSHi(I) PUSHs(sv_2mortal(newSViv(I)))
#endif
#ifndef PERL_MAGIC_ext
#endif
/* uvar magic and Hash::Util::FieldHash were commited with p28419 */
-#if VMG_HAS_PERL_AND(28419, 5, 9, 4)
+#if VMG_HAS_PERL_MAINT(5, 9, 4, 28419) || VMG_HAS_PERL(5, 10, 0)
# define VMG_UVAR 1
#else
# define VMG_UVAR 0
#endif
-#if !defined(VMG_COMPAT_ARRAY_PUSH_NOLEN) && VMG_HAS_PERL_OR(25854, 5, 9, 3)
+#if !defined(VMG_COMPAT_ARRAY_PUSH_NOLEN) && (VMG_HAS_PERL_BRANCH(5, 8, 9) || VMG_HAS_PERL_MAINT(5, 9, 3, 25854) || VMG_HAS_PERL(5, 10, 0))
# define VMG_COMPAT_ARRAY_PUSH_NOLEN 1
#else
# define VMG_COMPAT_ARRAY_PUSH_NOLEN 0
#endif
/* since 5.9.5 - see #43357 */
-#if VMG_HAS_PERL_OR(31473, 5, 9, 5)
+#if VMG_HAS_PERL_BRANCH(5, 8, 9) || VMG_HAS_PERL_MAINT(5, 9, 5, 31473) || VMG_HAS_PERL(5, 10, 0)
# define VMG_COMPAT_ARRAY_UNDEF_CLEAR 1
#else
# define VMG_COMPAT_ARRAY_UNDEF_CLEAR 0
#endif
-#if VMG_HAS_PERL_OR(32969, 5, 11, 0)
+#if VMG_HAS_PERL_MAINT(5, 11, 0, 32969)
# define VMG_COMPAT_SCALAR_LENGTH_NOLEN 1
#else
# define VMG_COMPAT_SCALAR_LENGTH_NOLEN 0
#define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
-typedef struct {
- HV *wizz;
- U16 count;
-} my_cxt_t;
+typedef HV * my_cxt_t;
START_MY_CXT
-STATIC void vmg_cxt_init
-#if defined(pMY_CXT) && defined(aMY_CXT)
- (pTHX_ pMY_CXT) {
-# define vmg_cxt_init() vmg_cxt_init(aTHX_ aMY_CXT)
-#else
- (pTHX) {
- dMY_CXT;
-# define vmg_cxt_init() vmg_cxt_init(aTHX)
-#endif
- MY_CXT.wizz = newHV();
-#ifdef USE_ITHREADS
- HvSHAREKEYS_off(MY_CXT.wizz);
-#endif
- MY_CXT.count = 0;
- return;
-}
-
/* --- Signatures ---------------------------------------------------------- */
#define SIG_MIN ((U16) (1u << 8))
do {
sig = SIG_NBR * Drand01() + SIG_MIN;
- } while (hv_exists(MY_CXT.wizz, buf, sprintf(buf, "%u", sig)));
+ } while (hv_exists(MY_CXT, buf, sprintf(buf, "%u", sig)));
return sig;
}
#if VMG_UVAR
SV *cb_fetch, *cb_store, *cb_exists, *cb_delete;
#endif /* VMG_UVAR */
+#if VMG_MULTIPLICITY
+ tTHX owner;
+#endif /* VMG_MULTIPLICITY */
} MGWIZ;
#define MGWIZ2SV(W) (newSVuv(PTR2UV(W)))
if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
nsv = POPs;
-#if PERL_VERSION_LE(5, 8, 2)
- nsv = sv_newref(nsv); /* Workaround some bug in SvREFCNT_inc() */
-#else
+#if VMG_HAS_PERL(5, 8, 3)
SvREFCNT_inc(nsv); /* Or it will be destroyed in FREETMPS */
+#else
+ nsv = sv_newref(nsv); /* Workaround some bug in SvREFCNT_inc() */
#endif
PUTBACK;
if (w->cb_copy)
mg->mg_flags |= MGf_COPY;
#endif /* MGf_COPY */
-#if MGf_DUP
+#if 0 /* MGf_DUP */
if (w->cb_dup)
mg->mg_flags |= MGf_DUP;
#endif /* MGf_DUP */
STATIC int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) {
/* So that it can survive tmp cleanup in vmg_cb_call */
SvREFCNT_inc(sv);
+#if !VMG_HAS_PERL_MAINT(5, 11, 0, 32686)
+ /* The previous magic tokens were freed but the magic chain wasn't updated, so
+ * if you access the sv from the callback the old deleted magics will trigger
+ * and cause memory misreads. Change 32686 solved it that way : */
+ SvMAGIC_set(sv, mg);
+#endif
/* Perl_mg_free will get rid of the magic and decrement mg->mg_obj and
* mg->mg_ptr reference count */
return vmg_cb_call1(SV2MGWIZ(mg->mg_ptr)->cb_free, sv, mg->mg_obj);
#if MGf_COPY
STATIC int vmg_svt_copy(pTHX_ SV *sv, MAGIC *mg, SV *nsv, const char *key,
-# if VMG_HAS_PERL_AND(33256, 5, 11, 0)
+# if VMG_HAS_PERL_MAINT(5, 11, 0, 33256)
I32 keylen
# else
int keylen
STATIC int vmg_wizard_free(pTHX_ SV *wiz, MAGIC *mg) {
char buf[8];
MGWIZ *w;
- dMY_CXT;
- w = SV2MGWIZ(wiz);
+ if (PL_dirty) /* during global destruction, the context is already freed */
+ return 0;
- if (hv_delete(MY_CXT.wizz, buf, sprintf(buf, "%u", w->sig), 0)) {
- --MY_CXT.count;
+ w = SV2MGWIZ(wiz);
+#if VMG_MULTIPLICITY
+ if (w->owner != aTHX)
+ return 0;
+ w->owner = NULL;
+#endif /* VMG_MULTIPLICITY */
+
+ {
+ dMY_CXT;
+ if (hv_delete(MY_CXT, buf, sprintf(buf, "%u", w->sig), 0) != wiz)
+ return 0;
}
SvFLAGS(wiz) |= SVf_BREAK;
FREETMPS;
#if MGf_COPY
if (w->cb_copy != NULL) { SvREFCNT_dec(SvRV(w->cb_copy)); }
#endif /* MGf_COPY */
-#if MGf_DUP
+#if 0 /* MGf_DUP */
if (w->cb_dup != NULL) { SvREFCNT_dec(SvRV(w->cb_dup)); }
#endif /* MGf_DUP */
#if MGf_LOCAL
if (w->cb_exists != NULL) { SvREFCNT_dec(SvRV(w->cb_exists)); }
if (w->cb_delete != NULL) { SvREFCNT_dec(SvRV(w->cb_delete)); }
#endif /* VMG_UVAR */
+
Safefree(w->vtbl);
Safefree(w);
return sig;
}
+STATIC U16 vmg_wizard_sig(pTHX_ SV *wiz) {
+#define vmg_wizard_sig(W) vmg_wizard_sig(aTHX_ (W))
+ char buf[8];
+ U16 sig;
+
+ if (SvROK(wiz)) {
+ sig = SV2MGWIZ(SvRV(wiz))->sig;
+ } else if (SvOK(wiz)) {
+ sig = vmg_sv2sig(wiz);
+ } else {
+ croak(vmg_invalid_wiz);
+ }
+
+ {
+ dMY_CXT;
+ if (!hv_fetch(MY_CXT, buf, sprintf(buf, "%u", sig), 0))
+ sig = 0;
+ }
+ return sig;
+}
+
+STATIC SV *vmg_wizard_wiz(pTHX_ SV *wiz) {
+#define vmg_wizard_wiz(W) vmg_wizard_wiz(aTHX_ (W))
+ char buf[8];
+ SV **old;
+ U16 sig;
+
+ if (SvROK(wiz)) {
+ wiz = SvRV(wiz);
+#if VMG_MULTIPLICITY
+ if (SV2MGWIZ(wiz)->owner == aTHX)
+ return wiz;
+#endif /* VMG_MULTIPLICITY */
+ sig = SV2MGWIZ(wiz)->sig;
+ } else if (SvOK(wiz)) {
+ sig = vmg_sv2sig(wiz);
+ } else {
+ croak(vmg_invalid_wiz);
+ }
+
+ {
+ dMY_CXT;
+ return (old = hv_fetch(MY_CXT, buf, sprintf(buf, "%u", sig), 0))
+ ? *old : NULL;
+ }
+}
+
#define VMG_SET_CB(S, N) \
cb = (S); \
w->cb_ ## N = (SvOK(cb) && SvROK(cb)) ? newRV_inc(SvRV(cb)) : NULL;
w->cb_ ## N = NULL; \
}
+#if VMG_THREADSAFE
+
+#define VMG_CLONE_CB(N) \
+ z->cb_ ## N = (w->cb_ ## N) ? newRV_noinc(vmg_clone(SvRV(w->cb_ ## N), \
+ w->owner)) \
+ : NULL;
+
+STATIC MGWIZ *vmg_wizard_clone(pTHX_ const MGWIZ *w) {
+#define vmg_wizard_clone(W) vmg_wizard_clone(aTHX_ (W))
+ MGVTBL *t;
+ MGWIZ *z;
+
+ Newx(t, 1, MGVTBL);
+ Copy(w->vtbl, t, 1, MGVTBL);
+
+ Newx(z, 1, MGWIZ);
+ VMG_CLONE_CB(data);
+ VMG_CLONE_CB(get);
+ VMG_CLONE_CB(set);
+ VMG_CLONE_CB(len);
+ VMG_CLONE_CB(clear);
+ VMG_CLONE_CB(free);
+#if MGf_COPY
+ VMG_CLONE_CB(copy);
+#endif /* MGf_COPY */
+#if MGf_DUP
+ VMG_CLONE_CB(dup);
+#endif /* MGf_DUP */
+#if MGf_LOCAL
+ VMG_CLONE_CB(local);
+#endif /* MGf_LOCAL */
+#if VMG_UVAR
+ VMG_CLONE_CB(fetch);
+ VMG_CLONE_CB(store);
+ VMG_CLONE_CB(exists);
+ VMG_CLONE_CB(delete);
+#endif /* VMG_UVAR */
+ z->owner = aTHX;
+ z->vtbl = t;
+ z->sig = w->sig;
+ z->uvar = w->uvar;
+
+ return z;
+}
+
+#endif /* VMG_THREADSAFE */
/* --- XS ------------------------------------------------------------------ */
{
HV *stash;
MY_CXT_INIT;
- vmg_cxt_init();
+ MY_CXT = newHV();
+ hv_iterinit(MY_CXT); /* Allocate iterator */
stash = gv_stashpv(__PACKAGE__, 1);
newCONSTSUB(stash, "SIG_MIN", newSVuv(SIG_MIN));
newCONSTSUB(stash, "SIG_MAX", newSVuv(SIG_MAX));
void
CLONE(...)
PROTOTYPE: DISABLE
+PREINIT:
+ HV *hv;
CODE:
-#ifdef MY_CXT_CLONE
- MY_CXT_CLONE;
- vmg_cxt_init();
-#endif
+#if VMG_THREADSAFE
+ {
+ HE *key;
+ dMY_CXT;
+ hv = newHV();
+ hv_iterinit(hv); /* Allocate iterator */
+ hv_iterinit(MY_CXT);
+ while ((key = hv_iternext(MY_CXT))) {
+ STRLEN len;
+ char *sig = HePV(key, len);
+ SV *sv;
+ MAGIC *mg;
+ sv = MGWIZ2SV(vmg_wizard_clone(SV2MGWIZ(HeVAL(key))));
+ mg = sv_magicext(sv, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, 0);
+ mg->mg_private = SIG_WIZ;
+ SvREADONLY_on(sv);
+ hv_store(hv, sig, len, sv, HeHASH(key));
+ }
+ }
+ {
+ MY_CXT_CLONE;
+ MY_CXT = hv;
+ }
+#endif /* VMG_THREADSAFE */
SV *_wizard(...)
PROTOTYPE: DISABLE
if (SvOK(svsig)) {
SV **old;
sig = vmg_sv2sig(svsig);
- if ((old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0))) {
+ if ((old = hv_fetch(MY_CXT, buf, sprintf(buf, "%u", sig), 0))) {
ST(0) = sv_2mortal(newRV_inc(*old));
XSRETURN(1);
}
} else {
- if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); }
+ if (HvKEYS(MY_CXT) >= SIG_NBR) { croak(vmg_toomanysigs); }
sig = vmg_gensig();
}
VMG_SET_CB(ST(i++), exists);
VMG_SET_CB(ST(i++), delete);
#endif /* VMG_UVAR */
+#if VMG_MULTIPLICITY
+ w->owner = aTHX;
+#endif /* VMG_MULTIPLICITY */
w->vtbl = t;
w->sig = sig;
#endif /* VMG_UVAR */
sv = MGWIZ2SV(w);
- mg = sv_magicext(sv, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, -1);
+ mg = sv_magicext(sv, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, 0);
mg->mg_private = SIG_WIZ;
+ SvREADONLY_on(sv);
- hv_store(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), sv, 0);
- ++MY_CXT.count;
+ hv_store(MY_CXT, buf, sprintf(buf, "%u", sig), sv, 0);
RETVAL = newRV_noinc(sv);
OUTPUT:
PROTOTYPE:
CODE:
dMY_CXT;
- if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); }
+ if (HvKEYS(MY_CXT) >= SIG_NBR) { croak(vmg_toomanysigs); }
RETVAL = newSVuv(vmg_gensig());
OUTPUT:
RETVAL
AV *args = NULL;
SV *ret;
CODE:
- dMY_CXT;
- if (SvROK(wiz)) {
- wiz = SvRV(wiz);
- } else if (SvOK(wiz)) {
- char buf[8];
- SV **old;
- U16 sig = vmg_sv2sig(wiz);
- if ((old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0))) {
- wiz = *old;
- } else {
- XSRETURN_UNDEF;
- }
- } else {
- croak(vmg_invalid_sig);
- }
+ wiz = vmg_wizard_wiz(wiz);
+ if (!wiz)
+ XSRETURN_UNDEF;
if (items > 2) {
I32 i;
args = newAV();
SV *data;
U16 sig;
CODE:
- dMY_CXT;
- if (SvROK(wiz)) {
- sig = SV2MGWIZ(SvRV(wiz))->sig;
- } else if (SvOK(wiz)) {
- char buf[8];
- sig = vmg_sv2sig(wiz);
- if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) {
- XSRETURN_UNDEF;
- }
- } else {
- croak(vmg_invalid_wiz);
- }
+ sig = vmg_wizard_sig(wiz);
+ if (!sig)
+ XSRETURN_UNDEF;
data = vmg_data_get(SvRV(sv), sig);
if (!data) { XSRETURN_UNDEF; }
ST(0) = data;
PREINIT:
U16 sig;
CODE:
- dMY_CXT;
- if (SvROK(wiz)) {
- sig = SV2MGWIZ(SvRV(wiz))->sig;
- } else if (SvOK(wiz)) {
- char buf[8];
- sig = vmg_sv2sig(wiz);
- if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0)) {
- XSRETURN_UNDEF;
- }
- } else {
- croak(vmg_invalid_wiz);
- }
+ sig = vmg_wizard_sig(wiz);
+ if (!sig)
+ XSRETURN_UNDEF;
RETVAL = newSVuv(vmg_dispell(SvRV(sv), sig));
OUTPUT:
RETVAL