#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #include "const-c.inc" static SV * extract_cv(pTHX_ SV * sv){ HV * st; GV * gvp; SV * cv = (SV*) sv_2cv(sv, &st, &gvp, 0); if (!cv) croak("expected a CODE reference for watcher handler"); return cv; } static int watcher_handler(pTHX_ SV * sv, MAGIC * mg){ dSP; SV * handler = mg->mg_obj; if( handler ){ PUSHMARK(SP); XPUSHs(sv); PUTBACK; call_sv(handler, G_VOID | G_DISCARD); } return 0; } static MGVTBL modified_vtbl = { 0, watcher_handler, 0, 0, 0 }; static MGVTBL destroyed_vtbl = { 0, 0, 0, 0, watcher_handler }; static int canceller_handler(pTHX_ SV * canceller, MAGIC * mg){ SV * target = SvRV(canceller); if( SvOK(target) ){ MAGIC * target_mg = SvMAGIC(target); SV * handler_cv = (SV*) mg->mg_ptr; while( target_mg ){ if( target_mg->mg_type==PERL_MAGIC_ext && target_mg->mg_obj == handler_cv ){ #ifdef SvREFCNT_dec_NN SvREFCNT_dec_NN(handler_cv); #else SvREFCNT_dec(handler_cv); #endif target_mg->mg_flags &= ~MGf_REFCOUNTED; target_mg->mg_obj = NULL; } target_mg = target_mg->mg_moremagic; } } return 0; } static MGVTBL canceller_vtbl = { 0, 0, 0, 0, canceller_handler }; void hook_watcher_magic(pTHX_ SV * target, SV * handler, MGVTBL * vtbl){ dSP; SV * handler_cv = extract_cv(aTHX_ handler); SvUPGRADE(target, SVt_PVMG); sv_magicext(target, handler_cv, PERL_MAGIC_ext, vtbl, NULL, 0); if( GIMME_V!=G_VOID ){ SV * canceller = newRV_inc(target); sv_rvweaken(canceller); sv_magicext(canceller, NULL, PERL_MAGIC_ext, &canceller_vtbl, (char *)handler_cv, 0); PUSHs(sv_2mortal(newRV_noinc(canceller))); PUTBACK; } } MODULE = Scalar::Watcher PACKAGE = Scalar::Watcher INCLUDE: const-xs.inc void when_modified(SV * target, SV * handler) PROTOTYPE: $& PPCODE: hook_watcher_magic(aTHX_ target, handler, &modified_vtbl); SPAGAIN; void when_destroyed(SV * target, SV * handler) PROTOTYPE: $& PPCODE: hook_watcher_magic(aTHX_ target, handler, &destroyed_vtbl); SPAGAIN;