#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" static HV *guard_stash; static SV * guard_get_cv (pTHX_ SV *cb_sv) { HV *st; GV *gvp; CV *cv = sv_2cv (cb_sv, &st, &gvp, 0); if (!cv) croak ("expected a CODE reference for guard"); return (SV *)cv; } static void exec_guard_cb (pTHX_ SV *cb) { dSP; SV *saveerr = SvOK (ERRSV) ? sv_mortalcopy (ERRSV) : 0; SV *savedie = PL_diehook; PL_diehook = 0; PUSHSTACKi (PERLSI_DESTROY); PUSHMARK (SP); PUTBACK; call_sv (cb, G_VOID | G_DISCARD | G_EVAL); SPAGAIN; if (SvTRUE (ERRSV)) { PUSHMARK (SP); PUTBACK; call_sv (get_sv ("Guard::DIED", 1), G_VOID | G_DISCARD | G_EVAL | G_KEEPERR); SPAGAIN; sv_setpvn (ERRSV, "", 0); } if (saveerr) sv_setsv (ERRSV, saveerr); { SV *oldhook = PL_diehook; PL_diehook = savedie; SvREFCNT_dec (oldhook); } POPSTACK; PUTBACK; } static void scope_guard_cb (pTHX_ void *cv) { exec_guard_cb (aTHX_ sv_2mortal ((SV *)cv)); } static int guard_free (pTHX_ SV *cv, MAGIC *mg) { exec_guard_cb (aTHX_ mg->mg_obj); } static MGVTBL guard_vtbl = { 0, 0, 0, 0, guard_free }; MODULE = Guard PACKAGE = Guard BOOT: guard_stash = gv_stashpv ("Guard", 1); void CLONE (...) PROTOTYPE: @ CODE: guard_stash = gv_stashpv ("Guard", 1); void scope_guard (SV *block) PROTOTYPE: & CODE: LEAVE; /* unfortunately, perl sandwiches XS calls into ENTER/LEAVE */ SAVEDESTRUCTOR_X (scope_guard_cb, (void *)SvREFCNT_inc (guard_get_cv (aTHX_ block))); ENTER; /* unfortunately, perl sandwiches XS calls into ENTER/LEAVE */ SV * guard (SV *block) PROTOTYPE: & CODE: { SV *cv = guard_get_cv (aTHX_ block); SV *guard = NEWSV (0, 0); SvUPGRADE (guard, SVt_PVMG); sv_magicext (guard, cv, PERL_MAGIC_ext, &guard_vtbl, 0, 0); RETVAL = newRV_noinc (guard); SvOBJECT_on (guard); ++PL_sv_objcount; SvSTASH_set (guard, (HV*)SvREFCNT_inc ((SV *)guard_stash)); } OUTPUT: RETVAL void cancel (SV *guard) PROTOTYPE: $ CODE: { MAGIC *mg; if (!SvROK (guard) || !(mg = mg_find (SvRV (guard), PERL_MAGIC_ext)) || mg->mg_virtual != &guard_vtbl) croak ("Guard::cancel called on a non-guard object"); SvREFCNT_dec (mg->mg_obj); mg->mg_obj = 0; mg->mg_virtual = 0; }