]> git.vpit.fr Git - perl/modules/indirect.git/blob - indirect.xs
Fix a small leak of hints objects
[perl/modules/indirect.git] / indirect.xs
1 /* This file is part of the indirect Perl module.
2  * See http://search.cpan.org/dist/indirect/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define __PACKAGE__     "indirect"
10 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
11
12 /* --- Compatibility wrappers ---------------------------------------------- */
13
14 #ifndef NOOP
15 # define NOOP
16 #endif
17
18 #ifndef dNOOP
19 # define dNOOP
20 #endif
21
22 #ifndef SvPV_const
23 # define SvPV_const SvPV
24 #endif
25
26 #ifndef SvPV_nolen_const
27 # define SvPV_nolen_const SvPV_nolen
28 #endif
29
30 #ifndef SvPVX_const
31 # define SvPVX_const SvPVX
32 #endif
33
34 #ifndef sv_catpvn_nomg
35 # define sv_catpvn_nomg sv_catpvn
36 #endif
37
38 #ifndef mPUSHu
39 # define mPUSHu(U) PUSHs(sv_2mortal(newSVuv(U)))
40 #endif
41
42 #ifndef HvNAME_get
43 # define HvNAME_get(H) HvNAME(H)
44 #endif
45
46 #ifndef HvNAMELEN_get
47 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
48 #endif
49
50 #define I_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
51
52 #if I_HAS_PERL(5, 10, 0) || defined(PL_parser)
53 # ifndef PL_lex_inwhat
54 #  define PL_lex_inwhat PL_parser->lex_inwhat
55 # endif
56 # ifndef PL_linestr
57 #  define PL_linestr PL_parser->linestr
58 # endif
59 # ifndef PL_bufptr
60 #  define PL_bufptr PL_parser->bufptr
61 # endif
62 # ifndef PL_oldbufptr
63 #  define PL_oldbufptr PL_parser->oldbufptr
64 # endif
65 #else
66 # ifndef PL_lex_inwhat
67 #  define PL_lex_inwhat PL_Ilex_inwhat
68 # endif
69 # ifndef PL_linestr
70 #  define PL_linestr PL_Ilinestr
71 # endif
72 # ifndef PL_bufptr
73 #  define PL_bufptr PL_Ibufptr
74 # endif
75 # ifndef PL_oldbufptr
76 #  define PL_oldbufptr PL_Ioldbufptr
77 # endif
78 #endif
79
80 #ifndef I_WORKAROUND_REQUIRE_PROPAGATION
81 # define I_WORKAROUND_REQUIRE_PROPAGATION !I_HAS_PERL(5, 10, 1)
82 #endif
83
84 /* ... Thread safety and multiplicity ...................................... */
85
86 #ifndef I_MULTIPLICITY
87 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
88 #  define I_MULTIPLICITY 1
89 # else
90 #  define I_MULTIPLICITY 0
91 # endif
92 #endif
93 #if I_MULTIPLICITY && !defined(tTHX)
94 # define tTHX PerlInterpreter*
95 #endif
96
97 #if I_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))
98 # define I_THREADSAFE 1
99 # ifndef MY_CXT_CLONE
100 #  define MY_CXT_CLONE \
101     dMY_CXT_SV;                                                      \
102     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
103     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
104     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
105 # endif
106 #else
107 # define I_THREADSAFE 0
108 # undef  dMY_CXT
109 # define dMY_CXT      dNOOP
110 # undef  MY_CXT
111 # define MY_CXT       indirect_globaldata
112 # undef  START_MY_CXT
113 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
114 # undef  MY_CXT_INIT
115 # define MY_CXT_INIT  NOOP
116 # undef  MY_CXT_CLONE
117 # define MY_CXT_CLONE NOOP
118 #endif
119
120 /* --- Helpers ------------------------------------------------------------- */
121
122 /* ... Thread-safe hints ................................................... */
123
124 /* If any of those are true, we need to store the hint in a global table. */
125
126 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
127
128 typedef struct {
129  SV  *code;
130 #if I_WORKAROUND_REQUIRE_PROPAGATION
131  I32  requires;
132 #endif
133 } indirect_hint_t;
134
135 #define PTABLE_NAME ptable_hints
136
137 #define PTABLE_VAL_FREE(V) \
138    { indirect_hint_t *h = (V); SvREFCNT_dec(h->code); PerlMemShared_free(h); }
139
140 #define pPTBL  pTHX
141 #define pPTBL_ pTHX_
142 #define aPTBL  aTHX
143 #define aPTBL_ aTHX_
144
145 #include "ptable.h"
146
147 #define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V))
148 #define ptable_hints_free(T)        ptable_hints_free(aTHX_ (T))
149
150 #endif /* I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION */
151
152 /* Define the op->str ptable here because we need to be able to clean it during
153  * thread cleanup. */
154
155 #define PTABLE_NAME        ptable
156 #define PTABLE_VAL_FREE(V) SvREFCNT_dec(V)
157
158 #define pPTBL  pTHX
159 #define pPTBL_ pTHX_
160 #define aPTBL  aTHX
161 #define aPTBL_ aTHX_
162
163 #include "ptable.h"
164
165 #define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V))
166 #define ptable_delete(T, K)   ptable_delete(aTHX_ (T), (K))
167 #define ptable_clear(T)       ptable_clear(aTHX_ (T))
168 #define ptable_free(T)        ptable_free(aTHX_ (T))
169
170 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
171
172 typedef struct {
173 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
174  ptable     *tbl; /* It really is a ptable_hints */
175 #endif
176  ptable     *map;
177  const char *linestr;
178 #if I_THREADSAFE
179  tTHX        owner;
180 #endif
181 } my_cxt_t;
182
183 START_MY_CXT
184
185 #if I_THREADSAFE
186
187 STATIC void indirect_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
188  my_cxt_t        *ud = ud_;
189  indirect_hint_t *h1 = ent->val;
190  indirect_hint_t *h2 = PerlMemShared_malloc(sizeof *h2);
191
192  *h2 = *h1;
193
194  if (ud->owner != aTHX) {
195   SV *val = h1->code;
196   CLONE_PARAMS param;
197   AV *stashes = (SvTYPE(val) == SVt_PVHV && HvNAME_get(val)) ? newAV() : NULL;
198   param.stashes    = stashes;
199   param.flags      = 0;
200   param.proto_perl = ud->owner;
201   h2->code = sv_dup(val, &param);
202   if (stashes) {
203    av_undef(stashes);
204    SvREFCNT_dec(stashes);
205   }
206  }
207
208  ptable_hints_store(ud->tbl, ent->key, h2);
209  SvREFCNT_inc(h2->code);
210 }
211
212 STATIC void indirect_thread_cleanup(pTHX_ void *);
213
214 STATIC void indirect_thread_cleanup(pTHX_ void *ud) {
215  int *level = ud;
216
217  if (*level) {
218   *level = 0;
219   LEAVE;
220   SAVEDESTRUCTOR_X(indirect_thread_cleanup, level);
221   ENTER;
222  } else {
223   dMY_CXT;
224   PerlMemShared_free(level);
225   ptable_free(MY_CXT.map);
226   ptable_hints_free(MY_CXT.tbl);
227  }
228 }
229
230 #endif /* I_THREADSAFE */
231
232 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
233
234 STATIC SV *indirect_tag(pTHX_ SV *value) {
235 #define indirect_tag(V) indirect_tag(aTHX_ (V))
236  indirect_hint_t *h;
237  dMY_CXT;
238
239  value = SvOK(value) && SvROK(value) ? SvRV(value) : NULL;
240
241  h = PerlMemShared_malloc(sizeof *h);
242  h->code = SvREFCNT_inc(value);
243
244 #if I_WORKAROUND_REQUIRE_PROPAGATION
245  {
246   const PERL_SI *si;
247   I32            requires = 0;
248
249   for (si = PL_curstackinfo; si; si = si->si_prev) {
250    I32 cxix;
251
252    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
253     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
254
255     if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE)
256      ++requires;
257    }
258   }
259
260   h->requires = requires;
261  }
262 #endif
263
264  /* We only need for the key to be an unique tag for looking up the value later.
265   * Allocated memory provides convenient unique identifiers, so that's why we
266   * use the value pointer as the key itself. */
267  ptable_hints_store(MY_CXT.tbl, value, h);
268
269  return newSVuv(PTR2UV(value));
270 }
271
272 STATIC SV *indirect_detag(pTHX_ const SV *hint) {
273 #define indirect_detag(H) indirect_detag(aTHX_ (H))
274  indirect_hint_t *h;
275  dMY_CXT;
276
277  if (!(hint && SvOK(hint) && SvIOK(hint)))
278   return NULL;
279
280  h = ptable_fetch(MY_CXT.tbl, INT2PTR(void *, SvUVX(hint)));
281
282 #if I_WORKAROUND_REQUIRE_PROPAGATION
283  {
284   const PERL_SI *si;
285   I32            requires = 0;
286
287   for (si = PL_curstackinfo; si; si = si->si_prev) {
288    I32 cxix;
289
290    for (cxix = si->si_cxix; cxix >= 0; --cxix) {
291     const PERL_CONTEXT *cx = si->si_cxstack + cxix;
292
293     if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE
294                                && ++requires > h->requires)
295      return NULL;
296    }
297   }
298  }
299 #endif
300
301  return h->code;
302 }
303
304 #else
305
306 STATIC SV *indirect_tag(pTHX_ SV *value) {
307 #define indirect_tag(V) indirect_tag(aTHX_ (V))
308  UV tag = 0;
309
310  if (SvOK(value) && SvROK(value)) {
311   value = SvRV(value);
312   SvREFCNT_inc(value);
313   tag = PTR2UV(value);
314  }
315
316  return newSVuv(tag);
317 }
318
319 #define indirect_detag(H) (((H) && SvOK(H)) ? INT2PTR(SV *, SvUVX(H)) : NULL)
320
321 #endif /* I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION */
322
323 STATIC U32 indirect_hash = 0;
324
325 STATIC SV *indirect_hint(pTHX) {
326 #define indirect_hint() indirect_hint(aTHX)
327  SV *hint, *code;
328 #if I_HAS_PERL(5, 9, 5)
329  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
330                                        NULL,
331                                        __PACKAGE__, __PACKAGE_LEN__,
332                                        0,
333                                        indirect_hash);
334 #else
335  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__,
336                                                                  indirect_hash);
337  if (!val)
338   return 0;
339  hint = *val;
340 #endif
341  return indirect_detag(hint);
342 }
343
344 /* ... op -> source position ............................................... */
345
346 STATIC void indirect_map_store(pTHX_ const OP *o, const char *src, SV *sv) {
347 #define indirect_map_store(O, S, N) indirect_map_store(aTHX_ (O), (S), (N))
348  dMY_CXT;
349  SV *val;
350
351  /* When lex_inwhat is set, we're in a quotelike environment (qq, qr, but not q)
352   * In this case the linestr has temporarly changed, but the old buffer should
353   * still be alive somewhere. */
354
355  if (!PL_lex_inwhat) {
356   const char *pl_linestr = SvPVX_const(PL_linestr);
357   if (MY_CXT.linestr != pl_linestr) {
358    ptable_clear(MY_CXT.map);
359    MY_CXT.linestr = pl_linestr;
360   }
361  }
362
363  val = newSVsv(sv);
364  SvUPGRADE(val, SVt_PVIV);
365  SvUVX(val) = PTR2UV(src);
366  SvIOK_on(val);
367  SvIsUV_on(val);
368  SvREADONLY_on(val);
369
370  ptable_store(MY_CXT.map, o, val);
371 }
372
373 STATIC const char *indirect_map_fetch(pTHX_ const OP *o, SV ** const name) {
374 #define indirect_map_fetch(O, S) indirect_map_fetch(aTHX_ (O), (S))
375  dMY_CXT;
376  SV *val;
377
378  if (MY_CXT.linestr != SvPVX_const(PL_linestr))
379   return NULL;
380
381  val = ptable_fetch(MY_CXT.map, o);
382  if (!val) {
383   *name = NULL;
384   return NULL;
385  }
386
387  *name = val;
388  return INT2PTR(const char *, SvUVX(val));
389 }
390
391 STATIC void indirect_map_delete(pTHX_ const OP *o) {
392 #define indirect_map_delete(O) indirect_map_delete(aTHX_ (O))
393  dMY_CXT;
394
395  ptable_delete(MY_CXT.map, o);
396 }
397
398 /* --- Check functions ----------------------------------------------------- */
399
400 STATIC const char *indirect_find(pTHX_ SV *sv, const char *s) {
401 #define indirect_find(N, S) indirect_find(aTHX_ (N), (S))
402  STRLEN len;
403  const char *p = NULL, *r = SvPV_const(sv, len);
404
405  if (len >= 1 && *r == '$') {
406   ++r;
407   --len;
408   s = strchr(s, '$');
409   if (!s)
410    return NULL;
411  }
412
413  p = strstr(s, r);
414  while (p) {
415   p += len;
416   if (!isALNUM(*p))
417    break;
418   p = strstr(p + 1, r);
419  }
420
421  return p;
422 }
423
424 /* ... ck_const ............................................................ */
425
426 STATIC OP *(*indirect_old_ck_const)(pTHX_ OP *) = 0;
427
428 STATIC OP *indirect_ck_const(pTHX_ OP *o) {
429  o = CALL_FPTR(indirect_old_ck_const)(aTHX_ o);
430
431  if (indirect_hint()) {
432   SV *sv = cSVOPo_sv;
433   if (SvPOK(sv) && (SvTYPE(sv) >= SVt_PV)) {
434    indirect_map_store(o, indirect_find(sv, PL_oldbufptr), sv);
435    return o;
436   }
437  }
438
439  indirect_map_delete(o);
440  return o;
441 }
442
443 /* ... ck_rv2sv ............................................................ */
444
445 STATIC OP *(*indirect_old_ck_rv2sv)(pTHX_ OP *) = 0;
446
447 STATIC OP *indirect_ck_rv2sv(pTHX_ OP *o) {
448  if (indirect_hint()) {
449   OP *op = cUNOPo->op_first;
450   SV *sv;
451   const char *name = NULL, *s;
452   STRLEN len;
453   OPCODE type = (OPCODE) op->op_type;
454
455   switch (type) {
456    case OP_GV:
457    case OP_GVSV: {
458     GV *gv = cGVOPx_gv(op);
459     name = GvNAME(gv);
460     len  = GvNAMELEN(gv);
461     break;
462    }
463    default:
464     if ((PL_opargs[type] & OA_CLASS_MASK) == OA_SVOP) {
465      SV *nsv = cSVOPx_sv(op);
466      if (SvPOK(nsv) && (SvTYPE(nsv) >= SVt_PV))
467       name = SvPV_const(nsv, len);
468     }
469   }
470   if (!name)
471    goto done;
472
473   sv = sv_2mortal(newSVpvn("$", 1));
474   sv_catpvn_nomg(sv, name, len);
475   s = indirect_find(sv, PL_oldbufptr);
476   if (!s) { /* If it failed, retry without the current stash */
477    const char *stash = HvNAME_get(PL_curstash);
478    STRLEN stashlen = HvNAMELEN_get(PL_curstash);
479
480    if ((len < stashlen + 2) || strnNE(name, stash, stashlen)
481        || name[stashlen] != ':' || name[stashlen+1] != ':') {
482     /* Failed again ? Try to remove main */
483     stash = "main";
484     stashlen = 4;
485     if ((len < stashlen + 2) || strnNE(name, stash, stashlen)
486         || name[stashlen] != ':' || name[stashlen+1] != ':')
487      goto done;
488    }
489
490    sv_setpvn(sv, "$", 1);
491    stashlen += 2;
492    sv_catpvn_nomg(sv, name + stashlen, len - stashlen);
493    s = indirect_find(sv, PL_oldbufptr);
494    if (!s)
495     goto done;
496   }
497
498   o = CALL_FPTR(indirect_old_ck_rv2sv)(aTHX_ o);
499   indirect_map_store(o, s, sv);
500   return o;
501  }
502
503 done:
504  o = CALL_FPTR(indirect_old_ck_rv2sv)(aTHX_ o);
505
506  indirect_map_delete(o);
507  return o;
508 }
509
510 /* ... ck_padany ........................................................... */
511
512 STATIC OP *(*indirect_old_ck_padany)(pTHX_ OP *) = 0;
513
514 STATIC OP *indirect_ck_padany(pTHX_ OP *o) {
515  o = CALL_FPTR(indirect_old_ck_padany)(aTHX_ o);
516
517  if (indirect_hint()) {
518   SV *sv;
519   const char *s = PL_oldbufptr, *t = PL_bufptr - 1;
520
521   while (s < t && isSPACE(*s)) ++s;
522   if (*s == '$' && ++s <= t) {
523    while (s < t && isSPACE(*s)) ++s;
524    while (s < t && isSPACE(*t)) --t;
525    sv = sv_2mortal(newSVpvn("$", 1));
526    sv_catpvn_nomg(sv, s, t - s + 1);
527    indirect_map_store(o, s, sv);
528    return o;
529   }
530  }
531
532  indirect_map_delete(o);
533  return o;
534 }
535
536 /* ... ck_method ........................................................... */
537
538 STATIC OP *(*indirect_old_ck_method)(pTHX_ OP *) = 0;
539
540 STATIC OP *indirect_ck_method(pTHX_ OP *o) {
541  if (indirect_hint()) {
542   OP *op = cUNOPo->op_first;
543   SV *sv;
544   const char *s = indirect_map_fetch(op, &sv);
545   if (!s) {
546    sv = cSVOPx_sv(op);
547    if (!SvPOK(sv) || (SvTYPE(sv) < SVt_PV))
548     goto done;
549    sv = sv_mortalcopy(sv);
550    s  = indirect_find(sv, PL_oldbufptr);
551   }
552   o = CALL_FPTR(indirect_old_ck_method)(aTHX_ o);
553   /* o may now be a method_named */
554   indirect_map_store(o, s, sv);
555   return o;
556  }
557
558 done:
559  o = CALL_FPTR(indirect_old_ck_method)(aTHX_ o);
560
561  indirect_map_delete(o);
562  return o;
563 }
564
565 /* ... ck_entersub ......................................................... */
566
567 STATIC OP *(*indirect_old_ck_entersub)(pTHX_ OP *) = 0;
568
569 STATIC OP *indirect_ck_entersub(pTHX_ OP *o) {
570  SV *code = indirect_hint();
571
572  o = CALL_FPTR(indirect_old_ck_entersub)(aTHX_ o);
573
574  if (code) {
575   const char *mpos, *opos;
576   SV *mnamesv, *onamesv;
577   OP *mop, *oop;
578   LISTOP *lop;
579
580   oop = o;
581   do {
582    lop = (LISTOP *) oop;
583    if (!(lop->op_flags & OPf_KIDS))
584     goto done;
585    oop = lop->op_first;
586   } while (oop->op_type != OP_PUSHMARK);
587   oop = oop->op_sibling;
588   mop = lop->op_last;
589
590   if (!oop)
591    goto done;
592
593   switch (oop->op_type) {
594    case OP_CONST:
595    case OP_RV2SV:
596    case OP_PADSV:
597     break;
598    default:
599     goto done;
600   }
601
602   if (mop->op_type == OP_METHOD)
603    mop = cUNOPx(mop)->op_first;
604   else if (mop->op_type != OP_METHOD_NAMED)
605    goto done;
606
607   mpos = indirect_map_fetch(mop, &mnamesv);
608   if (!mpos)
609    goto done;
610
611   opos = indirect_map_fetch(oop, &onamesv);
612   if (!opos)
613    goto done;
614
615   if (mpos < opos) {
616    SV     *file;
617    line_t  line;
618    dSP;
619
620    ENTER;
621    SAVETMPS;
622
623    onamesv = sv_mortalcopy(onamesv);
624    mnamesv = sv_mortalcopy(mnamesv);
625
626 #ifdef USE_ITHREADS
627    file = sv_2mortal(newSVpv(CopFILE(&PL_compiling), 0));
628 #else
629    file = sv_mortalcopy(CopFILESV(&PL_compiling));
630 #endif
631    line = CopLINE(&PL_compiling);
632
633    PUSHMARK(SP);
634    EXTEND(SP, 4);
635    PUSHs(onamesv);
636    PUSHs(mnamesv);
637    PUSHs(file);
638    mPUSHu(line);
639    PUTBACK;
640
641    call_sv(code, G_VOID);
642
643    PUTBACK;
644
645    FREETMPS;
646    LEAVE;
647   }
648  }
649
650 done:
651  return o;
652 }
653
654 STATIC U32 indirect_initialized = 0;
655
656 /* --- XS ------------------------------------------------------------------ */
657
658 MODULE = indirect      PACKAGE = indirect
659
660 PROTOTYPES: ENABLE
661
662 BOOT:
663 {
664  if (!indirect_initialized++) {
665   HV *stash;
666
667   MY_CXT_INIT;
668   MY_CXT.map     = ptable_new();
669   MY_CXT.linestr = NULL;
670 #if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
671   MY_CXT.tbl     = ptable_new();
672 #endif
673 #if I_THREADSAFE
674   MY_CXT.owner   = aTHX;
675 #endif
676
677   PERL_HASH(indirect_hash, __PACKAGE__, __PACKAGE_LEN__);
678
679   indirect_old_ck_const    = PL_check[OP_CONST];
680   PL_check[OP_CONST]       = MEMBER_TO_FPTR(indirect_ck_const);
681   indirect_old_ck_rv2sv    = PL_check[OP_RV2SV];
682   PL_check[OP_RV2SV]       = MEMBER_TO_FPTR(indirect_ck_rv2sv);
683   indirect_old_ck_padany   = PL_check[OP_PADANY];
684   PL_check[OP_PADANY]      = MEMBER_TO_FPTR(indirect_ck_padany);
685   indirect_old_ck_method   = PL_check[OP_METHOD];
686   PL_check[OP_METHOD]      = MEMBER_TO_FPTR(indirect_ck_method);
687   indirect_old_ck_entersub = PL_check[OP_ENTERSUB];
688   PL_check[OP_ENTERSUB]    = MEMBER_TO_FPTR(indirect_ck_entersub);
689
690   stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
691   newCONSTSUB(stash, "I_THREADSAFE", newSVuv(I_THREADSAFE));
692  }
693 }
694
695 #if I_THREADSAFE
696
697 void
698 CLONE(...)
699 PROTOTYPE: DISABLE
700 PREINIT:
701  ptable *t;
702  int    *level;
703 CODE:
704  {
705   my_cxt_t ud;
706   dMY_CXT;
707   ud.tbl   = t = ptable_new();
708   ud.owner = MY_CXT.owner;
709   ptable_walk(MY_CXT.tbl, indirect_ptable_clone, &ud);
710  }
711  {
712   MY_CXT_CLONE;
713   MY_CXT.map     = ptable_new();
714   MY_CXT.linestr = NULL;
715   MY_CXT.tbl     = t;
716   MY_CXT.owner   = aTHX;
717  }
718  {
719   level = PerlMemShared_malloc(sizeof *level);
720   *level = 1;
721   LEAVE;
722   SAVEDESTRUCTOR_X(indirect_thread_cleanup, level);
723   ENTER;
724  }
725
726 #endif
727
728 SV *
729 _tag(SV *value)
730 PROTOTYPE: $
731 CODE:
732  RETVAL = indirect_tag(value);
733 OUTPUT:
734  RETVAL