1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
static struct pe_watcher_vtbl pe_var_vtbl;
static pe_watcher *pe_var_allocate(HV *stash, SV *temple) {
pe_var *ev;
EvNew(10, ev, 1, pe_var);
ev->base.vtbl = &pe_var_vtbl;
pe_watcher_init(&ev->base, stash, temple);
ev->variable = &PL_sv_undef;
ev->events = PE_W;
WaREPEAT_on(ev);
WaINVOKE1_off(ev);
return (pe_watcher*) ev;
}
static void pe_var_dtor(pe_watcher *ev) {
pe_var *wv = (pe_var *)ev;
SvREFCNT_dec(wv->variable);
pe_watcher_dtor(ev);
EvFree(10, ev);
}
static void pe_tracevar(pe_watcher *wa, SV *sv, int got) {
/* Adapted from tkGlue.c
We are a "magic" set processor.
So we are (I think) supposed to look at "private" flags
and set the public ones if appropriate.
e.g. "chop" sets SvPOKp as a hint but not SvPOK
presumably other operators set other private bits.
Question are successive "magics" called in correct order?
i.e. if we are tracing a tied variable should we call
some magic list or be careful how we insert ourselves in the list?
*/
pe_ioevent *ev;
if (SvPOKp(sv)) SvPOK_on(sv);
if (SvNOKp(sv)) SvNOK_on(sv);
if (SvIOKp(sv)) SvIOK_on(sv);
ev = (pe_ioevent*) (*wa->vtbl->new_event)(wa);
++ev->base.hits;
ev->got |= got;
queueEvent((pe_event*) ev);
}
static I32 tracevar_r(pTHX_ IV ix, SV *sv)
{ pe_tracevar((pe_watcher *)ix, sv, PE_R); return 0; /*ignored*/ }
static I32 tracevar_w(pTHX_ IV ix, SV *sv)
{ pe_tracevar((pe_watcher *)ix, sv, PE_W); return 0; /*ignored*/ }
static char *pe_var_start(pe_watcher *_ev, int repeat) {
STRLEN n_a;
struct ufuncs *ufp;
MAGIC **mgp;
MAGIC *mg;
pe_var *ev = (pe_var*) _ev;
SV *sv = ev->variable;
if (!_ev->callback)
return "without callback";
if (!sv || !SvOK(sv))
return "watching what?";
if (!ev->events)
return "without poll events specified";
sv = SvRV(sv);
if (SvREADONLY(sv))
return "cannot trace read-only variable";
if (!SvUPGRADE(sv, SVt_PVMG))
return "SvUPGRADE failed";
mgp = &SvMAGIC(sv);
while ((mg = *mgp)) {
mgp = &mg->mg_moremagic;
}
EvNew(11, mg, 1, MAGIC);
Zero(mg, 1, MAGIC);
mg->mg_type = 'U';
mg->mg_virtual = &PL_vtbl_uvar;
*mgp = mg;
EvNew(8, ufp, 1, struct ufuncs);
ufp->uf_val = ev->events & PE_R? tracevar_r : 0;
ufp->uf_set = ev->events & PE_W? tracevar_w : 0;
ufp->uf_index = (IV) ev;
mg->mg_ptr = (char *) ufp;
mg->mg_obj = (SV*) ev;
mg_magical(sv);
if (!SvMAGICAL(sv))
return "mg_magical didn't";
return 0;
}
static void pe_var_stop(pe_watcher *_ev) {
MAGIC **mgp;
MAGIC *mg;
MAGIC *mgtmp;
pe_var *ev = (pe_var*) _ev;
SV *sv = SvRV(ev->variable);
if (SvTYPE(sv) < SVt_PVMG || !SvMAGIC(sv)) {
warn("Var unmagic'd already?");
return;
}
mgp = &SvMAGIC(sv);
while ((mg = *mgp)) {
if (mg->mg_type == 'U' && mg->mg_obj == (SV*)ev)
break;
mgp = &mg->mg_moremagic;
}
if(!mg) {
warn("Couldn't find var magic");
return;
}
*mgp = mg->mg_moremagic;
EvFree(8, mg->mg_ptr);
EvFree(11, mg);
}
static void _var_restart(pe_watcher *ev) {
if (!WaPOLLING(ev)) return;
pe_watcher_off(ev);
pe_watcher_on(ev, 0);
}
WKEYMETH(_var_events) {
pe_var *vp = (pe_var*)ev;
if (nval) {
vp->events = sv_2events_mask(nval, PE_R|PE_W);
_var_restart(ev);
}
{
dSP;
XPUSHs(sv_2mortal(events_mask_2sv(vp->events)));
PUTBACK;
}
}
WKEYMETH(_var_variable) {
pe_var *vp = (pe_var*)ev;
if (nval) {
SV *old = vp->variable;
int active = WaPOLLING(ev);
if (SvOK(nval)) {
if (!SvROK(nval))
croak("Expecting a reference");
if (SvTYPE(SvRV(nval)) > SVt_PVMG)
croak("Var watchers can only watch plain vanilla scalars");
}
if (active) pe_watcher_off(ev);
vp->variable = SvREFCNT_inc(nval);
if (active) pe_watcher_on(ev, 0);
SvREFCNT_dec(old);
}
{
dSP;
XPUSHs(vp->variable);
PUTBACK;
}
}
static void boot_var() {
pe_watcher_vtbl *vt = &pe_var_vtbl;
memcpy(vt, &pe_watcher_base_vtbl, sizeof(pe_watcher_base_vtbl));
vt->dtor = pe_var_dtor;
vt->start = pe_var_start;
vt->stop = pe_var_stop;
pe_register_vtbl(vt, gv_stashpv("Event::var",1), &ioevent_vtbl);
}
|