File Coverage

lib/Future/AsyncAwait/Hooks.xs
Criterion Covered Total %
statement 64 65 98.4
branch 22 32 68.7
condition n/a
subroutine n/a
pod n/a
total 86 97 88.6


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, 2023 -- leonerd@leonerd.org.uk
5             */
6             #include "EXTERN.h"
7             #include "perl.h"
8             #include "XSUB.h"
9              
10             #include "AsyncAwait.h"
11              
12             #include "XSParseKeyword.h"
13              
14             #include "perl-backcompat.c.inc"
15             #include "newOP_CUSTOM.c.inc"
16              
17             enum {
18             HOOK_SUSPEND = 1,
19             HOOK_RESUME,
20             };
21              
22 14           static void S_call_block_noargs(pTHX_ CV *blockcv)
23             {
24 14           dSP;
25 14 50         PUSHMARK(SP);
26              
27 14           call_sv((SV *)blockcv, G_VOID);
28 14           }
29             #define call_block_noargs(blockcv) S_call_block_noargs(aTHX_ blockcv)
30              
31 5           static void hook_post_suspend(pTHX_ CV *cv, HV *modhookdata, void *hookdata)
32             {
33 5           SV **svp = hv_fetchs(modhookdata, "Future::AsyncAwait::Hooks/hooklist", 0);
34 5 50         if(!svp)
35             return;
36              
37 5           AV *hooklist = (AV *)*svp;
38 5           svp = AvARRAY(hooklist);
39              
40             I32 i;
41 19 50         for(i = 0; i <= AvFILL(hooklist); i += 2) {
    100          
42 14 50         int type = SvIV(svp[i]);
43 14 100         if(type == HOOK_SUSPEND)
44 7           call_block_noargs((CV *)svp[i+1]);
45             }
46             }
47              
48 5           static void hook_pre_resume(pTHX_ CV *cv, HV *modhookdata, void *hookdata)
49             {
50 5           SV **svp = hv_fetchs(modhookdata, "Future::AsyncAwait::Hooks/hooklist", 0);
51 5 50         if(!svp)
52             return;
53              
54 5           AV *hooklist = (AV *)*svp;
55 5           svp = AvARRAY(hooklist);
56              
57             I32 i;
58 19 50         for(i = AvFILL(hooklist)-1; i >= 0; i -= 2) {
    100          
59 14 50         int type = SvIV(svp[i]);
60 14 100         if(type == HOOK_RESUME)
61 7           call_block_noargs((CV *)svp[i+1]);
62             }
63             }
64              
65             static const struct AsyncAwaitHookFuncs faa_hooks = {
66             .post_suspend = &hook_post_suspend,
67             .pre_resume = &hook_pre_resume,
68             };
69              
70             #define SAVEAVLEN(av) S_save_avlen(aTHX_ av)
71             /* This would be a lot neater if perl had a SAVEFUNCANY2() */
72             struct AvWithLength {
73             AV *av;
74             U32 len;
75             };
76 14           void restore_av_len(pTHX_ void *_avl)
77             {
78 14           struct AvWithLength *avl = _avl;
79 14           AV *av = avl->av;
80              
81 56 50         while(av_count(av) > avl->len)
    100          
82 28           SvREFCNT_dec(av_pop(av));
83              
84 14           Safefree(avl);
85 14           }
86 14           static void S_save_avlen(pTHX_ AV *av)
87             {
88             struct AvWithLength *avl;
89 14           Newx(avl, 1, struct AvWithLength);
90              
91 14           avl->av = av;
92 14 50         avl->len = av_count(av);
93              
94 14           SAVEDESTRUCTOR_X(restore_av_len, avl);
95 14           }
96              
97 14           static OP *pp_pushhook(pTHX)
98             {
99 14           CV *hookblock = (CV *)cSVOP_sv;
100 14           int type = PL_op->op_private;
101              
102 14           HV *modhookdata = future_asyncawait_get_modhookdata(find_runcv(0), FAA_MODHOOK_CREATE, PL_op->op_targ);
103 14 50         if(!modhookdata)
104 0           croak("panic: expected modhookdata");
105              
106             AV *hooklist;
107 14           SV **svp = hv_fetchs(modhookdata, "Future::AsyncAwait::Hooks/hooklist", 0);
108 14 100         if(svp)
109 10           hooklist = (AV *)*svp;
110             else
111 4           hv_stores(modhookdata, "Future::AsyncAwait::Hooks/hooklist", (SV *)(hooklist = newAV()));
112              
113 14           SAVEAVLEN(hooklist);
114              
115 14           av_push(hooklist, newSViv(type));
116 14           av_push(hooklist, (SV *)cv_clone(hookblock));
117              
118 14           return PL_op->op_next;
119             }
120              
121 6           static int build_suspend(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
122             {
123 6           CV *block = arg0->cv;
124              
125 6           *out = newSVOP_CUSTOM(&pp_pushhook, (HOOK_SUSPEND) << 8, (SV *)block);
126 6           (*out)->op_targ = future_asyncawait_make_precreate_padix();
127              
128 6           return KEYWORD_PLUGIN_STMT;
129             }
130              
131 6           static int build_resume(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
132             {
133 6           CV *block = arg0->cv;
134              
135 6           *out = newSVOP_CUSTOM(&pp_pushhook, (HOOK_RESUME) << 8, (SV *)block);
136 6           (*out)->op_targ = future_asyncawait_make_precreate_padix();
137              
138 6           return KEYWORD_PLUGIN_STMT;
139             }
140              
141             static const struct XSParseKeywordHooks hooks_suspend = {
142             .permit_hintkey = "Future::AsyncAwait::Hooks/suspend",
143             .piece1 = XPK_ANONSUB, // TODO: block
144             .build1 = &build_suspend,
145             };
146              
147             static const struct XSParseKeywordHooks hooks_resume = {
148             .permit_hintkey = "Future::AsyncAwait::Hooks/resume",
149             .piece1 = XPK_ANONSUB, // TODO: block
150             .build1 = &build_resume,
151             };
152              
153             MODULE = Future::AsyncAwait::Hooks PACKAGE = Future::AsyncAwait::Hooks
154              
155             BOOT:
156 2           boot_future_asyncawait(0.63);
157 2           boot_xs_parse_keyword(0.13);
158              
159 2           register_future_asyncawait_hook(&faa_hooks, NULL);
160              
161 2           register_xs_parse_keyword("suspend", &hooks_suspend, NULL);
162 2           register_xs_parse_keyword("resume", &hooks_resume, NULL);