File Coverage

SocketAlarm.xs
Criterion Covered Total %
statement 160 184 86.9
branch 68 116 58.6
condition n/a
subroutine n/a
pod n/a
total 228 300 76.0


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4             #define NEED_mg_findext
5             #include "ppport.h"
6              
7             #include
8             #include
9             #include
10             #include
11             #include
12             #include
13             #include
14             #include
15             #include
16             #include
17             #include
18             #include
19             #include
20             #include
21             #include
22             #include
23              
24             #define AUTOCREATE 1
25             #define OR_DIE 2
26              
27             struct socketalarm;
28              
29             #include "SocketAlarm_util.h"
30             #include "SocketAlarm_action.h"
31             #include "pollfd_rbhash.h"
32             #include "SocketAlarm_watcher.h"
33              
34             #define EVENT_SHUT 0x01
35             #define EVENT_EOF 0x02
36             #define EVENT_CLOSE 0x04
37             #define EVENT_IN 0x08
38             #define EVENT_PRI 0x10
39              
40             #ifdef POLLRDHUP
41             #define EVENT_DEFAULTS EVENT_SHUT
42             #else
43             #define EVENT_DEFAULTS (EVENT_SHUT|EVENT_EOF)
44             #endif
45              
46             struct socketalarm {
47             int list_ofs; // position within watch_list, initially -1 until activated
48             int watch_fd;
49             dev_t watch_fd_dev;
50             ino_t watch_fd_ino;
51             int event_mask;
52             int action_count;
53             SV *owner;
54             AV *actions_av; // lazy-built
55             bool unwaitable; //
56             int cur_action; // used during execution
57             struct timespec wake_ts; //
58             struct action actions[];
59             };
60              
61             static void socketalarm_exec_actions(struct socketalarm *sa);
62              
63             #include "SocketAlarm_util.c"
64             #include "SocketAlarm_action.c"
65             #include "SocketAlarm_watcher.c"
66              
67             struct socketalarm *
68 26           socketalarm_new(int watch_fd, struct stat *statbuf, int event_mask, SV **action_spec, size_t spec_count) {
69 26           size_t n_actions= 0, aux_len= 0, len_before_aux;
70 26           struct socketalarm *self= NULL;
71              
72 26           parse_actions(action_spec, spec_count, NULL, &n_actions, NULL, &aux_len);
73             // buffer needs aligned to pointer, which sizeof(struct action) is not guaranteed to be
74 26           len_before_aux= sizeof(struct socketalarm) + n_actions * sizeof(struct action);
75 26           len_before_aux += sizeof(void*)-1;
76 26           len_before_aux &= ~(sizeof(void*)-1);
77 26           self= (struct socketalarm *) safecalloc(1, len_before_aux + aux_len);
78             // second call should succeed, because we gave it back it's own requested buffer sizes.
79             // could fail if user did something evil like a tied scalar that changes length...
80 26 50         if (!parse_actions(action_spec, spec_count, self->actions, &n_actions, ((char*)self) + len_before_aux, &aux_len))
81 0           croak("BUG: buffers not large enough for parse_actions");
82             // If user requests EVENT_SHUT and not available on the platform, warn and downgrade to EVENT_EOF
83             if (!(EVENT_DEFAULTS & EVENT_SHUT) && (event_mask & EVENT_SHUT) && !(event_mask & EVENT_EOF)) {
84             SV *warned= get_sv("IO::SocketAlarm::_warned_EVENT_SHUT_unavail", GV_ADD);
85             if (!SvTRUE(warned)) {
86             warn("IO::SocketAlarm EVENT_SHUT is not available on this platform, using EVENT_EOF instead");
87             sv_setsv(warned, &PL_sv_yes);
88             }
89             event_mask |= EVENT_EOF;
90             }
91 26           self->watch_fd= watch_fd;
92 26           self->watch_fd_dev= statbuf->st_dev;
93 26           self->watch_fd_ino= statbuf->st_ino;
94 26           self->event_mask= event_mask;
95 26           self->actions_av= NULL;
96 26           self->action_count= n_actions;
97 26           self->list_ofs= -1; // initially not in the watch list
98 26           self->owner= NULL;
99 26           return self;
100             }
101              
102 57           void socketalarm_exec_actions(struct socketalarm *self) {
103 57           bool resume= self->cur_action >= 0;
104 57           struct timespec now_ts= { 0, -1 };
105 57 100         if (!resume)
106 12           self->cur_action= 0;
107 69 100         while (self->cur_action < self->action_count) {
108 12 50         if (!execute_action(self->actions + self->cur_action, resume, &now_ts, self))
109 0           break;
110 12           resume= false;
111 12           ++self->cur_action;
112             }
113 57           }
114              
115 14           static void socketalarm__build_actions(struct socketalarm *self) {
116             Size_t i;
117 14 50         if (!self->actions_av) {
118 14           self->actions_av= newAV();
119 14           av_extend(self->actions_av, self->action_count-1);
120 32 100         for (i= 0; i < self->action_count; i++) {
121 18           AV *act_spec= newAV();
122 18           inflate_action(self->actions+i, act_spec);
123 18           SvREADONLY_on((SV*) act_spec);
124 18           av_push(self->actions_av, newRV_noinc((SV*) act_spec));
125             }
126 14           SvREADONLY_on((SV*) self->actions_av);
127             }
128 14           }
129              
130 26           void socketalarm_free(struct socketalarm *sa) {
131             // Must remove the socketalarm from the active list, if present
132 26           watch_list_remove(sa);
133             // Release reference to lazy-built action AV
134 26 100         if (sa->actions_av)
135 14           SvREFCNT_dec((SV*) sa->actions_av);
136             // was allocated as one chunk
137 26           Safefree(sa);
138 26           }
139              
140             /* Return an SV array of an AV.
141             * Returns NULL if it wasn't an AV or arrayref.
142             */
143 13           static SV** unwrap_array(SV *array, SSize_t *len_out) {
144             AV *av;
145             SV **vec;
146             SSize_t n;
147 13 50         if (array && SvTYPE(array) == SVt_PVAV)
    50          
148 0           av= (AV*) array;
149 13 50         else if (array && SvROK(array) && SvTYPE(SvRV(array)) == SVt_PVAV)
    50          
    50          
150 13           av= (AV*) SvRV(array);
151             else
152 0           return NULL;
153 13           n= av_count(av);
154 13           vec= AvARRAY(av);
155             /* tied arrays and non-allocated empty arrays return NULL */
156 13 100         if (!vec) {
157 1 50         if (n == 0) /* don't return a NULL for an empty array, but doesn't need to be a real pointer */
158 1           vec= (SV**) 8;
159             else {
160             /* in case of a tied array, extract the elements into a temporary buffer */
161             SSize_t i;
162 0 0         Newx(vec, n, SV*);
163 0           SAVEFREEPV(vec);
164 0 0         for (i= 0; i < n; i++) {
165 0           SV **el= av_fetch(av, i, 0);
166 0 0         vec[i]= el? *el : NULL;
167             }
168             }
169             }
170 13 50         if (len_out) *len_out= n;
171 13           return vec;
172             }
173              
174             /*------------------------------------------------------------------------------------
175             * Definitions of Perl MAGIC that attach C structs to Perl SVs
176             */
177              
178             // destructor for Watch objects
179 26           static int socketalarm_magic_free(pTHX_ SV* sv, MAGIC* mg) {
180 26 50         if (mg->mg_ptr) {
181 26           socketalarm_free((struct socketalarm*) mg->mg_ptr);
182 26           mg->mg_ptr= NULL;
183             }
184 26           return 0; // ignored anyway
185             }
186             #ifdef USE_ITHREADS
187             static int socketalarm_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
188             croak("This object cannot be shared between threads");
189             return 0;
190             };
191             #else
192             #define socketalarm_magic_dup 0
193             #endif
194              
195             // magic table for Watch objects
196             static MGVTBL socketalarm_magic_vt= {
197             0, /* get */
198             0, /* write */
199             0, /* length */
200             0, /* clear */
201             socketalarm_magic_free,
202             0, /* copy */
203             socketalarm_magic_dup
204             #ifdef MGf_LOCAL
205             ,0
206             #endif
207             };
208              
209             // Return the socketalarm that was attached to a perl Watch object via MAGIC.
210             // The 'obj' should be a reference to a blessed magical SV.
211             static struct socketalarm*
212 76           get_magic_socketalarm(SV *obj, int flags) {
213             SV *sv;
214             MAGIC* magic;
215              
216 76 50         if (!sv_isobject(obj)) {
217 0 0         if (flags & OR_DIE)
218 0           croak("Not an object");
219 0           return NULL;
220             }
221 76           sv= SvRV(obj);
222 76 100         if (SvMAGICAL(sv)) {
223             /* Iterate magic attached to this scalar, looking for one with our vtable */
224 62 50         if ((magic= mg_findext(sv, PERL_MAGIC_ext, &socketalarm_magic_vt)))
225             /* If found, the mg_ptr points to the fields structure. */
226 62           return (struct socketalarm*) magic->mg_ptr;
227             }
228 14 50         if (flags & OR_DIE)
229 0           croak("Object lacks 'struct TreeRBXS_item' magic");
230 14           return NULL;
231             }
232              
233 26           static void attach_magic_socketalarm(SV *obj_inner_sv, struct socketalarm *sa) {
234             MAGIC *magic;
235 26 50         if (sa->owner)
236 0           croak("BUG: already attached to perl object");
237 26           sa->owner= obj_inner_sv;
238 26           magic= sv_magicext((SV*) sa->owner, NULL, PERL_MAGIC_ext, &socketalarm_magic_vt, (const char*) sa, 0);
239             #ifdef USE_ITHREADS
240             magic->mg_flags |= MGf_DUP;
241             #else
242             (void)magic; // suppress 'unused' warning
243             #endif
244 26           }
245              
246             // Return existing Watch object, or create a new one.
247             // Returned SV has a non-mortal refcount, which is what the typemap
248             // wants for returning a "struct socketalarm*" to perl-land
249 12           static SV* wrap_socketalarm(struct socketalarm *sa) {
250             SV *obj;
251             HV *hv;
252             // Since this is used in typemap, handle NULL gracefully
253 12 50         if (!sa)
254 0           return &PL_sv_undef;
255             // If there is already a node object, return a new reference to it.
256 12 50         if (sa->owner)
257 0           return newRV_inc((SV*) sa->owner);
258             // else create a node object
259 12           hv= newHV();
260 12           obj= newRV_noinc((SV*) hv);
261 12           sv_bless(obj, gv_stashpv("IO::SocketAlarm", GV_ADD));
262 12           attach_magic_socketalarm((SV*) hv, sa);
263 12           return obj;
264             }
265              
266             #define EXPORT_ENUM(x) newCONSTSUB(stash, #x, new_enum_dualvar(aTHX_ x, newSVpvs_share(#x)))
267 72           static SV * new_enum_dualvar(pTHX_ IV ival, SV *name) {
268 72 50         SvUPGRADE(name, SVt_PVNV);
269 72           SvIV_set(name, ival);
270 72           SvIOK_on(name);
271 72           SvREADONLY_on(name);
272 72           return name;
273             }
274              
275             /*------------------------------------------------------------------------------------
276             * Perl API
277             */
278              
279             MODULE = IO::SocketAlarm PACKAGE = IO::SocketAlarm
280              
281             void
282             _init_socketalarm(self, sock_sv, eventmask_sv, actions_sv)
283             SV *self
284             SV *sock_sv
285             SV *eventmask_sv
286             SV *actions_sv
287             INIT:
288 14           int sock_fd= fileno_from_sv(sock_sv);
289 14           int eventmask= EVENT_DEFAULTS;
290             struct stat statbuf;
291             struct socketalarm *sa;
292 14           SV **action_list= NULL;
293 14           SSize_t n_actions= 0;
294             PPCODE:
295 14 50         if (!sv_isobject(self))
296 0           croak("Not an object");
297 14 50         if ((sa= get_magic_socketalarm(self, 0)))
298 0           croak("Already initialized");
299 14 50         if (!(sock_fd >= 0 && fstat(sock_fd, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode)))
    50          
    50          
300 0           croak("Not an open socket");
301 14 50         if (eventmask_sv && SvOK(eventmask_sv))
    50          
302 14           eventmask= SvIV(eventmask_sv);
303 14 50         if (actions_sv && SvOK(actions_sv)) {
    100          
304 13           action_list= unwrap_array(actions_sv, &n_actions);
305 13 50         if (!action_list)
306 0           croak("Actions must be an arrayref (or undefined)");
307             }
308 14           sa= socketalarm_new(sock_fd, &statbuf, eventmask, action_list, n_actions);
309 14           attach_magic_socketalarm(SvRV(self), sa);
310 14           XSRETURN(1); // return $self
311              
312             int
313             socket(alarm)
314             struct socketalarm *alarm
315             CODE:
316 14 50         RETVAL= alarm->watch_fd;
317             OUTPUT:
318             RETVAL
319              
320             int
321             events(alarm)
322             struct socketalarm *alarm
323             CODE:
324 14 50         RETVAL= alarm->event_mask;
325             OUTPUT:
326             RETVAL
327              
328             void
329             actions(alarm)
330             struct socketalarm *alarm
331             PPCODE:
332 14           if (!alarm->actions_av);
333 14           socketalarm__build_actions(alarm);
334 14           ST(0)= sv_2mortal(newRV_inc((SV*) alarm->actions_av));
335 14           XSRETURN(1);
336              
337             int
338             action_count(alarm)
339             struct socketalarm *alarm
340             CODE:
341 1 50         RETVAL= alarm->action_count;
342             OUTPUT:
343             RETVAL
344              
345             int
346             cur_action(alarm)
347             struct socketalarm *alarm
348             CODE:
349 3           watch_list_item_get_status(alarm, &RETVAL);
350             OUTPUT:
351             RETVAL
352              
353             bool
354             start(alarm)
355             struct socketalarm *alarm
356             CODE:
357 0           RETVAL= watch_list_add(alarm);
358             OUTPUT:
359             RETVAL
360              
361             bool
362             cancel(alarm)
363             struct socketalarm *alarm
364             CODE:
365 2           RETVAL= watch_list_remove(alarm);
366             OUTPUT:
367             RETVAL
368              
369             SV*
370             stringify(alarm)
371             struct socketalarm *alarm
372             INIT:
373 14           SV *out= sv_2mortal(newSVpvn("",0));
374             Size_t i;
375             CODE:
376 14           sv_catpvf(out, "watch fd: %d\n", alarm->watch_fd);
377 14 50         sv_catpvf(out, "event mask:%s%s\n",
    50          
378             alarm->event_mask & EVENT_SHUT? " SHUT":"",
379             alarm->event_mask & EVENT_CLOSE? " CLOSE":""
380             );
381 14           sv_catpv(out, "actions:\n");
382 32 100         for (i= 0; i < alarm->action_count; i++) {
383             char buf[256];
384 18           snprint_action(buf, sizeof(buf), alarm->actions+i);
385 18           sv_catpvf(out, "%4d: %s\n", (int)i, buf);
386             }
387 14           SvREFCNT_inc(out);
388 14           RETVAL= out;
389             OUTPUT:
390             RETVAL
391              
392             void
393             _terminate_all()
394             PPCODE:
395 6           shutdown_watch_thread();
396              
397             MODULE = IO::SocketAlarm PACKAGE = IO::SocketAlarm::Util
398              
399             struct socketalarm *
400             socketalarm(sock_sv, ...)
401             SV *sock_sv
402             INIT:
403 12           int sock_fd= fileno_from_sv(sock_sv);
404 12           int eventmask= EVENT_DEFAULTS;
405 12           int action_ofs= 1;
406             struct stat statbuf;
407             CODE:
408 12 50         if (!(sock_fd >= 0 && fstat(sock_fd, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode)))
    50          
    50          
409 0           croak("Not an open socket");
410 12 100         if (items > 1) {
411             // must either be a scalar, a scalar followed by actions specs, or action specs
412 9 50         if (SvOK(ST(1)) && looks_like_number(ST(1))) {
    50          
413 0           eventmask= SvIV(ST(1));
414 0           action_ofs++;
415             }
416             }
417 12           RETVAL= socketalarm_new(sock_fd, &statbuf, eventmask, &(ST(action_ofs)), items - action_ofs);
418 12           watch_list_add(RETVAL);
419             OUTPUT:
420             RETVAL
421              
422             bool
423             is_socket(fd_sv)
424             SV *fd_sv
425             INIT:
426 5           int fd= fileno_from_sv(fd_sv);
427             struct stat statbuf;
428             CODE:
429 5 100         RETVAL= fd >= 0 && fstat(fd, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode);
    50          
    100          
    100          
430             OUTPUT:
431             RETVAL
432              
433             SV *
434             get_fd_table_str(max_fd=1024)
435             int max_fd
436             INIT:
437 1           SV *out= newSVpvn("",0);
438 1           size_t avail= 0, needed= 1023;
439             CODE:
440             // FD status could change between calls, changing the length requirement, so loop.
441             // 'avail' count includes the NUL byte, and 'needed' does not.
442 2 100         while (avail <= needed) {
443 1           sv_grow(out, needed+1);
444 1           avail= needed+1;
445 1           needed= snprint_fd_table(SvPVX(out), avail, max_fd);
446             }
447 1           SvCUR_set(out, needed);
448 1           RETVAL= out;
449             OUTPUT:
450             RETVAL
451              
452             # For unit test purposes only, export _poll that polls on a single file
453             # descriptor to verify the statuses for sockets in various states.
454              
455             void
456             _poll(fd, events, timeout)
457             int fd
458             SV *events;
459             int timeout;
460             INIT:
461             int ret;
462             struct pollfd pollbuf;
463             PPCODE:
464 7           pollbuf.fd= fd;
465 7           pollbuf.events= SvIV(events);
466 7           pollbuf.revents= 0;
467 7           ret= poll(&pollbuf, 1, timeout);
468 7 50         EXTEND(SP, 2);
469 7           PUSHs(sv_2mortal(newSViv(ret)));
470 7 50         PUSHs(sv_2mortal(newSViv(ret < 0? errno : ret > 0? pollbuf.revents : 0)));
    100          
471              
472             #-----------------------------------------------------------------------------
473             # Constants
474             #
475              
476             BOOT:
477 6           HV* stash= gv_stashpvn("IO::SocketAlarm::Util", 21, GV_ADD);
478 6           EXPORT_ENUM(EVENT_SHUT);
479 6           EXPORT_ENUM(EVENT_EOF);
480 6           EXPORT_ENUM(EVENT_IN);
481 6           EXPORT_ENUM(EVENT_PRI);
482 6           EXPORT_ENUM(EVENT_CLOSE);
483 6           EXPORT_ENUM(POLLIN);
484 6           EXPORT_ENUM(POLLOUT);
485 6           EXPORT_ENUM(POLLPRI);
486 6           EXPORT_ENUM(POLLERR);
487 6           EXPORT_ENUM(POLLHUP);
488             #ifdef POLLRDHUP
489 6           EXPORT_ENUM(POLLRDHUP);
490             #endif
491 6           EXPORT_ENUM(POLLNVAL);
492              
493             PROTOTYPES: DISABLE