1 static struct pe_watcher_vtbl pe_idle_vtbl;
2 static pe_ring Idle;
3 
4 /*#define D_IDLE(x) x  /**/
5 #define D_IDLE(x)  /**/
6 
pe_idle_allocate(HV * stash,SV * temple)7 static pe_watcher *pe_idle_allocate(HV *stash, SV *temple) {
8     pe_idle *ev;
9     EvNew(3, ev, 1, pe_idle);
10     ev->base.vtbl = &pe_idle_vtbl;
11     pe_watcher_init(&ev->base, stash, temple);
12     PE_RING_INIT(&ev->tm.ring, ev);
13     PE_RING_INIT(&ev->iring, ev);
14     ev->max_interval = &PL_sv_undef;
15     ev->min_interval = newSVnv(.01);
16     return (pe_watcher*) ev;
17 }
18 
pe_idle_dtor(pe_watcher * ev)19 static void pe_idle_dtor(pe_watcher *ev) {
20     pe_idle *ip = (pe_idle*) ev;
21     SvREFCNT_dec(ip->max_interval);
22     SvREFCNT_dec(ip->min_interval);
23     pe_watcher_dtor(ev);
24     EvFree(3, ev);
25 }
26 
pe_idle_start(pe_watcher * ev,int repeating)27 static char *pe_idle_start(pe_watcher *ev, int repeating) {
28     NV now;
29     NV min,max;
30     pe_idle *ip = (pe_idle*) ev;
31     if (!ev->callback)
32 	return "without callback";
33     if (!repeating) ev->cbtime = NVtime();
34     now = WaHARD(ev)? ev->cbtime : NVtime();
35     if (sv_2interval("min", ip->min_interval, &min)) {
36 	ip->tm.at = min + now;
37 	pe_timeable_start(&ip->tm);
38 	D_IDLE(warn("min %.2f setup '%s'\n", min, SvPV(ev->desc,na)));
39     }
40     else {
41 	PE_RING_UNSHIFT(&ip->iring, &Idle);
42 	D_IDLE(warn("idle '%s'\n", SvPV(ev->desc,na)));
43 	if (sv_2interval("max", ip->max_interval, &max)) {
44 	    D_IDLE(warn("max %.2f setup '%s'\n", max, SvPV(ev->desc,na)));
45 	    ip->tm.at = max + now;
46 	    pe_timeable_start(&ip->tm);
47 	}
48     }
49     return 0;
50 }
51 
pe_idle_alarm(pe_watcher * wa,pe_timeable * _ignore)52 static void pe_idle_alarm(pe_watcher *wa, pe_timeable *_ignore) {
53     NV now = NVtime();
54     NV min,max,left;
55     pe_idle *ip = (pe_idle*) wa;
56     pe_timeable_stop(&ip->tm);
57     if (sv_2interval("min", ip->min_interval, &min)) {
58 	left = wa->cbtime + min - now;
59 	if (left > IntervalEpsilon) {
60 	    ++TimeoutTooEarly;
61 	    ip->tm.at = now + left;
62 	    pe_timeable_start(&ip->tm);
63 	    D_IDLE(warn("min %.2f '%s'\n", left, SvPV(wa->desc,na)));
64 	    return;
65 	}
66     }
67     if (PE_RING_EMPTY(&ip->iring)) {
68 	PE_RING_UNSHIFT(&ip->iring, &Idle);
69 	D_IDLE(warn("idle '%s'\n", SvPV(wa->desc,na)));
70     }
71     if (sv_2interval("max", ip->max_interval, &max)) {
72 	left = wa->cbtime + max - now;
73 	if (left < IntervalEpsilon) {
74 	    pe_event *ev;
75 	    D_IDLE(warn("max '%s'\n", SvPV(wa->desc,na)));
76 	    PE_RING_DETACH(&ip->iring);
77 	    ev = (*wa->vtbl->new_event)(wa);
78 	    ++ev->hits;
79 	    queueEvent(ev);
80 	    return;
81 	}
82 	else {
83 	    ++TimeoutTooEarly;
84 	    ip->tm.at = now + left;
85 	    D_IDLE(warn("max %.2f '%s'\n", left, SvPV(wa->desc,na)));
86 	    pe_timeable_start(&ip->tm);
87 	}
88     }
89 }
90 
pe_idle_stop(pe_watcher * ev)91 static void pe_idle_stop(pe_watcher *ev) {
92     pe_idle *ip = (pe_idle*) ev;
93     PE_RING_DETACH(&ip->iring);
94     pe_timeable_stop(&ip->tm);
95 }
96 
WKEYMETH(_idle_max_interval)97 WKEYMETH(_idle_max_interval) {
98     pe_idle *ip = (pe_idle*) ev;
99     if (nval) {
100 	SV *old = ip->max_interval;
101 	ip->max_interval = SvREFCNT_inc(nval);
102 	if (old) SvREFCNT_dec(old);
103 	VERIFYINTERVAL("max", ip->max_interval);
104     }
105     {
106 	dSP;
107 	XPUSHs(ip->max_interval);
108 	PUTBACK;
109     }
110 }
111 
WKEYMETH(_idle_min_interval)112 WKEYMETH(_idle_min_interval) {
113     pe_idle *ip = (pe_idle*) ev;
114     if (nval) {
115 	SV *old = ip->min_interval;
116 	ip->min_interval = SvREFCNT_inc(nval);
117 	if (old) SvREFCNT_dec(old);
118 	VERIFYINTERVAL("min", ip->min_interval);
119     }
120     {
121 	dSP;
122 	XPUSHs(ip->min_interval);
123 	PUTBACK;
124     }
125 }
126 
boot_idle()127 static void boot_idle() {
128     pe_watcher_vtbl *vt = &pe_idle_vtbl;
129     PE_RING_INIT(&Idle, 0);
130     memcpy(vt, &pe_watcher_base_vtbl, sizeof(pe_watcher_base_vtbl));
131     vt->dtor = pe_idle_dtor;
132     vt->start = pe_idle_start;
133     vt->stop = pe_idle_stop;
134     vt->alarm = pe_idle_alarm;
135     pe_register_vtbl(vt, gv_stashpv("Event::idle",1), &event_vtbl);
136 }
137