File Coverage

Interrupt.xs
Criterion Covered Total %
statement 165 198 83.3
branch 98 224 43.7
condition n/a
subroutine n/a
pod n/a
total 263 422 62.3


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4              
5             #define ECB_NO_LIBM 1
6             #define ECB_NO_THREADS 1
7             #include "ecb.h"
8             #include "schmorp.h"
9              
10             typedef volatile sig_atomic_t atomic_t;
11              
12             static int *sig_pending, *psig_pend; /* make local copies because of missing THX */
13             static Sighandler_t old_sighandler;
14             static atomic_t async_pending;
15              
16             #define PERL_VERSION_ATLEAST(a,b,c) \
17             (PERL_REVISION > (a) \
18             || (PERL_REVISION == (a) \
19             && (PERL_VERSION > (b) \
20             || (PERL_VERSION == (b) && PERL_SUBVERSION >= (c)))))
21              
22             #if defined(HAS_SIGACTION) && defined(SA_SIGINFO)
23             # define HAS_SA_SIGINFO 1
24             #endif
25              
26             #if !PERL_VERSION_ATLEAST(5,10,0)
27             # undef HAS_SA_SIGINFO
28             #endif
29              
30             /*****************************************************************************/
31              
32             typedef struct {
33             SV *cb;
34             void (*c_cb)(pTHX_ void *c_arg, int value);
35             void *c_arg;
36             SV *fh_r, *fh_w;
37             SV *value;
38             int signum;
39             int autodrain;
40             ANY *scope_savestack;
41             volatile int blocked;
42              
43             s_epipe ep;
44             int fd_wlen;
45             atomic_t fd_enable;
46             atomic_t pending;
47             volatile IV *valuep;
48             atomic_t hysteresis;
49             } async_t;
50              
51             static AV *asyncs;
52             static async_t *sig_async [SIG_SIZE];
53              
54             #define SvASYNC_nrv(sv) INT2PTR (async_t *, SvIVX (sv))
55             #define SvASYNC(rv) SvASYNC_nrv (SvRV (rv))
56              
57             static void async_signal (void *signal_arg, int value);
58              
59             static void
60 10           setsig (int signum, void (*handler)(int))
61             {
62             #if _WIN32
63             signal (signum, handler);
64             #else
65             struct sigaction sa;
66 10           sa.sa_handler = handler;
67 10           sigfillset (&sa.sa_mask);
68 10           sa.sa_flags = 0; /* if we interrupt a syscall, we might drain the pipe before it became ready */
69 10           sigaction (signum, &sa, 0);
70             #endif
71 10           }
72              
73             static void
74 6           async_sigsend (int signum)
75             {
76 6           async_signal (sig_async [signum], 0);
77 6           }
78              
79             /* the main workhorse to signal */
80             static void
81 19           async_signal (void *signal_arg, int value)
82             {
83             static char pipedata [8];
84              
85 19           async_t *async = (async_t *)signal_arg;
86 19           int pending = async->pending;
87              
88 19 100         if (async->hysteresis)
89 4           setsig (async->signum, SIG_IGN);
90              
91 19 100         *async->valuep = value ? value : 1;
92             ECB_MEMORY_FENCE_RELEASE;
93 19           async->pending = 1;
94             ECB_MEMORY_FENCE_RELEASE;
95 19           async_pending = 1;
96             ECB_MEMORY_FENCE_RELEASE;
97              
98 19 100         if (!async->blocked)
99             {
100 11           psig_pend [9] = 1;
101             ECB_MEMORY_FENCE_RELEASE;
102 11           *sig_pending = 1;
103             ECB_MEMORY_FENCE_RELEASE;
104             }
105              
106 19 50         if (!pending && async->fd_enable && async->ep.len)
    100          
    50          
107 6           s_epipe_signal (&async->ep);
108 19           }
109              
110             static void
111 19           handle_async (async_t *async)
112             {
113 19           int old_errno = errno;
114 19           int value = *async->valuep;
115              
116 19           *async->valuep = 0;
117 19           async->pending = 0;
118              
119             /* restore signal */
120 19 100         if (async->hysteresis)
121 4           setsig (async->signum, async_sigsend);
122              
123             /* drain pipe */
124 19 100         if (async->fd_enable && async->ep.len && async->autodrain)
    50          
    50          
125 6           s_epipe_drain (&async->ep);
126              
127 19 50         if (async->c_cb)
128             {
129             dTHX;
130 0           async->c_cb (aTHX_ async->c_arg, value);
131             }
132              
133 19 50         if (async->cb)
134             {
135 19           dSP;
136              
137 19 50         SV *saveerr = SvOK (ERRSV) ? sv_mortalcopy (ERRSV) : 0;
    50          
    0          
    0          
    0          
    0          
    50          
138 19           SV *savedie = PL_diehook;
139              
140 19           PL_diehook = 0;
141              
142 19 50         PUSHSTACKi (PERLSI_SIGNAL);
143              
144 19 50         PUSHMARK (SP);
145 19 50         XPUSHs (sv_2mortal (newSViv (value)));
146 19           PUTBACK;
147 19           call_sv (async->cb, G_VOID | G_DISCARD | G_EVAL);
148              
149 19 50         if (SvTRUE (ERRSV))
    50          
    50          
    50          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
150             {
151 0           SPAGAIN;
152              
153 0 0         PUSHMARK (SP);
154 0           PUTBACK;
155 0           call_sv (get_sv ("Async::Interrupt::DIED", 1), G_VOID | G_DISCARD | G_EVAL | G_KEEPERR);
156              
157 0 0         sv_setpvn (ERRSV, "", 0);
158             }
159              
160 19 50         if (saveerr)
161 19 50         sv_setsv (ERRSV, saveerr);
162              
163             {
164 19           SV *oldhook = PL_diehook;
165 19           PL_diehook = savedie;
166 19           SvREFCNT_dec (oldhook);
167             }
168              
169 19 50         POPSTACK;
170             }
171              
172 19           errno = old_errno;
173 19           }
174              
175             static void
176 11           handle_asyncs (void)
177             {
178             int i;
179              
180             ECB_MEMORY_FENCE_ACQUIRE;
181              
182 11           async_pending = 0;
183              
184 23 100         for (i = AvFILLp (asyncs); i >= 0; --i)
185             {
186 12           SV *async_sv = AvARRAY (asyncs)[i];
187 12           async_t *async = SvASYNC_nrv (async_sv);
188              
189 12 100         if (async->pending && !async->blocked)
    50          
190             {
191             /* temporarily keep a refcount */
192 11           SvREFCNT_inc (async_sv);
193 11           handle_async (async);
194 11           SvREFCNT_dec (async_sv);
195              
196             /* the handler could have deleted any number of asyncs */
197 11 50         if (i > AvFILLp (asyncs))
198 0           i = AvFILLp (asyncs);
199             }
200             }
201 11           }
202              
203             #if HAS_SA_SIGINFO
204 11           static Signal_t async_sighandler (int signum, siginfo_t *si, void *sarg)
205             {
206 11 50         if (signum == 9)
207 11           handle_asyncs ();
208             else
209 0           old_sighandler (signum, si, sarg);
210 11           }
211             #else
212             static Signal_t async_sighandler (int signum)
213             {
214             if (signum == 9)
215             handle_asyncs ();
216             else
217             old_sighandler (signum);
218             }
219             #endif
220              
221             #define block(async) ++(async)->blocked
222              
223             static void
224 8           unblock (async_t *async)
225             {
226 8           --async->blocked;
227 8 50         if (async->pending && !async->blocked)
    50          
228 8           handle_async (async);
229 8           }
230              
231             static void
232 4           scope_block_cb (pTHX_ void *async_sv)
233             {
234 4           async_t *async = SvASYNC_nrv ((SV *)async_sv);
235              
236 4           async->scope_savestack = 0;
237 4           unblock (async);
238 4           SvREFCNT_dec (async_sv);
239 4           }
240              
241             static void
242 5           scope_block (SV *async_sv)
243             {
244 5           async_t *async = SvASYNC_nrv (async_sv);
245              
246             /* as a heuristic, we skip the scope block if we already are blocked */
247             /* and the existing scope block used the same savestack */
248              
249 5 100         if (!async->scope_savestack || async->scope_savestack != PL_savestack)
    50          
250             {
251 4           async->scope_savestack = PL_savestack;
252 4           block (async);
253              
254 4           LEAVE; /* unfortunately, perl sandwiches XS calls into ENTER/LEAVE */
255 4           SAVEDESTRUCTOR_X (scope_block_cb, (void *)SvREFCNT_inc (async_sv));
256 4           ENTER; /* unfortunately, perl sandwiches XS calls into ENTER/LEAVE */
257             }
258 5           }
259              
260             MODULE = Async::Interrupt PACKAGE = Async::Interrupt
261              
262             BOOT:
263 7           old_sighandler = PL_sighandlerp;
264 7           PL_sighandlerp = async_sighandler;
265 7           sig_pending = &PL_sig_pending;
266 7           psig_pend = PL_psig_pend;
267 7           asyncs = newAV ();
268 7           CvNODEBUG_on (get_cv ("Async::Interrupt::scope_block", 0)); /* otherwise calling scope can be the debugger */
269              
270             PROTOTYPES: DISABLE
271              
272             void
273             _alloc (SV *cb, void *c_cb, void *c_arg, SV *fh_r, SV *fh_w, SV *signl, SV *pvalue)
274             PPCODE:
275             {
276 7 100         SV *cv = SvOK (cb) ? SvREFCNT_inc (s_get_cv_croak (cb)) : 0;
    50          
    50          
277             async_t *async;
278              
279 7           Newz (0, async, 1, async_t);
280              
281 7 50         XPUSHs (sv_2mortal (newSViv (PTR2IV (async))));
282             /* TODO: need to bless right now to ensure deallocation */
283 7           av_push (asyncs, TOPs);
284              
285 7 50         SvGETMAGIC (fh_r); SvGETMAGIC (fh_w);
    0          
    50          
    0          
286 7 100         if (SvOK (fh_r) || SvOK (fh_w))
    50          
    50          
    50          
    50          
    50          
287             {
288 1           int fd_r = s_fileno_croak (fh_r, 0);
289 1           int fd_w = s_fileno_croak (fh_w, 1);
290              
291 1           async->fh_r = newSVsv (fh_r);
292 1           async->fh_w = newSVsv (fh_w);
293 1           async->ep.fd [0] = fd_r;
294 1           async->ep.fd [1] = fd_w;
295 1           async->ep.len = 1;
296 1           async->fd_enable = 1;
297             }
298              
299 14           async->value = SvROK (pvalue)
300 1           ? SvREFCNT_inc_NN (SvRV (pvalue))
301 7 100         : NEWSV (0, 0);
302              
303 7           sv_setiv (async->value, 0);
304 7 50         SvIOK_only (async->value); /* just to be sure */
305 7           SvREADONLY_on (async->value);
306              
307 7           async->valuep = &(SvIVX (async->value));
308              
309 7           async->autodrain = 1;
310 7           async->cb = cv;
311 7           async->c_cb = c_cb;
312 7           async->c_arg = c_arg;
313 7 100         async->signum = SvOK (signl) ? s_signum_croak (signl) : 0;
    50          
    50          
314              
315 7 100         if (async->signum)
316             {
317 1 50         if (async->signum < 0)
318 0 0         croak ("Async::Interrupt::new got passed illegal signal name or number: %s", SvPV_nolen (signl));
319              
320 1           sig_async [async->signum] = async;
321 1           setsig (async->signum, async_sigsend);
322             }
323             }
324              
325             void
326             signal_hysteresis (async_t *async, int enable)
327             CODE:
328 1           async->hysteresis = enable;
329              
330             void
331             signal_func (async_t *async)
332             PPCODE:
333 1 50         EXTEND (SP, 2);
334 1           PUSHs (sv_2mortal (newSViv (PTR2IV (async_signal))));
335 1           PUSHs (sv_2mortal (newSViv (PTR2IV (async))));
336              
337             void
338             scope_block_func (SV *self)
339             PPCODE:
340 0 0         EXTEND (SP, 2);
341 0           PUSHs (sv_2mortal (newSViv (PTR2IV (scope_block))));
342 0           PUSHs (sv_2mortal (newSViv (PTR2IV (SvRV (self)))));
343              
344             IV
345             c_var (async_t *async)
346             CODE:
347 1           RETVAL = PTR2IV (async->valuep);
348             OUTPUT:
349             RETVAL
350              
351             void
352             handle (async_t *async)
353             CODE:
354 0           handle_async (async);
355              
356             void
357             signal (async_t *async, int value = 1)
358             CODE:
359 13           async_signal (async, value);
360              
361             void
362             block (async_t *async)
363             CODE:
364 4           block (async);
365              
366             void
367             unblock (async_t *async)
368             CODE:
369 4           unblock (async);
370              
371             void
372             scope_block (SV *self)
373             CODE:
374 5           scope_block (SvRV (self));
375              
376             void
377             pipe_enable (async_t *async)
378             ALIAS:
379             pipe_enable = 1
380             pipe_disable = 0
381             CODE:
382 2           async->fd_enable = ix;
383              
384             int
385             pipe_fileno (async_t *async)
386             CODE:
387 4 100         if (!async->ep.len)
388             {
389             int res;
390              
391             /*block (async);*//*TODO*/
392 1           res = s_epipe_new (&async->ep);
393 1           async->fd_enable = 1;
394             /*unblock (async);*//*TODO*/
395              
396 1 50         if (res < 0)
397 0           croak ("Async::Interrupt: unable to initialize event pipe");
398             }
399              
400 4           RETVAL = async->ep.fd [0];
401             OUTPUT:
402             RETVAL
403              
404             int
405             pipe_autodrain (async_t *async, int enable = -1)
406             CODE:
407 0           RETVAL = async->autodrain;
408 0 0         if (enable >= 0)
409 0           async->autodrain = enable;
410             OUTPUT:
411             RETVAL
412              
413             void
414             pipe_drain (async_t *async)
415             CODE:
416 0 0         if (async->ep.len)
417 0           s_epipe_drain (&async->ep);
418              
419             void
420             post_fork (async_t *async)
421             CODE:
422 2 50         if (async->ep.len)
423             {
424             int res;
425              
426             /*block (async);*//*TODO*/
427 2           res = s_epipe_renew (&async->ep);
428             /*unblock (async);*//*TODO*/
429              
430 2 50         if (res < 0)
431 0           croak ("Async::Interrupt: unable to initialize event pipe after fork");
432             }
433              
434             void
435             DESTROY (SV *self)
436             CODE:
437             {
438             int i;
439 7           SV *async_sv = SvRV (self);
440 7           async_t *async = SvASYNC_nrv (async_sv);
441              
442 8 50         for (i = AvFILLp (asyncs); i >= 0; --i)
443 8 100         if (AvARRAY (asyncs)[i] == async_sv)
444             {
445 7           AvARRAY (asyncs)[i] = AvARRAY (asyncs)[AvFILLp (asyncs)];
446 7           av_pop (asyncs);
447 7           goto found;
448             }
449              
450 0 0         if (!PL_dirty)
451 0           warn ("Async::Interrupt::DESTROY could not find async object in list of asyncs, please report");
452              
453             found:
454              
455 7 100         if (async->signum)
456 1           setsig (async->signum, SIG_DFL);
457              
458 7 100         if (!async->fh_r && async->ep.len)
    100          
459 1           s_epipe_destroy (&async->ep);
460              
461 7           SvREFCNT_dec (async->fh_r);
462 7           SvREFCNT_dec (async->fh_w);
463 7           SvREFCNT_dec (async->cb);
464 7           SvREFCNT_dec (async->value);
465              
466 7           Safefree (async);
467             }
468              
469             SV *
470             sig2num (SV *signame_or_number)
471             ALIAS:
472             sig2num = 0
473             sig2name = 1
474             PROTOTYPE: $
475             CODE:
476             {
477 0           int signum = s_signum (signame_or_number);
478              
479 0 0         if (signum < 0)
480 0           RETVAL = &PL_sv_undef;
481 0 0         else if (ix)
482 0           RETVAL = newSVpv (PL_sig_name [signum], 0);
483             else
484 0           RETVAL = newSViv (signum);
485             }
486             OUTPUT:
487             RETVAL
488              
489             MODULE = Async::Interrupt PACKAGE = Async::Interrupt::EventPipe PREFIX = s_epipe_
490              
491             void
492             new (const char *klass)
493             PPCODE:
494             {
495             s_epipe *epp;
496              
497 1           Newz (0, epp, 1, s_epipe);
498 1 50         XPUSHs (sv_setref_iv (sv_newmortal (), klass, PTR2IV (epp)));
499              
500 1 50         if (s_epipe_new (epp) < 0)
501 0           croak ("Async::Interrupt::EventPipe: unable to create new event pipe");
502             }
503              
504             void
505             filenos (s_epipe *epp)
506             PPCODE:
507 0 0         EXTEND (SP, 2);
508 0           PUSHs (sv_2mortal (newSViv (epp->fd [0])));
509 0           PUSHs (sv_2mortal (newSViv (epp->fd [1])));
510              
511             int
512             fileno (s_epipe *epp)
513             ALIAS:
514             fileno = 0
515             fileno_r = 0
516             fileno_w = 1
517             CODE:
518 1           RETVAL = epp->fd [ix];
519             OUTPUT:
520             RETVAL
521              
522             int
523             type (s_epipe *epp)
524             CODE:
525 0           RETVAL = epp->len;
526             OUTPUT:
527             RETVAL
528              
529             void
530             s_epipe_signal (s_epipe *epp)
531              
532             void
533             s_epipe_drain (s_epipe *epp)
534              
535             void
536             signal_func (s_epipe *epp)
537             ALIAS:
538             drain_func = 1
539             PPCODE:
540 2 50         EXTEND (SP, 2);
541 2 100         PUSHs (sv_2mortal (newSViv (PTR2IV (ix ? s_epipe_drain : s_epipe_signal))));
542 2           PUSHs (sv_2mortal (newSViv (PTR2IV (epp))));
543              
544             void
545             s_epipe_wait (s_epipe *epp)
546              
547             void
548             s_epipe_renew (s_epipe *epp)
549              
550             void
551             DESTROY (s_epipe *epp)
552             CODE:
553 1           s_epipe_destroy (epp);
554