]> git.vpit.fr Git - perl/modules/autovivification.git/blobdiff - autovivification.xs
Don't cleanup thread local storage before END blocks are executed
[perl/modules/autovivification.git] / autovivification.xs
index 62162de1c331793406b464b65a8e342b160f2e62..d92b0b7b01bb92d6a94d7cc92f545ca2b79a609c 100644 (file)
 # define MY_CXT_CLONE NOOP
 #endif
 
+#if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK)
+# define A_CHECK_MUTEX_LOCK   OP_CHECK_MUTEX_LOCK
+# define A_CHECK_MUTEX_UNLOCK OP_CHECK_MUTEX_UNLOCK
+#else
+# define A_CHECK_MUTEX_LOCK   OP_REFCNT_LOCK
+# define A_CHECK_MUTEX_UNLOCK OP_REFCNT_UNLOCK
+#endif
+
+typedef OP *(*a_ck_t)(pTHX_ OP *);
+
+#ifdef wrap_op_checker
+
+# define a_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP))
+
+#else
+
+STATIC void a_ck_replace(pTHX_ OPCODE type, a_ck_t new_ck, a_ck_t *old_ck_p) {
+#define a_ck_replace(T, NC, OCP) a_ck_replace(aTHX_ (T), (NC), (OCP))
+ A_CHECK_MUTEX_LOCK;
+ if (!*old_ck_p) {
+  *old_ck_p      = PL_check[type];
+  PL_check[type] = new_ck;
+ }
+ A_CHECK_MUTEX_UNLOCK;
+}
+
+#endif
+
+STATIC void a_ck_restore(pTHX_ OPCODE type, a_ck_t *old_ck_p) {
+#define a_ck_restore(T, OCP) a_ck_restore(aTHX_ (T), (OCP))
+ A_CHECK_MUTEX_LOCK;
+ if (*old_ck_p) {
+  PL_check[type] = *old_ck_p;
+  *old_ck_p      = 0;
+ }
+ A_CHECK_MUTEX_UNLOCK;
+}
+
 /* --- Helpers ------------------------------------------------------------- */
 
 /* ... Thread-safe hints ................................................... */
@@ -134,11 +172,11 @@ START_MY_CXT
 #if A_WORKAROUND_REQUIRE_PROPAGATION
 
 typedef struct {
- ptable *tbl;
+ ptable       *tbl;
 #if A_HAS_PERL(5, 13, 2)
  CLONE_PARAMS *params;
 #else
- CLONE_PARAMS params;
+ CLONE_PARAMS  params;
 #endif
 } a_ptable_clone_ud;
 
@@ -172,8 +210,6 @@ STATIC void a_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
 
 #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
 
-#include "reap.h"
-
 STATIC void a_thread_cleanup(pTHX_ void *ud) {
  dMY_CXT;
 
@@ -183,6 +219,29 @@ STATIC void a_thread_cleanup(pTHX_ void *ud) {
  ptable_seen_free(MY_CXT.seen);
 }
 
+STATIC int a_endav_free(pTHX_ SV *sv, MAGIC *mg) {
+ SAVEDESTRUCTOR_X(a_thread_cleanup, NULL);
+
+ return 0;
+}
+
+STATIC MGVTBL a_endav_vtbl = {
+ 0,
+ 0,
+ 0,
+ 0,
+ a_endav_free
+#if MGf_COPY
+ , 0
+#endif
+#if MGf_DUP
+ , 0
+#endif
+#if MGf_LOCAL
+ , 0
+#endif
+};
+
 #endif /* A_THREADSAFE */
 
 #if A_WORKAROUND_REQUIRE_PROPAGATION
@@ -234,19 +293,19 @@ get_enclosing_cv:
 STATIC SV *a_tag(pTHX_ UV bits) {
 #define a_tag(B) a_tag(aTHX_ (B))
  a_hint_t *h;
-#if A_THREADSAFE
- dMY_CXT;
-#endif
 
  h              = PerlMemShared_malloc(sizeof *h);
  h->bits        = bits;
  h->require_tag = a_require_tag();
 
 #if A_THREADSAFE
- /* We only need for the key to be an unique tag for looking up the value later.
-  * Allocated memory provides convenient unique identifiers, so that's why we
-  * use the hint as the key itself. */
- ptable_hints_store(MY_CXT.tbl, h, h);
+ {
+  dMY_CXT;
+  /* We only need for the key to be an unique tag for looking up the value later
+   * Allocated memory provides convenient unique identifiers, so that's why we
+   * use the hint as the key itself. */
+  ptable_hints_store(MY_CXT.tbl, h, h);
+ }
 #endif /* A_THREADSAFE */
 
  return newSViv(PTR2IV(h));
@@ -255,16 +314,16 @@ STATIC SV *a_tag(pTHX_ UV bits) {
 STATIC UV a_detag(pTHX_ const SV *hint) {
 #define a_detag(H) a_detag(aTHX_ (H))
  a_hint_t *h;
-#if A_THREADSAFE
- dMY_CXT;
-#endif
 
  if (!(hint && SvIOK(hint)))
   return 0;
 
  h = INT2PTR(a_hint_t *, SvIVX(hint));
 #if A_THREADSAFE
- h = ptable_fetch(MY_CXT.tbl, h);
+ {
+  dMY_CXT;
+  h = ptable_fetch(MY_CXT.tbl, h);
+ }
 #endif /* A_THREADSAFE */
 
  if (a_require_tag() != h->require_tag)
@@ -320,7 +379,7 @@ STATIC UV a_hint(pTHX) {
                                        0,
                                        a_hash);
 #else
- SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, a_hash);
+ SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
  if (!val)
   return 0;
  hint = *val;
@@ -331,9 +390,9 @@ STATIC UV a_hint(pTHX) {
 /* ... op => info map ...................................................... */
 
 typedef struct {
- OP *(*old_pp)(pTHX);
UV flags;
void *next;
+ OP   *(*old_pp)(pTHX);
void   *next;
UV      flags;
 } a_op_info;
 
 #define PTABLE_NAME        ptable_map
@@ -586,7 +645,7 @@ STATIC OP *a_pp_rv2av(pTHX) {
    /* We always need to push an empty array to fool the pp_aelem() that comes
     * later. */
    SV *av;
-   POPs;
+   (void) POPs;
    av = sv_2mortal((SV *) newAV());
    PUSHs(av);
    RETURN;
@@ -623,7 +682,7 @@ STATIC OP *a_pp_rv2hv(pTHX) {
  if (oi->flags & A_HINT_DEREF) {
   if (a_undef(TOPs)) {
    SV *hv;
-   POPs;
+   (void) POPs;
    hv = sv_2mortal((SV *) newHV());
    PUSHs(hv);
    RETURN;
@@ -673,7 +732,7 @@ STATIC OP *a_pp_root_unop(pTHX) {
  dSP;
 
  if (a_undef(TOPs)) {
-  POPs;
+  (void) POPs;
   /* Can only be reached by keys or values */
   if (GIMME_V == G_SCALAR) {
    dTARGET;
@@ -693,8 +752,8 @@ STATIC OP *a_pp_root_binop(pTHX) {
  dSP;
 
  if (a_undef(TOPm1s)) {
-  POPs;
-  POPs;
+  (void) POPs;
+  (void) POPs;
   if (PL_op->op_type == OP_EXISTS)
    RETPUSHNO;
   else
@@ -942,11 +1001,22 @@ STATIC void a_peep_rec(pTHX_ OP *o, ptable *seen) {
   const a_op_info *oi = NULL;
   UV flags = 0;
 
+#if !A_HAS_RPEEP
   if (ptable_fetch(seen, o))
    break;
   ptable_seen_store(seen, o, o);
+#endif
 
   switch (o->op_type) {
+#if A_HAS_RPEEP
+   case OP_NEXTSTATE:
+   case OP_DBSTATE:
+   case OP_STUB:
+    if (ptable_fetch(seen, o))
+     return;
+    ptable_seen_store(seen, o, o);
+    break;
+#endif
    case OP_PADSV:
     if (o->op_ppaddr != a_pp_deref) {
      oi = a_map_fetch(o);
@@ -1062,36 +1132,23 @@ STATIC void a_teardown(pTHX_ void *root) {
   ptable_seen_free(MY_CXT.seen);
  }
 
- PL_check[OP_PADANY] = MEMBER_TO_FPTR(a_old_ck_padany);
- a_old_ck_padany     = 0;
- PL_check[OP_PADSV]  = MEMBER_TO_FPTR(a_old_ck_padsv);
- a_old_ck_padsv      = 0;
-
- PL_check[OP_AELEM]  = MEMBER_TO_FPTR(a_old_ck_aelem);
- a_old_ck_aelem      = 0;
- PL_check[OP_HELEM]  = MEMBER_TO_FPTR(a_old_ck_helem);
- a_old_ck_helem      = 0;
- PL_check[OP_RV2SV]  = MEMBER_TO_FPTR(a_old_ck_rv2sv);
- a_old_ck_rv2sv      = 0;
-
- PL_check[OP_RV2AV]  = MEMBER_TO_FPTR(a_old_ck_rv2av);
- a_old_ck_rv2av      = 0;
- PL_check[OP_RV2HV]  = MEMBER_TO_FPTR(a_old_ck_rv2hv);
- a_old_ck_rv2hv      = 0;
-
- PL_check[OP_ASLICE] = MEMBER_TO_FPTR(a_old_ck_aslice);
- a_old_ck_aslice     = 0;
- PL_check[OP_HSLICE] = MEMBER_TO_FPTR(a_old_ck_hslice);
- a_old_ck_hslice     = 0;
-
- PL_check[OP_EXISTS] = MEMBER_TO_FPTR(a_old_ck_exists);
- a_old_ck_exists     = 0;
- PL_check[OP_DELETE] = MEMBER_TO_FPTR(a_old_ck_delete);
- a_old_ck_delete     = 0;
- PL_check[OP_KEYS]   = MEMBER_TO_FPTR(a_old_ck_keys);
- a_old_ck_keys       = 0;
- PL_check[OP_VALUES] = MEMBER_TO_FPTR(a_old_ck_values);
- a_old_ck_values     = 0;
+ a_ck_restore(OP_PADANY, &a_old_ck_padany);
+ a_ck_restore(OP_PADSV,  &a_old_ck_padsv);
+
+ a_ck_restore(OP_AELEM,  &a_old_ck_aelem);
+ a_ck_restore(OP_HELEM,  &a_old_ck_helem);
+ a_ck_restore(OP_RV2SV,  &a_old_ck_rv2sv);
+
+ a_ck_restore(OP_RV2AV,  &a_old_ck_rv2av);
+ a_ck_restore(OP_RV2HV,  &a_old_ck_rv2hv);
+
+ a_ck_restore(OP_ASLICE, &a_old_ck_aslice);
+ a_ck_restore(OP_HSLICE, &a_old_ck_hslice);
+
+ a_ck_restore(OP_EXISTS, &a_old_ck_exists);
+ a_ck_restore(OP_DELETE, &a_old_ck_delete);
+ a_ck_restore(OP_KEYS,   &a_old_ck_keys);
+ a_ck_restore(OP_VALUES, &a_old_ck_values);
 
 #if A_HAS_RPEEP
  PL_rpeepp  = a_old_peep;
@@ -1117,36 +1174,23 @@ STATIC void a_setup(pTHX) {
   MY_CXT.seen  = ptable_new();
  }
 
- a_old_ck_padany     = PL_check[OP_PADANY];
- PL_check[OP_PADANY] = MEMBER_TO_FPTR(a_ck_padany);
- a_old_ck_padsv      = PL_check[OP_PADSV];
- PL_check[OP_PADSV]  = MEMBER_TO_FPTR(a_ck_padsv);
-
- a_old_ck_aelem      = PL_check[OP_AELEM];
- PL_check[OP_AELEM]  = MEMBER_TO_FPTR(a_ck_deref);
- a_old_ck_helem      = PL_check[OP_HELEM];
- PL_check[OP_HELEM]  = MEMBER_TO_FPTR(a_ck_deref);
- a_old_ck_rv2sv      = PL_check[OP_RV2SV];
- PL_check[OP_RV2SV]  = MEMBER_TO_FPTR(a_ck_deref);
-
- a_old_ck_rv2av      = PL_check[OP_RV2AV];
- PL_check[OP_RV2AV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
- a_old_ck_rv2hv      = PL_check[OP_RV2HV];
- PL_check[OP_RV2HV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
-
- a_old_ck_aslice     = PL_check[OP_ASLICE];
- PL_check[OP_ASLICE] = MEMBER_TO_FPTR(a_ck_xslice);
- a_old_ck_hslice     = PL_check[OP_HSLICE];
- PL_check[OP_HSLICE] = MEMBER_TO_FPTR(a_ck_xslice);
-
- a_old_ck_exists     = PL_check[OP_EXISTS];
- PL_check[OP_EXISTS] = MEMBER_TO_FPTR(a_ck_root);
- a_old_ck_delete     = PL_check[OP_DELETE];
- PL_check[OP_DELETE] = MEMBER_TO_FPTR(a_ck_root);
- a_old_ck_keys       = PL_check[OP_KEYS];
- PL_check[OP_KEYS]   = MEMBER_TO_FPTR(a_ck_root);
- a_old_ck_values     = PL_check[OP_VALUES];
- PL_check[OP_VALUES] = MEMBER_TO_FPTR(a_ck_root);
+ a_ck_replace(OP_PADANY, a_ck_padany, &a_old_ck_padany);
+ a_ck_replace(OP_PADSV,  a_ck_padsv,  &a_old_ck_padsv);
+
+ a_ck_replace(OP_AELEM,  a_ck_deref,  &a_old_ck_aelem);
+ a_ck_replace(OP_HELEM,  a_ck_deref,  &a_old_ck_helem);
+ a_ck_replace(OP_RV2SV,  a_ck_deref,  &a_old_ck_rv2sv);
+
+ a_ck_replace(OP_RV2AV,  a_ck_rv2xv,  &a_old_ck_rv2av);
+ a_ck_replace(OP_RV2HV,  a_ck_rv2xv,  &a_old_ck_rv2hv);
+
+ a_ck_replace(OP_ASLICE, a_ck_xslice, &a_old_ck_aslice);
+ a_ck_replace(OP_HSLICE, a_ck_xslice, &a_old_ck_hslice);
+
+ a_ck_replace(OP_EXISTS, a_ck_root,   &a_old_ck_exists);
+ a_ck_replace(OP_DELETE, a_ck_root,   &a_old_ck_delete);
+ a_ck_replace(OP_KEYS,   a_ck_root,   &a_old_ck_keys);
+ a_ck_replace(OP_VALUES, a_ck_root,   &a_old_ck_values);
 
 #if A_HAS_RPEEP
  a_old_peep = PL_rpeepp;
@@ -1210,6 +1254,7 @@ PREINIT:
  ptable *t;
 #endif
  ptable *s;
+ GV     *gv;
 PPCODE:
  {
   dMY_CXT;
@@ -1233,7 +1278,23 @@ PPCODE:
 #endif
   MY_CXT.seen  = s;
  }
- reap(3, a_thread_cleanup, NULL);
+ gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV);
+ if (gv) {
+  CV *cv = GvCV(gv);
+  if (!PL_endav)
+   PL_endav = newAV();
+  SvREFCNT_inc(cv);
+  if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv))
+   SvREFCNT_dec(cv);
+  sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &a_endav_vtbl, NULL, 0);
+ }
+ XSRETURN(0);
+
+void
+_THREAD_CLEANUP(...)
+PROTOTYPE: DISABLE
+PPCODE:
+ a_thread_cleanup(aTHX_ NULL);
  XSRETURN(0);
 
 #endif /* A_THREADSAFE */