Skip Menu |

This queue is for tickets about the Clone CPAN distribution.

Report information
The Basics
Id: 41551
Status: resolved
Priority: 0/
Queue: Clone

People
Owner: Nobody in particular
Requestors: smueller [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Critical
Broken in: 0.29
Fixed in: (no value)



Hi Ray, thanks for Clone. It's highly useful! Unfortunately, Clone isn't thread safe because it uses a global C-level HV *HSEEN. I'm involved in the development of the Padre editor and we have to use threads for concurrency. Our dependencies use Clone, and so do we. Please consider applying the patch (against Clone 0.29) below and making a quick release. If I can be of any assistance, please let me know. I'm prepared to go at as far as making the release for you if you let me. The appended patch replaces the global HSEEN with an HV* which is created in the clone XSUB and passed as an argument to the various *_clone functions. Naturally, this comes with a slight performance penalty. Some benchmarking suggested between 2-3%. But it's better to be correct than fast. Best regards, Steffen
Subject: Clone-thread-saftey.patch
--- Clone-0.29/Clone.xs 2008-07-12 18:49:01.000000000 +0200 +++ C2/Clone.xs 2008-12-08 20:35:01.000000000 +0100 @@ -10,9 +10,9 @@ #define CLONE_STORE(x,y) \ do { \ - if (!hv_store(HSEEN, CLONE_KEY(x), PTRSIZE, SvREFCNT_inc(y), 0)) { \ + if (!hv_store(hseen, CLONE_KEY(x), PTRSIZE, SvREFCNT_inc(y), 0)) { \ SvREFCNT_dec(y); /* Restore the refcount */ \ - croak("Can't store clone in seen hash (HSEEN)"); \ + croak("Can't store clone in seen hash (hseen)"); \ } \ else { \ TRACEME(("storing ref = 0x%x clone = 0x%x\n", ref, clone)); \ @@ -21,14 +21,12 @@ } \ } while (0) -#define CLONE_FETCH(x) (hv_fetch(HSEEN, CLONE_KEY(x), PTRSIZE, 0)) +#define CLONE_FETCH(x) (hv_fetch(hseen, CLONE_KEY(x), PTRSIZE, 0)) -static SV *hv_clone (SV *, SV *, int); -static SV *av_clone (SV *, SV *, int); -static SV *sv_clone (SV *, int); -static SV *rv_clone (SV *, int); - -static HV *HSEEN; +static SV *hv_clone (SV *, SV *, HV *, int); +static SV *av_clone (SV *, SV *, HV *, int); +static SV *sv_clone (SV *, HV *, int); +static SV *rv_clone (SV *, HV *, int); #ifdef DEBUG_CLONE #define TRACEME(a) printf("%s:%d: ",__FUNCTION__, __LINE__) && printf a; @@ -37,7 +35,7 @@ #endif static SV * -hv_clone (SV * ref, SV * target, int depth) +hv_clone (SV * ref, SV * target, HV* hseen, int depth) { HV *clone = (HV *) target; HV *self = (HV *) ref; @@ -53,7 +51,7 @@ { SV *key = hv_iterkeysv (next); hv_store_ent (clone, key, - sv_clone (hv_iterval (self, next), recur), 0); + sv_clone (hv_iterval (self, next), hseen, recur), 0); } TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone))); @@ -61,7 +59,7 @@ } static SV * -av_clone (SV * ref, SV * target, int depth) +av_clone (SV * ref, SV * target, HV* hseen, int depth) { AV *clone = (AV *) target; AV *self = (AV *) ref; @@ -87,7 +85,7 @@ { svp = av_fetch (self, i, 0); if (svp) - av_store (clone, i, sv_clone (*svp, recur)); + av_store (clone, i, sv_clone (*svp, hseen, recur)); } TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone))); @@ -95,7 +93,7 @@ } static SV * -rv_clone (SV * ref, int depth) +rv_clone (SV * ref, HV* hseen, int depth) { SV *clone = NULL; SV *rv = NULL; @@ -109,18 +107,18 @@ if (sv_isobject (ref)) { - clone = newRV_noinc(sv_clone (SvRV(ref), depth)); + clone = newRV_noinc(sv_clone (SvRV(ref), hseen, depth)); sv_2mortal (sv_bless (clone, SvSTASH (SvRV (ref)))); } else - clone = newRV_inc(sv_clone (SvRV(ref), depth)); + clone = newRV_inc(sv_clone (SvRV(ref), hseen, depth)); TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone))); return clone; } static SV * -sv_clone (SV * ref, int depth) +sv_clone (SV * ref, HV* hseen, int depth) { SV *clone = ref; SV **seen = NULL; @@ -252,7 +250,7 @@ continue; break; default: - obj = sv_clone(mg->mg_obj, -1); + obj = sv_clone(mg->mg_obj, hseen, -1); } } else { TRACEME(("magic object for type %c in NULL\n", mg->mg_type)); @@ -275,15 +273,15 @@ ;; } else if ( SvTYPE(ref) == SVt_PVHV ) - clone = hv_clone (ref, clone, depth); + clone = hv_clone (ref, clone, hseen, depth); else if ( SvTYPE(ref) == SVt_PVAV ) - clone = av_clone (ref, clone, depth); + clone = av_clone (ref, clone, hseen, depth); /* 3: REFERENCE (inlined for speed) */ else if (SvROK (ref)) { TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone))); SvREFCNT_dec(SvRV(clone)); - SvRV(clone) = sv_clone (SvRV(ref), depth); /* Clone the referent */ + SvRV(clone) = sv_clone (SvRV(ref), hseen, depth); /* Clone the referent */ if (sv_isobject (ref)) { sv_bless (clone, SvSTASH (SvRV (ref))); @@ -301,10 +299,6 @@ PROTOTYPES: ENABLE -BOOT: -/* Initialize HSEEN */ -HSEEN = newHV(); if (!HSEEN) croak ("Can't initialize seen hash (HSEEN)"); - void clone(self, depth=-1) SV *self @@ -312,8 +306,10 @@ PREINIT: SV * clone = &PL_sv_undef; PPCODE: + HV *hseen = newHV(); TRACEME(("ref = 0x%x\n", self)); - clone = sv_clone(self, depth); - hv_clear(HSEEN); /* Free HV */ + clone = sv_clone(self, hseen, depth); + hv_clear(hseen); /* Free HV */ + SvREFCNT_dec((SV *)hseen); EXTEND(SP,1); PUSHs(sv_2mortal(clone));
I applied the patch to Clone-0.30, which was just uploaded to CPAN.