File Coverage

lib/Future/XS.xs
Criterion Covered Total %
statement 1 149 0.6
branch 0 436 0.0
condition n/a
subroutine n/a
pod n/a
total 1 585 0.1


line stmt bran cond sub pod time code
1             /* You may distribute under the terms of either the GNU General Public License
2             * or the Artistic License (the same terms as Perl itself)
3             *
4             * (C) Paul Evans, 2022 -- leonerd@leonerd.org.uk
5             */
6             #define PERL_NO_GET_CONTEXT
7              
8             #include "EXTERN.h"
9             #include "perl.h"
10             #include "XSUB.h"
11              
12             #include "future.h"
13              
14             #include "perl-backcompat.c.inc"
15              
16             #include "av-utils.c.inc"
17             #include "croak_from_caller.c.inc"
18              
19             #define warn_void_context(func) S_warn_void_context(aTHX_ func)
20 0           static void S_warn_void_context(pTHX_ const char *func)
21             {
22 0 0         if(GIMME_V == G_VOID)
23 0           warn("Calling ->%s in void context", func);
24 0           }
25              
26             #define CHECK_INSTANCE(self) \
27             if(!SvROK(self) || !SvOBJECT(SvRV(self)) || \
28             !sv_derived_from(self, "Future::XS")) { \
29             GV *gv = CvGV(cv); HV *stash = GvSTASH(gv); \
30             croak("Expected a Future instance for %s::%s", \
31             HvNAME(stash), GvNAME(gv)); \
32             }
33              
34             MODULE = Future::XS PACKAGE = Future::XS
35              
36             SV *
37             new(SV *proto)
38             CODE:
39 0 0         if(SvROK(proto) && SvOBJECT(SvRV(proto))) {
    0          
40 0           HV *protostash = SvSTASH(SvRV(proto));
41 0 0         RETVAL = future_new(HvNAME(protostash));
    0          
    0          
    0          
    0          
    0          
42             }
43             else
44 0           RETVAL = future_new(SvPV_nolen(proto));
45             OUTPUT:
46             RETVAL
47              
48             void
49             DESTROY(SV *self)
50             CODE:
51 0           future_destroy(self);
52              
53             bool
54             is_ready(SV *self)
55             CODE:
56 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
57 0           RETVAL = future_is_ready(self);
58             OUTPUT:
59             RETVAL
60              
61             bool
62             is_done(SV *self)
63             CODE:
64 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
65 0           RETVAL = future_is_done(self);
66             OUTPUT:
67             RETVAL
68              
69             bool
70             is_failed(SV *self)
71             CODE:
72 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
73 0           RETVAL = future_is_failed(self);
74             OUTPUT:
75             RETVAL
76              
77             bool
78             is_cancelled(SV *self)
79             CODE:
80 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
81 0           RETVAL = future_is_cancelled(self);
82             OUTPUT:
83             RETVAL
84              
85             char *
86             state(SV *self)
87             CODE:
88 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
89             // TODO: We can do this more efficiently sometime
90 0 0         if(!future_is_ready(self))
91 0           RETVAL = "pending";
92 0 0         else if(future_is_failed(self))
93 0           RETVAL = "failed";
94 0 0         else if(future_is_cancelled(self))
95 0           RETVAL = "cancelled";
96             else
97 0           RETVAL = "done";
98             OUTPUT:
99             RETVAL
100              
101             SV *
102             done(SV *self, ...)
103             CODE:
104 0 0         if(sv_is_future(self))
105 0           RETVAL = SvREFCNT_inc(ST(0));
106             else
107 0           RETVAL = future_new(SvPV_nolen(ST(0)));
108              
109 0           future_donev(RETVAL, &ST(1), items - 1);
110             OUTPUT:
111             RETVAL
112              
113             SV *
114             fail(SV *self, ...)
115             ALIAS:
116             fail = 0
117             die = 1
118             CODE:
119 0           SV *exception = ST(1);
120              
121 0 0         if(ix == 1 && /* ->die */
122 0 0         !SvROK(exception) && SvPV_nolen(exception)[SvCUR(exception)-1] != '\n') {
    0          
123 0           ST(1) = exception = newSVsv(exception);
124 0 0         sv_catpvf(exception, " at %s line %d\n", CopFILE(PL_curcop), CopLINE(PL_curcop));
125             }
126              
127             // TODO: mess about with Future::Exception
128              
129 0 0         if(sv_is_future(self))
130 0           RETVAL = SvREFCNT_inc(ST(0));
131             else
132 0           RETVAL = future_new(SvPV_nolen(ST(0)));
133              
134 0           future_failv(RETVAL, &ST(1), items - 1);
135             OUTPUT:
136             RETVAL
137              
138             SV *
139             on_cancel(SV *self, SV *code)
140             CODE:
141 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
142 0           RETVAL = newSVsv(self);
143 0           future_on_cancel(self, code);
144             OUTPUT:
145             RETVAL
146              
147             SV *
148             on_ready(SV *self, SV *code)
149             CODE:
150 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
151             /* Need to copy the return value first in case on_ready destroys it
152             * RT145168 */
153 0           RETVAL = newSVsv(self);
154 0           future_on_ready(self, code);
155             OUTPUT:
156             RETVAL
157              
158             SV *
159             await(SV *self)
160             CODE:
161 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
162 0 0         if(future_is_ready(self)) {
163 0           RETVAL = newSVsv(ST(0));
164 0           XSRETURN(1);
165             }
166 0           croak_from_caller("%" SVf " is not yet complete and does not provide an ->await method",
167             SVfARG(self));
168             OUTPUT:
169             RETVAL
170              
171             void
172             result(SV *self)
173             ALIAS:
174             result = FALSE
175             get = TRUE
176             PPCODE:
177 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
178             /* This PUTBACK + SPAGAIN pair is required in case future_get_result_av()
179             * causes the arguments stack to be reällocated. It works fine on perls
180             * 5.24+ but causes older perls to crash. For now we just depend on 5.24
181             * https://rt.cpan.org/Ticket/Display.html?id=145597
182             */
183 0           PUTBACK;
184 0           AV *result = future_get_result_av(self, ix);
185 0           SPAGAIN;
186 0 0         if(GIMME_V == G_LIST) {
187 0 0         XPUSHs_from_AV(result);
    0          
    0          
    0          
    0          
    0          
    0          
188 0           XSRETURN(av_count(result));
189             }
190             else {
191 0 0         if(av_count(result))
192 0 0         XPUSHs(AvARRAY(result)[0]);
193             else
194 0 0         XPUSHs(&PL_sv_undef);
195 0           XSRETURN(1);
196             }
197              
198             SV *
199             on_done(SV *self, SV *code)
200             CODE:
201 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
202 0           RETVAL = newSVsv(self);
203 0           future_on_done(self, code);
204             OUTPUT:
205             RETVAL
206              
207             void
208             failure(SV *self)
209             PPCODE:
210 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
211 0           PUTBACK;
212 0           AV *failure = future_get_failure_av(self);
213 0           SPAGAIN;
214 0 0         if(!failure)
215 0           XSRETURN(0);
216              
217 0 0         if(GIMME_V == G_LIST) {
218 0 0         XPUSHs_from_AV(failure);
    0          
    0          
    0          
    0          
    0          
    0          
219 0           XSRETURN(av_count(failure));
220             }
221             else {
222 0 0         if(av_count(failure))
223 0 0         XPUSHs(AvARRAY(failure)[0]);
224             else
225 0 0         XPUSHs(&PL_sv_undef);
226 0           XSRETURN(1);
227             }
228              
229             SV *
230             on_fail(SV *self, SV *code)
231             CODE:
232 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
233 0           RETVAL = newSVsv(self);
234 0           future_on_fail(self, code);
235             OUTPUT:
236             RETVAL
237              
238             SV *
239             cancel(SV *self)
240             CODE:
241 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
242 0           RETVAL = SvREFCNT_inc(self);
243 0           future_cancel(self);
244             OUTPUT:
245             RETVAL
246              
247             SV *
248             without_cancel(SV *self)
249             CODE:
250 0           RETVAL = future_without_cancel(self);
251             OUTPUT:
252             RETVAL
253              
254             SV *
255             then(SV *self, ...)
256             ALIAS:
257             then = 0
258             then_with_f = FUTURE_THEN_WITH_F
259             CODE:
260 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
261 0 0         if(GIMME_V == G_VOID) {
262             // Need to ensure we print the ->transform message right
263 0           const PERL_CONTEXT *cx = caller_cx(0, NULL);
264 0 0         if(CxTYPE(cx) == CXt_SUB &&
265 0 0         strEQ(GvNAME(CvGV(cx->blk_sub.cv)), "transform")) {
266 0           warn_void_context("transform");
267             }
268             else {
269 0 0         warn_void_context(ix ? "then_with_f" : "then");
270             }
271             }
272              
273 0           items--; /* account for self */
274              
275 0           SV *thencode = &PL_sv_undef;
276 0 0         if(items) {
277 0           thencode = newSVsv(ST(1));
278 0           items--;
279             }
280              
281 0           SV *elsecode = &PL_sv_undef;
282 0 0         if(items % 2) {
283 0           elsecode = newSVsv(ST(1 + items));
284 0           items--;
285             }
286              
287 0 0         if(items) {
288 0           HV *catches = newHV();
289              
290 0 0         for(int i = 0; i < items/2; i++)
291 0           hv_store_ent(catches, ST(2 + i*2), newSVsv(ST(2 + i*2 + 1)), 0);
292              
293 0           RETVAL = future_thencatch(self, ix, thencode, catches, elsecode);
294             }
295             else {
296 0           RETVAL = future_then(self, ix, thencode, elsecode);
297             }
298             OUTPUT:
299             RETVAL
300              
301             SV *
302             else(SV *self, SV *code)
303             ALIAS:
304             else = 0
305             else_with_f = FUTURE_THEN_WITH_F
306             CODE:
307 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
308 0 0         warn_void_context(ix ? "else_with_f" : "else");
309 0           RETVAL = future_then(self, ix, NULL, newSVsv(code));
310             OUTPUT:
311             RETVAL
312              
313             SV *
314             catch(SV *self, ...)
315             ALIAS:
316             catch = 0
317             catch_with_f = FUTURE_THEN_WITH_F
318             CODE:
319 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
320 0 0         warn_void_context(ix ? "catch_with_f" : "catch");
321 0           items--; /* account for self */
322              
323 0           SV *elsecode = &PL_sv_undef;
324 0 0         if(items % 2) {
325 0           elsecode = newSVsv(ST(items));
326 0           items--;
327             }
328              
329 0           HV *catches = newHV();
330              
331 0 0         for(int i = 0; i < items/2; i++)
332 0           hv_store_ent(catches, ST(1 + i*2), newSVsv(ST(1 + i*2 + 1)), 0);
333              
334 0           RETVAL = future_thencatch(self, ix, NULL, catches, elsecode);
335             OUTPUT:
336             RETVAL
337              
338             SV *
339             followed_by(SV *self, SV *code)
340             CODE:
341 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
342 0           warn_void_context("followed_by");
343 0           RETVAL = future_followed_by(self, newSVsv(code));
344             OUTPUT:
345             RETVAL
346              
347             SV *
348             wait_all(SV *cls, ...)
349             CODE:
350 0           RETVAL = future_new_waitallv(SvPV_nolen(cls), &ST(1), items - 1);
351             OUTPUT:
352             RETVAL
353              
354             SV *
355             wait_any(SV *cls, ...)
356             CODE:
357 0           RETVAL = future_new_waitanyv(SvPV_nolen(cls), &ST(1), items - 1);
358             OUTPUT:
359             RETVAL
360              
361             SV *
362             needs_all(SV *cls, ...)
363             CODE:
364 0           RETVAL = future_new_needsallv(SvPV_nolen(cls), &ST(1), items - 1);
365             OUTPUT:
366             RETVAL
367              
368             SV *
369             needs_any(SV *cls, ...)
370             CODE:
371 0           RETVAL = future_new_needsanyv(SvPV_nolen(cls), &ST(1), items - 1);
372             OUTPUT:
373             RETVAL
374              
375             void
376             pending_futures(SV *self)
377             ALIAS:
378             pending_futures = FUTURE_SUBS_PENDING
379             ready_futures = FUTURE_SUBS_READY
380             done_futures = FUTURE_SUBS_DONE
381             failed_futures = FUTURE_SUBS_FAILED
382             cancelled_futures = FUTURE_SUBS_CANCELLED
383             PPCODE:
384 0 0         CHECK_INSTANCE(self);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
385 0           PUTBACK;
386 0           Size_t count = future_mPUSH_subs(self, ix);
387 0           SPAGAIN;
388 0           XSRETURN(count);
389              
390             SV *
391             btime(SV *self)
392             ALIAS:
393             btime = 0
394             rtime = 1
395             CODE:
396             {
397             struct timeval t;
398 0           switch(ix) {
399 0           case 0: t = future_get_btime(self); break;
400 0           case 1: t = future_get_rtime(self); break;
401             }
402              
403 0           RETVAL = &PL_sv_undef;
404              
405 0 0         if(t.tv_sec) {
406 0           AV *retav = newAV();
407 0           av_push(retav, newSViv(t.tv_sec));
408 0           av_push(retav, newSViv(t.tv_usec));
409              
410 0           RETVAL = newRV_noinc((SV *)retav);
411             }
412             }
413             OUTPUT:
414             RETVAL
415              
416             SV *
417             set_label(SV *self, SV *label)
418             CODE:
419 0           future_set_label(self, label);
420 0           RETVAL = SvREFCNT_inc(self);
421             OUTPUT:
422             RETVAL
423              
424             SV *
425             label(SV *self)
426             CODE:
427 0           SV *label = future_get_label(self);
428 0 0         RETVAL = label ? newSVsv(label) : &PL_sv_undef;
429             OUTPUT:
430             RETVAL
431              
432             SV *
433             set_udata(SV *self, SV *name, SV *value)
434             CODE:
435 0           future_set_udata(self, name, value);
436 0           RETVAL = SvREFCNT_inc(self);
437             OUTPUT:
438             RETVAL
439              
440             SV *
441             udata(SV *self, SV *name)
442             CODE:
443 0           RETVAL = newSVsv(future_get_udata(self, name));
444             OUTPUT:
445             RETVAL
446              
447             void
448             reread_environment()
449             CODE:
450 0           Future_reread_environment(aTHX);
451              
452             BOOT:
453 3           future_boot();