# define VMG_MULTIPLICITY 0
# endif
#endif
-#if VMG_MULTIPLICITY && !defined(tTHX)
-# define tTHX PerlInterpreter*
-#endif
#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
# define VMG_UNLOCK(M)
#endif
-#if VMG_THREADSAFE
-
-STATIC SV *vmg_clone(pTHX_ SV *sv, tTHX owner) {
-#define vmg_clone(P, O) vmg_clone(aTHX_ (P), (O))
- SV *dupsv;
-
-#if VMG_HAS_PERL(5, 13, 2)
- CLONE_PARAMS *param = Perl_clone_params_new(owner, aTHX);
-
- dupsv = sv_dup(sv, param);
-
- Perl_clone_params_del(param);
-#else
- CLONE_PARAMS param;
-
- param.stashes = NULL; /* don't need it unless sv is a PVHV */
- param.flags = 0;
- param.proto_perl = owner;
-
- dupsv = sv_dup(sv, ¶m);
-#endif
-
- return SvREFCNT_inc(dupsv);
-}
-
-#endif /* VMG_THREADSAFE */
-
/* --- Compatibility ------------------------------------------------------- */
#ifndef Newx
#if VMG_UVAR
SV *cb_fetch, *cb_store, *cb_exists, *cb_delete;
#endif /* VMG_UVAR */
-
-#if VMG_MULTIPLICITY
- tTHX owner;
-#endif /* VMG_MULTIPLICITY */
} MGWIZ;
STATIC void vmg_op_info_init(pTHX_ unsigned int opinfo);
if (!w)
return;
- /* We reach this point in dirty state when ptable_free() is called from the
- * atexit cleanup callback, and that the global table still holds a live
- * wizard. This happens before all the SV bodies are freed, so all the wizard
- * callbacks are still alive (as they are referenced by the undead wizard).
- * Hence it is safe to decrement their refcount. Later on, the wizard free
- * callback itself will trigger when the wizard body is reaped, but it will
- * be skipped as it guards against dirty state - which is good since nothing
- * has to be done anymore at that point. */
-
SvREFCNT_dec(w->cb_data);
SvREFCNT_dec(w->cb_get);
SvREFCNT_dec(w->cb_set);
#if VMG_THREADSAFE
#define VMG_CLONE_CB(N) \
- z->cb_ ## N = (w->cb_ ## N) ? vmg_clone(w->cb_ ## N, w->owner) \
+ z->cb_ ## N = (w->cb_ ## N) ? SvREFCNT_inc(sv_dup(w->cb_ ## N, params)) \
: NULL;
-STATIC MGWIZ *vmg_mgwiz_clone(pTHX_ const MGWIZ *w) {
-#define vmg_mgwiz_clone(W) vmg_mgwiz_clone(aTHX_ (W))
+STATIC const MGWIZ *vmg_mgwiz_dup(pTHX_ const MGWIZ *w, CLONE_PARAMS *params) {
+#define vmg_mgwiz_dup(W, P) vmg_mgwiz_dup(aTHX_ (W), (P))
MGWIZ *z;
if (!w)
VMG_CLONE_CB(delete);
#endif /* VMG_UVAR */
- z->owner = aTHX;
-
return z;
}
/* --- Context-safe global data -------------------------------------------- */
-#if VMG_THREADSAFE
-
-#define PTABLE_NAME ptable
-#define PTABLE_VAL_FREE(V) vmg_mgwiz_free(VOID2(MGWIZ *, (V)))
-
-#define pPTBL pTHX
-#define pPTBL_ pTHX_
-#define aPTBL aTHX
-#define aPTBL_ aTHX_
-
-#include "ptable.h"
-
-#define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V))
-#define ptable_clear(T) ptable_clear(aTHX_ (T))
-#define ptable_free(T) ptable_free(aTHX_ (T))
-
-#endif /* VMG_THREADSAFE */
-
#define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
typedef struct {
-#if VMG_THREADSAFE
- ptable *wizards;
- tTHX owner;
-#endif
- HV *b__op_stashes[OPc_MAX];
+ HV *b__op_stashes[OPc_MAX];
} my_cxt_t;
START_MY_CXT
-#if VMG_THREADSAFE
-
-STATIC void vmg_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
- my_cxt_t *ud = VOID2(my_cxt_t *, ud_);
- MGWIZ *w;
-
- if (ud->owner == aTHX)
- return;
-
- w = vmg_mgwiz_clone(VOID2(MGWIZ *, ent->val));
- if (w)
- ptable_store(ud->wizards, ent->key, w);
-}
-
-#endif /* VMG_THREADSAFE */
-
/* --- Wizard objects ------------------------------------------------------ */
STATIC int vmg_wizard_free(pTHX_ SV *sv, MAGIC *mg);
+#if VMG_THREADSAFE
+STATIC int vmg_wizard_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *params);
+#endif
STATIC MGVTBL vmg_wizard_vtbl = {
NULL, /* get */
NULL, /* clear */
vmg_wizard_free, /* free */
NULL, /* copy */
+#if VMG_THREADSAFE
+ vmg_wizard_dup, /* dup */
+#else
NULL, /* dup */
+#endif
#if MGf_LOCAL
NULL, /* local */
#endif /* MGf_LOCAL */
STATIC SV *vmg_wizard_new(pTHX_ const MGWIZ *w) {
#define vmg_wizard_new(W) vmg_wizard_new(aTHX_ (W))
- SV *wiz = newSVuv(PTR2IV(w));
+ SV *wiz;
+
+#if VMG_THREADSAFE
+ wiz = newSV(0);
+#else
+ wiz = newSViv(PTR2IV(w));
+#endif
if (w) {
- MAGIC *mg = sv_magicext(wiz, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, 0);
+ MAGIC *mg = sv_magicext(wiz, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl,
+ (const char *) w, 0);
mg->mg_private = SIG_WZO;
+#if VMG_THREADSAFE
+ mg->mg_flags |= MGf_DUP;
+#endif
}
SvREADONLY_on(wiz);
#define vmg_wizard_validate(W) vmg_wizard_validate(aTHX_ (W))
if (SvROK(wiz)) {
wiz = SvRV_const(wiz);
+#if VMG_THREADSAFE
+ if (SvTYPE(wiz) >= SVt_PVMG && SvMAGIC(wiz))
+#else
if (SvIOK(wiz))
+#endif
return wiz;
}
return NULL;
}
-#define vmg_wizard_id(W) SvIVX((const SV *) (W))
-#define vmg_wizard_main_mgwiz(W) INT2PTR(const MGWIZ *, vmg_wizard_id(W))
+#if VMG_THREADSAFE
+
+STATIC const MGWIZ *vmg_wizard_mgwiz(const SV *wiz) {
+ MAGIC *mg;
+
+ for (mg = SvMAGIC(wiz); mg; mg = mg->mg_moremagic) {
+ if (mg->mg_type == PERL_MAGIC_ext && mg->mg_virtual == &vmg_wizard_vtbl)
+ return (const MGWIZ *) mg->mg_ptr;
+ }
+
+ return NULL;
+}
+
+#define vmg_wizard_mgwiz(W) vmg_wizard_mgwiz((const SV *) (W))
+
+#define vmg_wizard_id(W) PTR2IV(vmg_vtable_vtbl(vmg_wizard_mgwiz(W)->vtable))
+
+#else
+
+#define vmg_wizard_id(W) SvIVX((const SV *) (W))
+
+#define vmg_wizard_mgwiz(W) INT2PTR(const MGWIZ *, vmg_wizard_id(W))
+
+#endif
/* ... Wizard destructor ................................................... */
STATIC int vmg_wizard_free(pTHX_ SV *sv, MAGIC *mg) {
- MGWIZ *w;
-
if (PL_dirty) /* During global destruction, the context is already freed */
return 0;
- w = (MGWIZ *) vmg_wizard_main_mgwiz(sv);
-
-#if VMG_THREADSAFE
- {
- dMY_CXT;
- ptable_store(MY_CXT.wizards, w, NULL);
- }
-#else /* VMG_THREADSAFE */
- vmg_mgwiz_free(w);
-#endif /* !VMG_THREADSAFE */
+ vmg_mgwiz_free((MGWIZ *) mg->mg_ptr);
return 0;
}
#if VMG_THREADSAFE
-STATIC const MGWIZ *vmg_wizard_mgwiz(pTHX_ const SV *wiz) {
-#define vmg_wizard_mgwiz(W) vmg_wizard_mgwiz(aTHX_ ((const SV *) (W)))
- const MGWIZ *w;
-
- w = vmg_wizard_main_mgwiz(wiz);
- if (w->owner == aTHX)
- return w;
+STATIC int vmg_wizard_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *params) {
+ mg->mg_ptr = (char *) vmg_mgwiz_dup((const MGWIZ *) mg->mg_ptr, params);
- {
- dMY_CXT;
- return VOID2(const MGWIZ *, ptable_fetch(MY_CXT.wizards, w));
- }
+ return 0;
}
-#else /* VMG_THREADSAFE */
-
-#define vmg_wizard_mgwiz(W) vmg_wizard_main_mgwiz(W)
-
-#endif /* !VMG_THREADSAFE */
+#endif /* VMG_THREADSAFE */
/* --- User-level functions implementation --------------------------------- */
w->cb_ ## N = NULL; \
}
-#if VMG_THREADSAFE
-
-STATIC void vmg_cleanup(pTHX_ void *ud) {
- dMY_CXT;
-
- ptable_free(MY_CXT.wizards);
- MY_CXT.wizards = NULL;
-}
-
-#endif /* VMG_THREADSAFE */
-
/* --- XS ------------------------------------------------------------------ */
MODULE = Variable::Magic PACKAGE = Variable::Magic
HV *stash;
MY_CXT_INIT;
-#if VMG_THREADSAFE
- MY_CXT.wizards = ptable_new();
- MY_CXT.owner = aTHX;
-#endif
MY_CXT.b__op_stashes[0] = NULL;
#if VMG_THREADSAFE
MUTEX_INIT(&vmg_vtable_refcount_mutex);
MUTEX_INIT(&vmg_op_name_init_mutex);
- call_atexit(vmg_cleanup, NULL);
#endif
stash = gv_stashpv(__PACKAGE__, 1);
CLONE(...)
PROTOTYPE: DISABLE
PREINIT:
- ptable *t;
- U32 had_b__op_stash = 0;
- int c;
+ U32 had_b__op_stash = 0;
+ int c;
PPCODE:
{
- my_cxt_t ud;
dMY_CXT;
-
- ud.wizards = t = ptable_new();
- ud.owner = MY_CXT.owner;
- ptable_walk(MY_CXT.wizards, vmg_ptable_clone, &ud);
-
for (c = OPc_NULL; c < OPc_MAX; ++c) {
if (MY_CXT.b__op_stashes[c])
had_b__op_stash |= (((U32) 1) << c);
}
{
MY_CXT_CLONE;
- MY_CXT.wizards = t;
- MY_CXT.owner = aTHX;
for (c = OPc_NULL; c < OPc_MAX; ++c) {
MY_CXT.b__op_stashes[c] = (had_b__op_stash & (((U32) 1) << c))
? gv_stashpv(vmg_opclassnames[c], 1) : NULL;
MGVTBL *t;
SV *cb, *op_info, *copy_key;
CODE:
- dMY_CXT;
-
if (items != 9
#if MGf_LOCAL
+ 1
if (w->cb_fetch || w->cb_store || w->cb_exists || w->cb_delete)
w->uvar = SvTRUE(copy_key) ? 2 : 1;
#endif /* VMG_UVAR */
-#if VMG_MULTIPLICITY
- w->owner = aTHX;
-#endif /* VMG_MULTIPLICITY */
-#if VMG_THREADSAFE
- ptable_store(MY_CXT.wizards, w, w);
-#endif /* VMG_THREADSAFE */
RETVAL = newRV_noinc(vmg_wizard_new(w));
OUTPUT:
+++ /dev/null
-/* This file is part of the Variable::Magic Perl module.
- * See http://search.cpan.org/dist/Variable-Magic/ */
-
-/* This is a pointer table implementation essentially copied from the ptr_table
- * implementation in perl's sv.c, except that it has been modified to use memory
- * shared across threads.
- * Copyright goes to the original authors, bug reports to me. */
-
-/* This header is designed to be included several times with different
- * definitions for PTABLE_NAME and PTABLE_VAL_FREE(). */
-
-#undef VOID2
-#ifdef __cplusplus
-# define VOID2(T, P) static_cast<T>(P)
-#else
-# define VOID2(T, P) (P)
-#endif
-
-#undef pPTBLMS
-#undef pPTBLMS_
-#undef aPTBLMS
-#undef aPTBLMS_
-
-/* Context for PerlMemShared_* functions */
-
-#ifdef PERL_IMPLICIT_SYS
-# define pPTBLMS pTHX
-# define pPTBLMS_ pTHX_
-# define aPTBLMS aTHX
-# define aPTBLMS_ aTHX_
-#else
-# define pPTBLMS void
-# define pPTBLMS_
-# define aPTBLMS
-# define aPTBLMS_
-#endif
-
-#ifndef pPTBL
-# define pPTBL pPTBLMS
-#endif
-#ifndef pPTBL_
-# define pPTBL_ pPTBLMS_
-#endif
-#ifndef aPTBL
-# define aPTBL aPTBLMS
-#endif
-#ifndef aPTBL_
-# define aPTBL_ aPTBLMS_
-#endif
-
-#ifndef PTABLE_NAME
-# define PTABLE_NAME ptable
-#endif
-
-#ifndef PTABLE_VAL_FREE
-# define PTABLE_VAL_FREE(V)
-#endif
-
-#ifndef PTABLE_JOIN
-# define PTABLE_PASTE(A, B) A ## B
-# define PTABLE_JOIN(A, B) PTABLE_PASTE(A, B)
-#endif
-
-#ifndef PTABLE_PREFIX
-# define PTABLE_PREFIX(X) PTABLE_JOIN(PTABLE_NAME, X)
-#endif
-
-#ifndef ptable_ent
-typedef struct ptable_ent {
- struct ptable_ent *next;
- const void * key;
- void * val;
-} ptable_ent;
-#define ptable_ent ptable_ent
-#endif /* !ptable_ent */
-
-#ifndef ptable
-typedef struct ptable {
- ptable_ent **ary;
- size_t max;
- size_t items;
-} ptable;
-#define ptable ptable
-#endif /* !ptable */
-
-#ifndef ptable_new
-STATIC ptable *ptable_new(pPTBLMS) {
-#define ptable_new() ptable_new(aPTBLMS)
- ptable *t = VOID2(ptable *, PerlMemShared_malloc(sizeof *t));
- t->max = 15;
- t->items = 0;
- t->ary = VOID2(ptable_ent **,
- PerlMemShared_calloc(t->max + 1, sizeof *t->ary));
- return t;
-}
-#endif /* !ptable_new */
-
-#ifndef PTABLE_HASH
-# define PTABLE_HASH(ptr) \
- ((PTR2UV(ptr) >> 3) ^ (PTR2UV(ptr) >> (3 + 7)) ^ (PTR2UV(ptr) >> (3 + 17)))
-#endif
-
-#ifndef ptable_find
-STATIC ptable_ent *ptable_find(const ptable * const t, const void * const key) {
-#define ptable_find ptable_find
- ptable_ent *ent;
- const UV hash = PTABLE_HASH(key);
-
- ent = t->ary[hash & t->max];
- for (; ent; ent = ent->next) {
- if (ent->key == key)
- return ent;
- }
-
- return NULL;
-}
-#endif /* !ptable_find */
-
-#ifndef ptable_fetch
-STATIC void *ptable_fetch(const ptable * const t, const void * const key) {
-#define ptable_fetch ptable_fetch
- const ptable_ent *const ent = ptable_find(t, key);
-
- return ent ? ent->val : NULL;
-}
-#endif /* !ptable_fetch */
-
-#ifndef ptable_split
-STATIC void ptable_split(pPTBLMS_ ptable * const t) {
-#define ptable_split(T) ptable_split(aPTBLMS_ (T))
- ptable_ent **ary = t->ary;
- const size_t oldsize = t->max + 1;
- size_t newsize = oldsize * 2;
- size_t i;
-
- ary = VOID2(ptable_ent **, PerlMemShared_realloc(ary, newsize * sizeof(*ary)));
- Zero(&ary[oldsize], newsize - oldsize, sizeof(*ary));
- t->max = --newsize;
- t->ary = ary;
-
- for (i = 0; i < oldsize; i++, ary++) {
- ptable_ent **curentp, **entp, *ent;
- if (!*ary)
- continue;
- curentp = ary + oldsize;
- for (entp = ary, ent = *ary; ent; ent = *entp) {
- if ((newsize & PTABLE_HASH(ent->key)) != i) {
- *entp = ent->next;
- ent->next = *curentp;
- *curentp = ent;
- continue;
- } else
- entp = &ent->next;
- }
- }
-}
-#endif /* !ptable_split */
-
-STATIC void PTABLE_PREFIX(_store)(pPTBL_ ptable * const t, const void * const key, void * const val) {
- ptable_ent *ent = ptable_find(t, key);
-
- if (ent) {
- void *oldval = ent->val;
- PTABLE_VAL_FREE(oldval);
- ent->val = val;
- } else if (val) {
- const size_t i = PTABLE_HASH(key) & t->max;
- ent = VOID2(ptable_ent *, PerlMemShared_malloc(sizeof *ent));
- ent->key = key;
- ent->val = val;
- ent->next = t->ary[i];
- t->ary[i] = ent;
- t->items++;
- if (ent->next && t->items > t->max)
- ptable_split(t);
- }
-}
-
-STATIC void PTABLE_PREFIX(_delete)(pPTBL_ ptable * const t, const void * const key) {
- ptable_ent *prev, *ent;
- const size_t i = PTABLE_HASH(key) & t->max;
-
- prev = NULL;
- ent = t->ary[i];
- for (; ent; prev = ent, ent = ent->next) {
- if (ent->key == key)
- break;
- }
-
- if (ent) {
- if (prev)
- prev->next = ent->next;
- else
- t->ary[i] = ent->next;
- PTABLE_VAL_FREE(ent->val);
- PerlMemShared_free(ent);
- }
-}
-
-#ifndef ptable_walk
-STATIC void ptable_walk(pTHX_ ptable * const t, void (*cb)(pTHX_ ptable_ent *ent, void *userdata), void *userdata) {
-#define ptable_walk(T, CB, UD) ptable_walk(aTHX_ (T), (CB), (UD))
- if (t && t->items) {
- register ptable_ent ** const array = t->ary;
- size_t i = t->max;
- do {
- ptable_ent *entry;
- for (entry = array[i]; entry; entry = entry->next)
- if (entry->val)
- cb(aTHX_ entry, userdata);
- } while (i--);
- }
-}
-#endif /* !ptable_walk */
-
-STATIC void PTABLE_PREFIX(_clear)(pPTBL_ ptable * const t) {
- if (t && t->items) {
- register ptable_ent ** const array = t->ary;
- size_t i = t->max;
-
- do {
- ptable_ent *entry = array[i];
- while (entry) {
- ptable_ent * const oentry = entry;
- void *val = oentry->val;
- entry = entry->next;
- PTABLE_VAL_FREE(val);
- PerlMemShared_free(oentry);
- }
- array[i] = NULL;
- } while (i--);
-
- t->items = 0;
- }
-}
-
-STATIC void PTABLE_PREFIX(_free)(pPTBL_ ptable * const t) {
- if (!t)
- return;
- PTABLE_PREFIX(_clear)(aPTBL_ t);
- PerlMemShared_free(t->ary);
- PerlMemShared_free(t);
-}
-
-#undef pPTBL
-#undef pPTBL_
-#undef aPTBL
-#undef aPTBL_
-
-#undef PTABLE_NAME
-#undef PTABLE_VAL_FREE