File Coverage

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