File Coverage

XSPromises.xs
Criterion Covered Total %
statement 296 307 96.4
branch 145 212 68.4
condition n/a
subroutine n/a
pod n/a
total 441 519 84.9


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             #define MY_CXT_KEY "AnyEvent::XSPromises::_guts" XS_VERSION
9              
10             typedef struct xspr_callback_s xspr_callback_t;
11             typedef struct xspr_promise_s xspr_promise_t;
12             typedef struct xspr_result_s xspr_result_t;
13             typedef struct xspr_callback_queue_s xspr_callback_queue_t;
14              
15             typedef enum {
16             XSPR_STATE_NONE,
17             XSPR_STATE_PENDING,
18             XSPR_STATE_FINISHED,
19             } xspr_promise_state_t;
20              
21             typedef enum {
22             XSPR_RESULT_NONE,
23             XSPR_RESULT_RESOLVED,
24             XSPR_RESULT_REJECTED,
25             XSPR_RESULT_BOTH
26             } xspr_result_state_t;
27              
28             typedef enum {
29             XSPR_CALLBACK_PERL,
30             XSPR_CALLBACK_FINALLY,
31             XSPR_CALLBACK_CHAIN
32             } xspr_callback_type_t;
33              
34             struct xspr_callback_s {
35             xspr_callback_type_t type;
36             union {
37             struct {
38             SV* on_resolve;
39             SV* on_reject;
40             xspr_promise_t* next;
41             } perl;
42             struct {
43             SV* on_finally;
44             xspr_promise_t* next;
45             } finally;
46             xspr_promise_t* chain;
47             };
48             };
49              
50             struct xspr_result_s {
51             xspr_result_state_t state;
52             SV** result;
53             int count;
54             int refs;
55             };
56              
57             struct xspr_promise_s {
58             xspr_promise_state_t state;
59             int refs;
60             union {
61             struct {
62             xspr_callback_t** callbacks;
63             int callbacks_count;
64             } pending;
65             struct {
66             xspr_result_t *result;
67             } finished;
68             };
69             };
70              
71             struct xspr_callback_queue_s {
72             xspr_promise_t* origin;
73             xspr_callback_t* callback;
74             xspr_callback_queue_t* next;
75             };
76              
77             void xspr_queue_flush(pTHX);
78             void xspr_queue_add(pTHX_ xspr_callback_t* callback, xspr_promise_t* origin);
79             void xspr_queue_maybe_schedule(pTHX);
80              
81             xspr_callback_t* xspr_callback_new_perl(pTHX_ SV* on_resolve, SV* on_reject, xspr_promise_t* next);
82             xspr_callback_t* xspr_callback_new_chain(pTHX_ xspr_promise_t* chain);
83             void xspr_callback_process(pTHX_ xspr_callback_t* callback, xspr_promise_t* origin);
84             void xspr_callback_free(pTHX_ xspr_callback_t* callback);
85              
86             xspr_promise_t* xspr_promise_new(pTHX);
87             void xspr_promise_then(pTHX_ xspr_promise_t* promise, xspr_callback_t* callback);
88             void xspr_promise_finish(pTHX_ xspr_promise_t* promise, xspr_result_t *result);
89             void xspr_promise_incref(pTHX_ xspr_promise_t* promise);
90             void xspr_promise_decref(pTHX_ xspr_promise_t* promise);
91              
92             xspr_result_t* xspr_result_new(pTHX_ xspr_result_state_t state, int count);
93             xspr_result_t* xspr_result_from_error(pTHX_ const char *error);
94             void xspr_result_incref(pTHX_ xspr_result_t* result);
95             void xspr_result_decref(pTHX_ xspr_result_t* result);
96              
97             xspr_result_t* xspr_invoke_perl(pTHX_ SV* perl_fn, SV** input, int input_count);
98             xspr_promise_t* xspr_promise_from_sv(pTHX_ SV* input);
99              
100              
101             typedef struct {
102             xspr_callback_queue_t* queue_head;
103             xspr_callback_queue_t* queue_tail;
104             int in_flush;
105             int backend_scheduled;
106             SV* conversion_helper;
107             SV* backend_fn;
108             } my_cxt_t;
109              
110             typedef struct {
111             xspr_promise_t* promise;
112             } AnyEvent__XSPromises__Deferred;
113              
114             typedef struct {
115             xspr_promise_t* promise;
116             } AnyEvent__XSPromises__Promise;
117              
118             START_MY_CXT
119              
120             /* Process a single callback */
121 193           void xspr_callback_process(pTHX_ xspr_callback_t* callback, xspr_promise_t* origin)
122             {
123             assert(origin->state == XSPR_STATE_FINISHED);
124              
125 193 100         if (callback->type == XSPR_CALLBACK_CHAIN) {
126 12           xspr_promise_finish(aTHX_ callback->chain, origin->finished.result);
127              
128 181 100         } else if (callback->type == XSPR_CALLBACK_PERL) {
129             SV* callback_fn;
130              
131 172 100         if (origin->finished.result->state == XSPR_RESULT_RESOLVED) {
132 148           callback_fn = callback->perl.on_resolve;
133 24 50         } else if (origin->finished.result->state == XSPR_RESULT_REJECTED) {
134 24           callback_fn = callback->perl.on_reject;
135             } else {
136 0           callback_fn = NULL; /* Be quiet, bad compiler! */
137             assert(0);
138             }
139              
140 172 100         if (callback_fn != NULL) {
141             xspr_result_t* result;
142 168           result = xspr_invoke_perl(aTHX_
143             callback_fn,
144 168           origin->finished.result->result,
145 168           origin->finished.result->count);
146              
147 168 100         if (callback->perl.next != NULL) {
148 113           int skip_passthrough = 0;
149              
150 113 100         if (result->count == 1 && result->state == XSPR_RESULT_RESOLVED) {
    100          
151 102           xspr_promise_t* promise = xspr_promise_from_sv(aTHX_ result->result[0]);
152 103 100         if (promise != NULL && promise == callback->perl.next) {
    100          
153             /* This is an extreme corner case the A+ spec made us implement: we need to reject
154             * cases where the promise created from then() is passed back to its own callback */
155 1           xspr_result_t* chain_error = xspr_result_from_error(aTHX_ "TypeError");
156 1           xspr_promise_finish(aTHX_ callback->perl.next, chain_error);
157              
158 1           xspr_result_decref(aTHX_ chain_error);
159 1           xspr_promise_decref(aTHX_ promise);
160 1           skip_passthrough= 1;
161              
162 101 100         } else if (promise != NULL) {
163             /* Fairly normal case: we returned a promise from the callback */
164 12           xspr_callback_t* chainback = xspr_callback_new_chain(aTHX_ callback->perl.next);
165 12           xspr_promise_then(aTHX_ promise, chainback);
166              
167 12           xspr_promise_decref(aTHX_ promise);
168 12           skip_passthrough = 1;
169             }
170             }
171              
172 113 100         if (!skip_passthrough) {
173 100           xspr_promise_finish(aTHX_ callback->perl.next, result);
174             }
175             }
176              
177 168           xspr_result_decref(aTHX_ result);
178              
179 4 50         } else if (callback->perl.next) {
180             /* No callback, so we're just passing the result along. */
181 4           xspr_result_t* result = origin->finished.result;
182 172           xspr_promise_finish(aTHX_ callback->perl.next, result);
183             }
184              
185 9 50         } else if (callback->type == XSPR_CALLBACK_FINALLY) {
186 9           SV* callback_fn = callback->finally.on_finally;
187 9 50         if (callback_fn != NULL) {
188             xspr_result_t* result;
189 9           result = xspr_invoke_perl(aTHX_
190             callback_fn,
191 9           origin->finished.result->result,
192 9           origin->finished.result->count);
193 9           xspr_result_decref(aTHX_ result);
194             }
195              
196 9 100         if (callback->finally.next != NULL) {
197 9           xspr_promise_finish(aTHX_ callback->finally.next, origin->finished.result);
198             }
199              
200             } else {
201             assert(0);
202             }
203 193           }
204              
205             /* Frees the xspr_callback_t structure */
206 194           void xspr_callback_free(pTHX_ xspr_callback_t *callback)
207             {
208 194 100         if (callback->type == XSPR_CALLBACK_CHAIN) {
209 12           xspr_promise_decref(aTHX_ callback->chain);
210              
211 182 100         } else if (callback->type == XSPR_CALLBACK_PERL) {
212 173           SvREFCNT_dec(callback->perl.on_resolve);
213 173           SvREFCNT_dec(callback->perl.on_reject);
214 173 100         if (callback->perl.next != NULL)
215 173           xspr_promise_decref(aTHX_ callback->perl.next);
216              
217 9 50         } else if (callback->type == XSPR_CALLBACK_FINALLY) {
218 9           SvREFCNT_dec(callback->finally.on_finally);
219 9 100         if (callback->finally.next != NULL)
220 9           xspr_promise_decref(aTHX_ callback->finally.next);
221              
222             } else {
223             assert(0);
224             }
225              
226 194           Safefree(callback);
227 194           }
228              
229             /* Process the queue until it's empty */
230 5           void xspr_queue_flush(pTHX)
231             {
232             dMY_CXT;
233              
234 5 50         if (MY_CXT.in_flush) {
235             /* XXX: is there a reasonable way to trigger this? */
236 0           warn("Rejecting request to flush promises queue: already processing");
237 0           return;
238             }
239 5           MY_CXT.in_flush = 1;
240              
241 198 100         while (MY_CXT.queue_head != NULL) {
242             /* Save some typing... */
243 193           xspr_callback_queue_t *cur = MY_CXT.queue_head;
244              
245             /* Process the callback. This could trigger some Perl code, meaning we
246             * could end up with additional queue entries after this */
247 193           xspr_callback_process(aTHX_ cur->callback, cur->origin);
248              
249             /* Free-ing the callback structure could theoretically trigger DESTROY subs,
250             * enqueueing new callbacks, so we can't assume the loop ends here! */
251 193           MY_CXT.queue_head = cur->next;
252 193 100         if (cur->next == NULL) {
253 5           MY_CXT.queue_tail = NULL;
254             }
255              
256             /* Destroy the structure */
257 193           xspr_callback_free(aTHX_ cur->callback);
258 193           xspr_promise_decref(aTHX_ cur->origin);
259 193           Safefree(cur);
260             }
261              
262 5           MY_CXT.in_flush = 0;
263 5           MY_CXT.backend_scheduled = 0;
264             }
265              
266             /* Add a callback invocation into the queue for the given origin promise.
267             * Takes ownership of the callback structure */
268 193           void xspr_queue_add(pTHX_ xspr_callback_t* callback, xspr_promise_t* origin)
269             {
270             dMY_CXT;
271              
272             xspr_callback_queue_t* entry;
273 193           Newxz(entry, 1, xspr_callback_queue_t);
274 193           entry->origin = origin;
275 193           xspr_promise_incref(aTHX_ entry->origin);
276 193           entry->callback = callback;
277              
278 193 100         if (MY_CXT.queue_head == NULL) {
279             assert(MY_CXT.queue_tail == NULL);
280             /* Empty queue, so now it's just us */
281 5           MY_CXT.queue_head = entry;
282 5           MY_CXT.queue_tail = entry;
283              
284             } else {
285             assert(MY_CXT.queue_tail != NULL);
286             /* Existing queue, add to the tail */
287 188           MY_CXT.queue_tail->next = entry;
288 188           MY_CXT.queue_tail = entry;
289             }
290 193           }
291              
292 241           void xspr_queue_maybe_schedule(pTHX)
293             {
294             dMY_CXT;
295 241 100         if (MY_CXT.queue_head == NULL || MY_CXT.backend_scheduled || MY_CXT.in_flush) {
    100          
    50          
296 236           return;
297             }
298              
299 5           MY_CXT.backend_scheduled = 1;
300             /* We trust our backends to be sane, so little guarding against errors here */
301 5           dSP;
302 5 50         PUSHMARK(SP);
303 5           call_sv(MY_CXT.backend_fn, G_DISCARD|G_NOARGS);
304             }
305              
306             /* Invoke the user's perl code. We need to be really sure this doesn't return early via croak/next/etc. */
307 182           xspr_result_t* xspr_invoke_perl(pTHX_ SV* perl_fn, SV** input, int input_count)
308             {
309 182           dSP;
310             int count, i;
311             SV* error;
312             xspr_result_t* result;
313              
314 182 100         if (!SvROK(perl_fn)) {
315 3           return xspr_result_from_error(aTHX_ "promise callbacks need to be a CODE reference");
316             }
317              
318 179           ENTER;
319 179           SAVETMPS;
320              
321 179 50         PUSHMARK(SP);
322 179 50         EXTEND(SP, input_count);
    50          
323 373 100         for (i = 0; i < input_count; i++) {
324 194           PUSHs(input[i]);
325             }
326 179           PUTBACK;
327              
328             /* Clear $_ so that callbacks don't end up talking to each other by accident */
329 179           SAVE_DEFSV;
330 179           DEFSV_set(sv_newmortal());
331              
332 179           count = call_sv(perl_fn, G_EVAL|G_ARRAY);
333              
334 179           SPAGAIN;
335 179 50         error = ERRSV;
336 179 50         if (SvTRUE(error)) {
    50          
    0          
    50          
    0          
    0          
    100          
    50          
    100          
    50          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
337 8           result = xspr_result_new(aTHX_ XSPR_RESULT_REJECTED, 1);
338 8           result->result[0] = newSVsv(error);
339             } else {
340 171           result = xspr_result_new(aTHX_ XSPR_RESULT_RESOLVED, count);
341 335 100         for (i = 0; i < count; i++) {
342 164           result->result[count-i-1] = SvREFCNT_inc(POPs);
343             }
344             }
345 179           PUTBACK;
346              
347 179 50         FREETMPS;
348 179           LEAVE;
349              
350 179           return result;
351             }
352              
353             /* Increments the ref count for xspr_result_t */
354 184           void xspr_result_incref(pTHX_ xspr_result_t* result)
355             {
356 184           result->refs++;
357 184           }
358              
359             /* Decrements the ref count for the xspr_result_t, freeing the structure if needed */
360 426           void xspr_result_decref(pTHX_ xspr_result_t* result)
361             {
362 426 100         if (--(result->refs) == 0) {
363             int i;
364 496 100         for (i = 0; i < result->count; i++) {
365 254           SvREFCNT_dec(result->result[i]);
366             }
367 242           Safefree(result->result);
368 242           Safefree(result);
369             }
370 426           }
371              
372             /* Transitions a promise from pending to finished, using the given result */
373 184           void xspr_promise_finish(pTHX_ xspr_promise_t* promise, xspr_result_t* result)
374             {
375             assert(promise->state == XSPR_STATE_PENDING);
376 184           xspr_callback_t** pending_callbacks = promise->pending.callbacks;
377 184           int count = promise->pending.callbacks_count;
378              
379 184           promise->state = XSPR_STATE_FINISHED;
380 184           promise->finished.result = result;
381 184           xspr_result_incref(aTHX_ promise->finished.result);
382              
383             int i;
384 325 100         for (i = 0; i < count; i++) {
385 141           xspr_queue_add(aTHX_ pending_callbacks[i], promise);
386             }
387 184           Safefree(pending_callbacks);
388 184           }
389              
390             /* Create a new xspr_result_t object with the given number of item slots */
391 242           xspr_result_t* xspr_result_new(pTHX_ xspr_result_state_t state, int count)
392             {
393             xspr_result_t* result;
394 242           Newxz(result, 1, xspr_result_t);
395 242 50         Newxz(result->result, count, SV*);
396 242           result->state = state;
397 242           result->refs = 1;
398 242           result->count = count;
399 242           return result;
400             }
401              
402 4           xspr_result_t* xspr_result_from_error(pTHX_ const char *error)
403             {
404 4           xspr_result_t* result = xspr_result_new(aTHX_ XSPR_RESULT_REJECTED, 1);
405 4           result->result[0] = newSVpv(error, 0);
406 4           return result;
407             }
408              
409             /* Increments the ref count for xspr_promise_t */
410 405           void xspr_promise_incref(pTHX_ xspr_promise_t* promise)
411             {
412 405           (promise->refs)++;
413 405           }
414              
415             /* Decrements the ref count for the xspr_promise_t, freeing the structure if needed */
416 590           void xspr_promise_decref(pTHX_ xspr_promise_t *promise)
417             {
418 590 100         if (--(promise->refs) == 0) {
419 185 100         if (promise->state == XSPR_STATE_PENDING) {
420             /* XXX: is this a bad thing we should warn for? */
421 1           int count = promise->pending.callbacks_count;
422 1           xspr_callback_t **callbacks = promise->pending.callbacks;
423             int i;
424 2 100         for (i = 0; i < count; i++) {
425 1           xspr_callback_free(aTHX_ callbacks[i]);
426             }
427 1           Safefree(callbacks);
428              
429 184 50         } else if (promise->state == XSPR_STATE_FINISHED) {
430 184           xspr_result_decref(aTHX_ promise->finished.result);
431              
432             } else {
433             assert(0);
434             }
435              
436 185           Safefree(promise);
437             }
438 590           }
439              
440             /* Creates a new promise. It's that simple. */
441 185           xspr_promise_t* xspr_promise_new(pTHX)
442             {
443             xspr_promise_t* promise;
444 185           Newxz(promise, 1, xspr_promise_t);
445 185           promise->refs = 1;
446 185           promise->state = XSPR_STATE_PENDING;
447 185           return promise;
448             }
449              
450 173           xspr_callback_t* xspr_callback_new_perl(pTHX_ SV* on_resolve, SV* on_reject, xspr_promise_t* next)
451             {
452             xspr_callback_t* callback;
453 173           Newxz(callback, 1, xspr_callback_t);
454 173           callback->type = XSPR_CALLBACK_PERL;
455 173 100         if (SvOK(on_resolve))
    50          
    50          
456 162           callback->perl.on_resolve = newSVsv(on_resolve);
457 173 100         if (SvOK(on_reject))
    50          
    50          
458 124           callback->perl.on_reject = newSVsv(on_reject);
459 173           callback->perl.next = next;
460 173 100         if (next)
461 117           xspr_promise_incref(aTHX_ callback->perl.next);
462 173           return callback;
463             }
464              
465 9           xspr_callback_t* xspr_callback_new_finally(pTHX_ SV* on_finally, xspr_promise_t* next)
466             {
467             xspr_callback_t* callback;
468 9           Newxz(callback, 1, xspr_callback_t);
469 9           callback->type = XSPR_CALLBACK_FINALLY;
470 9 50         if (SvOK(on_finally))
    0          
    0          
471 9           callback->finally.on_finally = newSVsv(on_finally);
472 9           callback->finally.next = next;
473 9 100         if (next)
474 8           xspr_promise_incref(aTHX_ callback->finally.next);
475 9           return callback;
476             }
477              
478 12           xspr_callback_t* xspr_callback_new_chain(pTHX_ xspr_promise_t* chain)
479             {
480             xspr_callback_t* callback;
481 12           Newxz(callback, 1, xspr_callback_t);
482 12           callback->type = XSPR_CALLBACK_CHAIN;
483 12           callback->chain = chain;
484 12           xspr_promise_incref(aTHX_ chain);
485 12           return callback;
486             }
487              
488             /* Adds a then to the promise. Takes ownership of the callback */
489 194           void xspr_promise_then(pTHX_ xspr_promise_t* promise, xspr_callback_t* callback)
490             {
491 194 100         if (promise->state == XSPR_STATE_PENDING) {
492 142           promise->pending.callbacks_count++;
493 142 50         Renew(promise->pending.callbacks, promise->pending.callbacks_count, xspr_callback_t*);
494 142           promise->pending.callbacks[promise->pending.callbacks_count-1] = callback;
495              
496 52 50         } else if (promise->state == XSPR_STATE_FINISHED) {
497 52           xspr_queue_add(aTHX_ callback, promise);
498              
499             } else {
500             assert(0);
501             }
502 194           }
503              
504             /* Returns a promise if the given SV is a thenable. Ownership handed to the caller! */
505 102           xspr_promise_t* xspr_promise_from_sv(pTHX_ SV* input)
506             {
507 102 50         if (input == NULL || !sv_isobject(input)) {
    100          
508 89           return NULL;
509             }
510              
511             /* If we got one of our own promises: great, not much to do here! */
512 13 100         if (sv_derived_from(input, "AnyEvent::XSPromises::PromisePtr")) {
513 8 50         IV tmp = SvIV((SV*)SvRV(input));
514 8           AnyEvent__XSPromises__Promise* promise = INT2PTR(AnyEvent__XSPromises__Promise*, tmp);
515 8           xspr_promise_incref(aTHX_ promise->promise);
516 8           return promise->promise;
517             }
518              
519             /* Maybe we got another type of promise. Let's convert it */
520 5           GV* method_gv = gv_fetchmethod_autoload(SvSTASH(SvRV(input)), "then", FALSE);
521 5 50         if (method_gv != NULL && isGV(method_gv) && GvCV(method_gv) != NULL) {
    50          
    50          
522             dMY_CXT;
523              
524 5           xspr_result_t* new_result = xspr_invoke_perl(aTHX_ MY_CXT.conversion_helper, &input, 1);
525 5 50         if (new_result->state == XSPR_RESULT_RESOLVED &&
    50          
526 5 50         new_result->count == 1 &&
527 5 50         new_result->result[0] != NULL &&
528 5 50         SvROK(new_result->result[0]) &&
529 5           sv_derived_from(new_result->result[0], "AnyEvent::XSPromises::PromisePtr")) {
530             /* This is expected: our conversion function returned us one of our own promises */
531 5 50         IV tmp = SvIV((SV*)SvRV(new_result->result[0]));
532 5           AnyEvent__XSPromises__Promise* new_promise = INT2PTR(AnyEvent__XSPromises__Promise*, tmp);
533              
534 5           xspr_promise_t* promise = new_promise->promise;
535 5           xspr_promise_incref(aTHX_ promise);
536              
537 5           xspr_result_decref(aTHX_ new_result);
538 5           return promise;
539              
540             } else {
541 0           xspr_promise_t* promise = xspr_promise_new(aTHX);
542 0           xspr_promise_finish(aTHX_ promise, new_result);
543 0           xspr_result_decref(aTHX_ new_result);
544 0           return promise;
545             }
546             }
547              
548             /* We didn't get a promise. */
549 0           return NULL;
550             }
551              
552              
553             MODULE = AnyEvent::XSPromises PACKAGE = AnyEvent::XSPromises
554              
555             PROTOTYPES: ENABLE
556              
557             TYPEMAP: <
558             TYPEMAP
559             AnyEvent::XSPromises::Deferred* T_PTROBJ
560             AnyEvent::XSPromises::Promise* T_PTROBJ
561             EOT
562              
563             BOOT:
564             {
565             /* XXX: do we need a CLONE? */
566              
567             MY_CXT_INIT;
568 2           MY_CXT.queue_head = NULL;
569 2           MY_CXT.queue_tail = NULL;
570 2           MY_CXT.in_flush = 0;
571 2           MY_CXT.backend_scheduled = 0;
572 2           MY_CXT.conversion_helper = NULL;
573 2           MY_CXT.backend_fn = NULL;
574             }
575              
576             AnyEvent::XSPromises::Deferred*
577             deferred()
578             CODE:
579 60           Newxz(RETVAL, 1, AnyEvent__XSPromises__Deferred);
580 60           xspr_promise_t* promise = xspr_promise_new(aTHX);
581 60           RETVAL->promise = promise;
582             OUTPUT:
583             RETVAL
584              
585             void
586             ___flush()
587             CODE:
588 5           xspr_queue_flush(aTHX);
589              
590             void
591             ___set_conversion_helper(helper)
592             SV* helper
593             CODE:
594             dMY_CXT;
595 2 50         if (MY_CXT.conversion_helper != NULL)
596 0           croak("Refusing to set a conversion helper twice");
597 2           MY_CXT.conversion_helper = newSVsv(helper);
598              
599             void
600             ___set_backend(backend)
601             SV* backend
602             CODE:
603             dMY_CXT;
604 2 50         if (MY_CXT.backend_fn != NULL)
605 0           croak("Refusing to set a backend twice");
606 2           MY_CXT.backend_fn = newSVsv(backend);
607              
608              
609             MODULE = AnyEvent::XSPromises PACKAGE = AnyEvent::XSPromises::DeferredPtr
610              
611             AnyEvent::XSPromises::Promise*
612             promise(self)
613             AnyEvent::XSPromises::Deferred* self
614             CODE:
615 62           Newxz(RETVAL, 1, AnyEvent__XSPromises__Promise);
616 62           RETVAL->promise = self->promise;
617 62           xspr_promise_incref(aTHX_ RETVAL->promise);
618             OUTPUT:
619             RETVAL
620              
621             void
622             resolve(self, ...)
623             AnyEvent::XSPromises::Deferred* self
624             CODE:
625 51 100         if (self->promise->state != XSPR_STATE_PENDING) {
626 4           croak("Cannot resolve deferred: not pending");
627             }
628              
629 47           xspr_result_t* result = xspr_result_new(aTHX_ XSPR_RESULT_RESOLVED, items-1);
630             int i;
631 118 100         for (i = 0; i < items-1; i++) {
632 71           result->result[i] = SvREFCNT_inc(ST(1+i));
633             }
634 47           xspr_promise_finish(aTHX_ self->promise, result);
635 47           xspr_result_decref(aTHX_ result);
636 47           xspr_queue_maybe_schedule(aTHX);
637              
638             void
639             reject(self, ...)
640             AnyEvent::XSPromises::Deferred* self
641             CODE:
642 14 100         if (self->promise->state != XSPR_STATE_PENDING) {
643 2           croak("Cannot reject deferred: not pending");
644             }
645              
646 12           xspr_result_t* result = xspr_result_new(aTHX_ XSPR_RESULT_REJECTED, items-1);
647             int i;
648 19 100         for (i = 0; i < items-1; i++) {
649 7           result->result[i] = SvREFCNT_inc(ST(1+i));
650             }
651 12           xspr_promise_finish(aTHX_ self->promise, result);
652 12           xspr_result_decref(aTHX_ result);
653 12           xspr_queue_maybe_schedule(aTHX);
654              
655             bool
656             is_in_progress(self)
657             AnyEvent::XSPromises::Deferred* self
658             CODE:
659 2           RETVAL = (self->promise->state == XSPR_STATE_PENDING);
660             OUTPUT:
661             RETVAL
662              
663             void
664             DESTROY(self)
665             AnyEvent::XSPromises::Deferred* self
666             CODE:
667 60           xspr_promise_decref(aTHX_ self->promise);
668 60           Safefree(self);
669              
670              
671             MODULE = AnyEvent::XSPromises PACKAGE = AnyEvent::XSPromises::PromisePtr
672              
673             void
674             then(self, ...)
675             AnyEvent::XSPromises::Promise* self
676             PPCODE:
677             SV* on_resolve;
678             SV* on_reject;
679 162           xspr_promise_t* next = NULL;
680              
681 162 50         if (items > 3) {
682 0           croak_xs_usage(cv, "self, on_resolve, on_reject");
683             }
684              
685 162 50         on_resolve = (items > 1) ? ST(1) : &PL_sv_undef;
686 162 100         on_reject = (items > 2) ? ST(2) : &PL_sv_undef;
687              
688             /* Many promises are just thrown away after the final callback, no need to allocate a next promise for those */
689 162 100         if (GIMME_V != G_VOID) {
    100          
690             AnyEvent__XSPromises__Promise* next_promise;
691 106           Newxz(next_promise, 1, AnyEvent__XSPromises__Promise);
692              
693 106           next = xspr_promise_new(aTHX);
694 106           next_promise->promise = next;
695              
696 106           ST(0) = sv_newmortal();
697 106           sv_setref_pv(ST(0), "AnyEvent::XSPromises::PromisePtr", (void*)next_promise);
698             }
699              
700 162           xspr_callback_t* callback = xspr_callback_new_perl(aTHX_ on_resolve, on_reject, next);
701 162           xspr_promise_then(aTHX_ self->promise, callback);
702 162           xspr_queue_maybe_schedule(aTHX);
703              
704 162           XSRETURN(1);
705              
706             void
707             catch(self, on_reject)
708             AnyEvent::XSPromises::Promise* self
709             SV* on_reject
710             PPCODE:
711 11           xspr_promise_t* next = NULL;
712              
713             /* Many promises are just thrown away after the final callback, no need to allocate a next promise for those */
714 11 100         if (GIMME_V != G_VOID) {
    50          
715             AnyEvent__XSPromises__Promise* next_promise;
716 11           Newxz(next_promise, 1, AnyEvent__XSPromises__Promise);
717              
718 11           next = xspr_promise_new(aTHX);
719 11           next_promise->promise = next;
720              
721 11           ST(0) = sv_newmortal();
722 11           sv_setref_pv(ST(0), "AnyEvent::XSPromises::PromisePtr", (void*)next_promise);
723             }
724              
725 11           xspr_callback_t* callback = xspr_callback_new_perl(aTHX_ &PL_sv_undef, on_reject, next);
726 11           xspr_promise_then(aTHX_ self->promise, callback);
727 11           xspr_queue_maybe_schedule(aTHX);
728              
729 11           XSRETURN(1);
730              
731             void
732             finally(self, on_finally)
733             AnyEvent::XSPromises::Promise* self
734             SV* on_finally
735             PPCODE:
736 9           xspr_promise_t* next = NULL;
737              
738             /* Many promises are just thrown away after the final callback, no need to allocate a next promise for those */
739 9 100         if (GIMME_V != G_VOID) {
    100          
740             AnyEvent__XSPromises__Promise* next_promise;
741 8           Newxz(next_promise, 1, AnyEvent__XSPromises__Promise);
742              
743 8           next = xspr_promise_new(aTHX);
744 8           next_promise->promise = next;
745              
746 8           ST(0) = sv_newmortal();
747 8           sv_setref_pv(ST(0), "AnyEvent::XSPromises::PromisePtr", (void*)next_promise);
748             }
749              
750 9           xspr_callback_t* callback = xspr_callback_new_finally(aTHX_ on_finally, next);
751 9           xspr_promise_then(aTHX_ self->promise, callback);
752 9           xspr_queue_maybe_schedule(aTHX);
753              
754 9           XSRETURN(1);
755              
756             void
757             DESTROY(self)
758             AnyEvent::XSPromises::Promise* self
759             CODE:
760 187           xspr_promise_decref(aTHX_ self->promise);
761 187           Safefree(self);