-/* ... Thread safety and multiplicity ...................................... */
-
-/* Always safe when the workaround isn't needed */
-#if !A_WORKAROUND_REQUIRE_PROPAGATION
-# undef A_FORKSAFE
-# define A_FORKSAFE 1
-/* Otherwise, safe unless Makefile.PL says it's Win32 */
-#elif !defined(A_FORKSAFE)
-# define A_FORKSAFE 1
-#endif
-
-#ifndef A_MULTIPLICITY
-# if defined(MULTIPLICITY)
-# define A_MULTIPLICITY 1
-# else
-# define A_MULTIPLICITY 0
-# endif
-#endif
-#if A_MULTIPLICITY
-# ifndef PERL_IMPLICIT_CONTEXT
-# error MULTIPLICITY builds must set PERL_IMPLICIT_CONTEXT
-# endif
-#endif
-
-#ifndef tTHX
-# define tTHX PerlInterpreter*
-#endif
-
-#if A_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 A_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 A_THREADSAFE 0
-# undef dMY_CXT
-# define dMY_CXT dNOOP
-# undef MY_CXT
-# define MY_CXT a_globaldata
-# undef START_MY_CXT
-# define START_MY_CXT static my_cxt_t MY_CXT;
-# undef MY_CXT_INIT
-# define MY_CXT_INIT NOOP
-# undef MY_CXT_CLONE
-# define MY_CXT_CLONE NOOP
-#endif
-
-#if A_THREADSAFE
-/* We must use preexistent global mutexes or we will never be able to destroy
- * them. */
-# if A_HAS_PERL(5, 9, 3)
-# define A_LOADED_LOCK MUTEX_LOCK(&PL_my_ctx_mutex)
-# define A_LOADED_UNLOCK MUTEX_UNLOCK(&PL_my_ctx_mutex)
-# else
-# define A_LOADED_LOCK OP_REFCNT_LOCK
-# define A_LOADED_UNLOCK OP_REFCNT_UNLOCK
-# endif
-#else
-# define A_LOADED_LOCK NOOP
-# define A_LOADED_UNLOCK NOOP
-#endif
-
-#if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK)
-# define A_CHECK_LOCK OP_CHECK_MUTEX_LOCK
-# define A_CHECK_UNLOCK OP_CHECK_MUTEX_UNLOCK
-#elif A_HAS_PERL(5, 9, 3)
-# define A_CHECK_LOCK OP_REFCNT_LOCK
-# define A_CHECK_UNLOCK OP_REFCNT_UNLOCK
-#else
-/* Before perl 5.9.3, indirect_ck_*() calls are already protected by the
- * A_LOADED mutex, which falls back to the OP_REFCNT mutex. Make sure we don't
- * lock it twice. */
-# define A_CHECK_LOCK NOOP
-# define A_CHECK_UNLOCK NOOP
-#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_LOCK;
- if (!*old_ck_p) {
- *old_ck_p = PL_check[type];
- PL_check[type] = new_ck;
- }
- A_CHECK_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_LOCK;
- if (*old_ck_p) {
- PL_check[type] = *old_ck_p;
- *old_ck_p = 0;
- }
- A_CHECK_UNLOCK;
-}
-
-/* --- Helpers ------------------------------------------------------------- */
-
-/* ... Check if the module is loaded ....................................... */
-
-static I32 a_loaded = 0;
-
-#if A_THREADSAFE
-
-#define PTABLE_NAME ptable_loaded
-#define PTABLE_NEED_DELETE 1
-#define PTABLE_NEED_WALK 0
-
-#include "ptable.h"
-
-#define ptable_loaded_store(T, K, V) ptable_loaded_store(aPTBLMS_ (T), (K), (V))
-#define ptable_loaded_delete(T, K) ptable_loaded_delete(aPTBLMS_ (T), (K))
-#define ptable_loaded_free(T) ptable_loaded_free(aPTBLMS_ (T))
-
-static ptable *a_loaded_cxts = NULL;
-
-static int a_is_loaded(pTHX_ void *cxt) {
-#define a_is_loaded(C) a_is_loaded(aTHX_ (C))
- int res = 0;
-
- A_LOADED_LOCK;
- if (a_loaded_cxts && ptable_fetch(a_loaded_cxts, cxt))
- res = 1;
- A_LOADED_UNLOCK;
-
- return res;
-}
-
-static int a_set_loaded_locked(pTHX_ void *cxt) {
-#define a_set_loaded_locked(C) a_set_loaded_locked(aTHX_ (C))
- int global_setup = 0;
-
- if (a_loaded <= 0) {
- assert(a_loaded == 0);
- assert(!a_loaded_cxts);
- a_loaded_cxts = ptable_new();
- global_setup = 1;
- }
- ++a_loaded;
- assert(a_loaded_cxts);
- ptable_loaded_store(a_loaded_cxts, cxt, cxt);
-
- return global_setup;
-}
-
-static int a_clear_loaded_locked(pTHX_ void *cxt) {
-#define a_clear_loaded_locked(C) a_clear_loaded_locked(aTHX_ (C))
- int global_teardown = 0;
-
- if (a_loaded > 1) {
- assert(a_loaded_cxts);
- ptable_loaded_delete(a_loaded_cxts, cxt);
- --a_loaded;
- } else if (a_loaded_cxts) {
- assert(a_loaded == 1);
- ptable_loaded_free(a_loaded_cxts);
- a_loaded_cxts = NULL;
- a_loaded = 0;
- global_teardown = 1;
- }
-
- return global_teardown;
-}
-
-#else
-
-#define a_is_loaded(C) (a_loaded > 0)
-#define a_set_loaded_locked(C) ((a_loaded++ <= 0) ? 1 : 0)
-#define a_clear_loaded_locked(C) ((--a_loaded <= 0) ? 1 : 0)
-
-#endif
-
-/* ... Thread-safe hints ................................................... */
-
-#if A_WORKAROUND_REQUIRE_PROPAGATION
-
-typedef struct {
- U32 bits;
- IV require_tag;
-} a_hint_t;
-
-#define A_HINT_FREE(H) PerlMemShared_free(H)
-
-#if A_THREADSAFE
-
-#define PTABLE_NAME ptable_hints
-#define PTABLE_VAL_FREE(V) A_HINT_FREE(V)
-#define PTABLE_NEED_DELETE 0
-#define PTABLE_NEED_WALK 1
-
-#define pPTBL pTHX
-#define pPTBL_ pTHX_
-#define aPTBL aTHX
-#define aPTBL_ aTHX_
-
-#include "ptable.h"
-
-#define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
-#define ptable_hints_free(T) ptable_hints_free(aTHX_ (T))
-
-#endif /* A_THREADSAFE */
-
-#endif /* A_WORKAROUND_REQUIRE_PROPAGATION */
-
-#define PTABLE_NAME ptable_seen
-#define PTABLE_NEED_DELETE 0
-#define PTABLE_NEED_WALK 0
-
-#include "ptable.h"
-
-/* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
-#define ptable_seen_store(T, K, V) ptable_seen_store(aPTBLMS_ (T), (K), (V))
-#define ptable_seen_clear(T) ptable_seen_clear(aPTBLMS_ (T))
-#define ptable_seen_free(T) ptable_seen_free(aPTBLMS_ (T))
-
-#define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
-
-typedef struct {
- peep_t old_peep; /* This is actually the rpeep past 5.13.5 */
- ptable *seen; /* It really is a ptable_seen */
-#if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION
- ptable *tbl; /* It really is a ptable_hints */
- tTHX owner;
-#endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */
-} my_cxt_t;
-
-START_MY_CXT
-
-#if A_WORKAROUND_REQUIRE_PROPAGATION
-
-#if A_THREADSAFE
-
-typedef struct {
- ptable *tbl;
-#if A_HAS_PERL(5, 13, 2)
- CLONE_PARAMS *params;
-#else
- CLONE_PARAMS params;
-#endif
-} a_ptable_clone_ud;
-
-#if A_HAS_PERL(5, 13, 2)
-# define a_ptable_clone_ud_init(U, T, O) \
- (U).tbl = (T); \
- (U).params = Perl_clone_params_new((O), aTHX)
-# define a_ptable_clone_ud_deinit(U) Perl_clone_params_del((U).params)
-# define a_dup_inc(S, U) SvREFCNT_inc(sv_dup((S), (U)->params))
-#else
-# define a_ptable_clone_ud_init(U, T, O) \
- (U).tbl = (T); \
- (U).params.stashes = newAV(); \
- (U).params.flags = 0; \
- (U).params.proto_perl = (O)
-# define a_ptable_clone_ud_deinit(U) SvREFCNT_dec((U).params.stashes)
-# define a_dup_inc(S, U) SvREFCNT_inc(sv_dup((S), &((U)->params)))
-#endif
-
-static void a_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
- a_ptable_clone_ud *ud = ud_;
- a_hint_t *h1 = ent->val;
- a_hint_t *h2;
-
- h2 = PerlMemShared_malloc(sizeof *h2);
- h2->bits = h1->bits;
- h2->require_tag = PTR2IV(a_dup_inc(INT2PTR(SV *, h1->require_tag), ud));
-
- ptable_hints_store(ud->tbl, ent->key, h2);
-}
-
-#endif /* A_THREADSAFE */
-
-static IV a_require_tag(pTHX) {
-#define a_require_tag() a_require_tag(aTHX)
- const CV *cv, *outside;
-
- cv = PL_compcv;
-
- if (!cv) {
- /* If for some reason the pragma is operational at run-time, try to discover
- * the current cv in use. */
- const PERL_SI *si;
-
- for (si = PL_curstackinfo; si; si = si->si_prev) {
- I32 cxix;
-
- for (cxix = si->si_cxix; cxix >= 0; --cxix) {
- const PERL_CONTEXT *cx = si->si_cxstack + cxix;
-
- switch (CxTYPE(cx)) {
- case CXt_SUB:
- case CXt_FORMAT:
- /* The propagation workaround is only needed up to 5.10.0 and at that
- * time format and sub contexts were still identical. And even later the
- * cv members offsets should have been kept the same. */
- cv = cx->blk_sub.cv;
- goto get_enclosing_cv;
- case CXt_EVAL:
- cv = cx->blk_eval.cv;
- goto get_enclosing_cv;
- default:
- break;
- }
- }
- }
-
- cv = PL_main_cv;
- }
-
-get_enclosing_cv:
- for (outside = CvOUTSIDE(cv); outside; outside = CvOUTSIDE(cv))
- cv = outside;
-
- return PTR2IV(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;
-
- if (!MY_CXT.tbl)
- return newSViv(0);
-#endif /* A_THREADSAFE */
-
- 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);
-#endif /* A_THREADSAFE */
-
- return newSViv(PTR2IV(h));
-}
-
-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;
-
- if (!MY_CXT.tbl)
- return 0;
-#endif /* A_THREADSAFE */
-
- if (!(hint && SvIOK(hint)))
- return 0;
-
- h = INT2PTR(a_hint_t *, SvIVX(hint));
-#if A_THREADSAFE
- h = ptable_fetch(MY_CXT.tbl, h);
-#endif /* A_THREADSAFE */
-
- if (a_require_tag() != h->require_tag)
- return 0;
-
- return h->bits;
-}
-
-#else /* A_WORKAROUND_REQUIRE_PROPAGATION */
-
-#define a_tag(B) newSVuv(B)
-/* PVs fetched from the hints chain have their SvLEN set to zero, so get the UV
- * from a copy. */
-#define a_detag(H) \
- ((H) \
- ? (SvIOK(H) \
- ? SvUVX(H) \
- : (SvPOK(H) \
- ? sv_2uv(SvLEN(H) ? (H) : sv_mortalcopy(H)) \
- : 0 \
- ) \
- ) \
- : 0)
-
-#endif /* !A_WORKAROUND_REQUIRE_PROPAGATION */
-
-/* Used both for hints and op flags */
-#define A_HINT_STRICT 1
-#define A_HINT_WARN 2
-#define A_HINT_FETCH 4
-#define A_HINT_STORE 8
-#define A_HINT_EXISTS 16
-#define A_HINT_DELETE 32
-#define A_HINT_NOTIFY (A_HINT_STRICT|A_HINT_WARN)
-#define A_HINT_DO (A_HINT_FETCH|A_HINT_STORE|A_HINT_EXISTS|A_HINT_DELETE)
-#define A_HINT_MASK (A_HINT_NOTIFY|A_HINT_DO)
-
-/* Only used in op flags */
-#define A_HINT_ROOT 64
-#define A_HINT_DEREF 128
-
-static VOL U32 a_hash = 0;
-
-static UV a_hint(pTHX) {
-#define a_hint() a_hint(aTHX)
- SV *hint;
-#ifdef cop_hints_fetch_pvn
- hint = cop_hints_fetch_pvn(PL_curcop, __PACKAGE__, __PACKAGE_LEN__, a_hash, 0);
-#elif A_HAS_PERL(5, 9, 5)
- hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
- NULL,
- __PACKAGE__, __PACKAGE_LEN__,
- 0,
- a_hash);
-#else
- SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
- if (!val)
- return 0;
- hint = *val;
-#endif
- return a_detag(hint);
-}
-
-/* ... op => info map ...................................................... */