File Coverage

AtFork.xs
Criterion Covered Total %
statement 104 108 96.3
branch 34 88 38.6
condition n/a
subroutine n/a
pod n/a
total 138 196 70.4


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include "ppport.h"
7              
8             #include
9              
10             #define MY_CXT_KEY "POSIX::AtFork::_guts" XS_VERSION
11             typedef struct {
12             AV* prepare_list;
13             AV* parent_list;
14             AV* child_list;
15             } my_cxt_t;
16             START_MY_CXT
17              
18             typedef struct {
19             PERL_SI *curstackinfo;
20             AV *curstack;
21             AV *mainstack;
22             SV **stack_base;
23             SV **stack_sp;
24             SV **stack_max;
25             } stack_backup_t;
26              
27             static void
28 20           paf_save_stacks(pTHX_ stack_backup_t* bk) {
29 20           bk->curstackinfo = PL_curstackinfo;
30 20           bk->curstack = PL_curstack;
31 20           bk->mainstack = PL_mainstack;
32              
33 20           bk->stack_base = PL_stack_base;
34 20           bk->stack_sp = PL_stack_sp;
35 20           bk->stack_max = PL_stack_max;
36 20           }
37              
38             static void
39 20           paf_restore_stacks(pTHX_ stack_backup_t* bk) {
40 20           PL_curstackinfo = bk->curstackinfo;
41 20           PL_curstack = bk->curstack;
42 20           PL_mainstack = bk->mainstack;
43              
44 20           PL_stack_base = bk->stack_base;
45 20           PL_stack_sp = bk->stack_sp;
46 20           PL_stack_max = bk->stack_max;
47 20           }
48              
49             static void
50 20           paf_init_stacks(pTHX) {
51 20           PL_curstackinfo = new_stackinfo(32, 4);
52 20           PL_curstackinfo->si_type = PERLSI_MAIN;
53 20           PL_curstack = PL_curstackinfo->si_stack;
54 20           PL_mainstack = PL_curstack;
55              
56 20           PL_stack_base = AvARRAY(PL_curstack);
57 20           PL_stack_sp = PL_stack_base;
58 20           PL_stack_max = PL_stack_base + AvMAX(PL_curstack);
59 20           }
60              
61             static void
62 20           paf_destruct_stacks(pTHX) {
63 22 100         while (PL_curstackinfo->si_next)
64 2           PL_curstackinfo = PL_curstackinfo->si_next;
65              
66 42 100         while (PL_curstackinfo) {
67 22           PERL_SI *p = PL_curstackinfo->si_prev;
68              
69 22 50         if (!PL_dirty)
70 22           SvREFCNT_dec (PL_curstackinfo->si_stack);
71              
72 22           Safefree (PL_curstackinfo->si_cxstack);
73 22           Safefree (PL_curstackinfo);
74 22           PL_curstackinfo = p;
75             }
76 20           }
77              
78             static void
79 20           paf_call_list(pTHX_ AV* const av) {
80 20 50         const char* const opname = PL_op ? OP_NAME(PL_op) : "(unknown)";
    50          
81             SV* opnamesv;
82 20           I32 const len = av_len(av) + 1;
83             I32 i;
84              
85             stack_backup_t bk;
86 20           paf_save_stacks(aTHX_ &bk);
87 20           paf_init_stacks(aTHX);
88 20           ENTER;
89 20           SAVETMPS;
90 20           opnamesv = sv_2mortal(newSVpv(opname, 0));
91 48 100         for(i = 0; i < len; i++) {
92 28           dSP;
93 28 50         PUSHMARK(SP);
94 28 50         XPUSHs(opnamesv);
95 28           PUTBACK;
96 28           call_sv(*av_fetch(av, i, TRUE), G_VOID | G_EVAL);
97 28 50         if(SvTRUEx(ERRSV)) {
    50          
    50          
    0          
    50          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
98 0           warn("Callback for pthread_atfork() died (ignored): %"SVf,
99 0 0         ERRSV);
100             }
101             }
102 20 50         FREETMPS;
103 20           LEAVE;
104 20           paf_destruct_stacks(aTHX);
105 20           paf_restore_stacks(aTHX_ &bk);
106 20           }
107              
108             static void
109 10           paf_prepare(void) {
110             dTHX;
111             dMY_CXT;
112 10           paf_call_list(aTHX_ MY_CXT.prepare_list);
113 10           }
114              
115             static void
116 6           paf_parent(void) {
117             dTHX;
118             dMY_CXT;
119 6           paf_call_list(aTHX_ MY_CXT.parent_list);
120 6           }
121              
122             static void
123 4           paf_child(void) {
124             dTHX;
125             dMY_CXT;
126             SV* pidsv;
127              
128             /* fix up pid */
129 4           pidsv = get_sv("$", GV_ADD);
130 4           SvREADONLY_off(pidsv);
131 4           sv_setiv(pidsv, (IV)PerlProc_getpid());
132 4           SvREADONLY_on(pidsv);
133              
134 4           paf_call_list(aTHX_ MY_CXT.child_list);
135 4           }
136              
137             static void
138 51           paf_register_cb(pTHX_ AV* const list, SV* const cb) {
139 51 50         SvGETMAGIC(cb);
    0          
140 51 50         if(SvOK(cb)) {
    0          
    0          
141 51 50         if(SvROK(cb) && SvTYPE(SvRV(cb)) == SVt_PVCV) {
    50          
142 51           av_push(list, newSVsv(cb));
143             }
144             else {
145 0           croak("Callback for atfork must be a CODE reference");
146             }
147             }
148 51           }
149              
150             static void
151 6           paf_delete(pTHX_ AV* const av, SV* const cb) {
152 6           I32 len = av_len(av) + 1;
153             I32 i;
154              
155 6 50         if(!(SvROK(cb) && SvTYPE(SvRV(cb)) == SVt_PVCV)) {
    50          
156 0           croak("Not a CODE reference to delete callbacks");
157             }
158              
159 24 100         for(i = 0; i < len; i++) {
160 18           SV* const sv = *av_fetch(av, i, TRUE);
161 18 50         if(!SvROK(sv)){ sv_dump(sv); }
162             assert(SvROK(sv));
163              
164 18 100         if(SvRV(sv) == SvRV(cb)) {
165 12           size_t const tail = len - i - 1;
166 12 50         Move(AvARRAY(av) + i + 1, AvARRAY(av) + i, tail, SV*);
167 12           AvFILLp(av)--;
168 12           len--;
169 12           SvREFCNT_dec(sv);
170             }
171             }
172 6           }
173              
174             static void
175 9           paf_initialize(pTHX_ pMY_CXT_ bool const cloning PERL_UNUSED_DECL) {
176 9           pthread_atfork(paf_prepare, paf_parent, paf_child);
177              
178 9           MY_CXT.prepare_list = newAV();
179 9           MY_CXT.parent_list = newAV();
180 9           MY_CXT.child_list = newAV();
181 9           }
182              
183             MODULE = POSIX::AtFork PACKAGE = POSIX::AtFork
184              
185             PROTOTYPES: DISABLE
186              
187             BOOT:
188             {
189             MY_CXT_INIT;
190 9           paf_initialize(aTHX_ aMY_CXT_ FALSE);
191             }
192              
193             #ifdef USE_ITHREADS
194              
195             void
196             CLONE(...)
197             CODE:
198             {
199             MY_CXT_CLONE;
200             paf_initialize(aTHX_ aMY_CXT_ TRUE);
201             PERL_UNUSED_VAR(items);
202             }
203              
204             #endif
205              
206             void
207             pthread_atfork(SV* prepare, SV* parent, SV* child)
208             CODE:
209             {
210             dMY_CXT;
211 5           paf_register_cb(aTHX_ MY_CXT.prepare_list, prepare);
212 5           paf_register_cb(aTHX_ MY_CXT.parent_list, parent);
213 5           paf_register_cb(aTHX_ MY_CXT.child_list, child);
214             }
215              
216              
217             void
218             add_to_prepare(klass, SV* cb)
219             CODE:
220             {
221             dMY_CXT;
222 12           paf_register_cb(aTHX_ MY_CXT.prepare_list, cb);
223             }
224              
225              
226             void
227             add_to_parent(klass, SV* cb)
228             CODE:
229             {
230             dMY_CXT;
231 12           paf_register_cb(aTHX_ MY_CXT.parent_list, cb);
232             }
233              
234              
235             void
236             add_to_child(klass, SV* cb)
237             CODE:
238             {
239             dMY_CXT;
240 12           paf_register_cb(aTHX_ MY_CXT.child_list, cb);
241             }
242              
243             void
244             delete_from_prepare(klass, SV* cb)
245             CODE:
246             {
247             dMY_CXT;
248 2           paf_delete(aTHX_ MY_CXT.prepare_list, cb);
249             }
250              
251             void
252             delete_from_parent(klass, SV* cb)
253             CODE:
254             {
255             dMY_CXT;
256 2           paf_delete(aTHX_ MY_CXT.parent_list, cb);
257             }
258              
259             void
260             delete_from_child(klass, SV* cb)
261             CODE:
262             {
263             dMY_CXT;
264 2           paf_delete(aTHX_ MY_CXT.child_list, cb);
265             }
266