File Coverage

AsyncAwait.h
Criterion Covered Total %
statement 21 27 77.7
branch 12 24 50.0
condition n/a
subroutine n/a
pod n/a
total 33 51 64.7


line stmt bran cond sub pod time code
1             #ifndef __FUTURE_ASYNCAWAIT_H__
2             #define __FUTURE_ASYNCAWAIT_H__
3              
4             #include "perl.h"
5              
6             #define FUTURE_ASYNCAWAIT_ABI_VERSION 2
7              
8             /*
9             * The API contained in this file is even more experimental than the rest of
10             * Future::AsyncAwait. It is primarily designed to allow suspend-aware dynamic
11             * variables in Syntax::Keyword::Dynamically, but may be useful for other
12             * tasks.
13             *
14             * There are no unit tests for these hooks inside this distribution, as testing
15             * it would require more XS code. It is tested as a side-effect of the
16             * integration with Syntax::Keyword::Dynamically.
17             */
18              
19             struct AsyncAwaitHookFuncs
20             {
21             U32 flags; /* currently unused but reserve the ABI space just in case */
22             void (*post_cv_copy)(pTHX_ CV *runcv, CV *cv, HV *modhookdata, void *hookdata);
23             void (*pre_suspend) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
24             void (*post_suspend)(pTHX_ CV *cv, HV *modhookdata, void *hookdata);
25             void (*pre_resume) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
26             void (*post_resume) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
27             void (*free) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
28             };
29              
30             static void (*register_future_asyncawait_hook_func)(pTHX_ const struct AsyncAwaitHookFuncs *hookfuncs, void *hookdata);
31             #define register_future_asyncawait_hook(hookfuncs, hookdata) S_register_future_asyncawait_hook(aTHX_ hookfuncs, hookdata)
32             static void S_register_future_asyncawait_hook(pTHX_ const struct AsyncAwaitHookFuncs *hookfuncs, void *hookdata)
33             {
34 2 50         if(!register_future_asyncawait_hook_func)
35 0           croak("Must call boot_future_asyncawait() first");
36              
37 2           (*register_future_asyncawait_hook_func)(aTHX_ hookfuncs, hookdata);
38             }
39              
40             #define future_asyncawait_on_activate(func, data) S_future_asyncawait_on_activate(aTHX_ func, data)
41             static void S_future_asyncawait_on_activate(pTHX_ void (*func)(pTHX_ void *data), void *data)
42             {
43             SV **svp;
44              
45             if((svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/loaded", FALSE)) && SvOK(*svp)) {
46             (*func)(aTHX_ data);
47             }
48             else {
49             AV *av;
50              
51             svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/on_loaded", FALSE);
52             if(svp)
53             av = (AV *)*svp;
54             else {
55             av = newAV();
56             hv_stores(PL_modglobal, "Future::AsyncAwait/on_loaded", (SV *)av);
57             }
58              
59             av_push(av, newSVuv(PTR2UV(func)));
60             av_push(av, newSVuv(PTR2UV(data)));
61             }
62             }
63              
64             /* flags constants for future_asyncawait_get_modhookdata() */
65             enum {
66             FAA_MODHOOK_CREATE = (1<<0),
67             };
68              
69             static HV *(*future_asyncawait_get_modhookdata_func)(pTHX_ CV *cv, U32 flags, PADOFFSET precreate_padix);
70             #define future_asyncawait_get_modhookdata(cv, flags, precreate_padix) \
71             S_future_asyncawait_get_modhookdata(aTHX_ cv, flags, precreate_padix)
72             static HV *S_future_asyncawait_get_modhookdata(pTHX_ CV *cv, U32 flags, PADOFFSET precreate_padix)
73             {
74 14 50         if(!future_asyncawait_get_modhookdata_func)
75 0           croak("Must call boot_future_asyncawait() first");
76              
77 14           return (*future_asyncawait_get_modhookdata_func)(aTHX_ cv, flags, precreate_padix);
78             }
79              
80             static PADOFFSET (*future_asyncawait_make_precreate_padix_func)(pTHX);
81             #define future_asyncawait_make_precreate_padix() S_future_asyncawait_make_precreate_padix(aTHX)
82 12           PADOFFSET S_future_asyncawait_make_precreate_padix(pTHX)
83             {
84 12 50         if(!future_asyncawait_make_precreate_padix_func)
85 0           croak("Must call boot_future_asyncawait() first");
86              
87 12           return (*future_asyncawait_make_precreate_padix_func)(aTHX);
88             }
89              
90             #define boot_future_asyncawait(ver) S_boot_future_asyncawait(aTHX_ ver)
91 2           static void S_boot_future_asyncawait(pTHX_ double ver)
92             {
93             SV **svp;
94 2 50         SV *versv = ver ? newSVnv(ver) : NULL;
95              
96 2           load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("Future::AsyncAwait"), versv, NULL);
97              
98 2           svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/ABIVERSION_MIN", 0);
99 2 50         if(!svp)
100 0           croak("Future::AsyncAwait ABI minimum version missing");
101 2 50         int abi_ver = SvIV(*svp);
102 2 50         if(abi_ver > FUTURE_ASYNCAWAIT_ABI_VERSION)
103 0           croak("Future::AsyncAwait ABI version mismatch - library supports >= %d, compiled for %d",
104             abi_ver, FUTURE_ASYNCAWAIT_ABI_VERSION);
105              
106 2           svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/ABIVERSION_MAX", 0);
107 2 50         abi_ver = SvIV(*svp);
108 2 50         if(abi_ver < FUTURE_ASYNCAWAIT_ABI_VERSION)
109 0           croak("Future::AsyncAwait ABI version mismatch - library supports <= %d, compiled for %d",
110             abi_ver, FUTURE_ASYNCAWAIT_ABI_VERSION);
111              
112 2 50         register_future_asyncawait_hook_func = INT2PTR(void (*)(pTHX_ const struct AsyncAwaitHookFuncs *, void *),
113             SvUV(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/register()@2", 0)));
114              
115 2 50         future_asyncawait_get_modhookdata_func = INT2PTR(HV *(*)(pTHX_ CV *, U32, PADOFFSET),
116             SvUV(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/get_modhookdata()@1", 0)));
117              
118 2 50         future_asyncawait_make_precreate_padix_func = INT2PTR(PADOFFSET (*)(pTHX),
119             SvUV(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/make_precreate_padix()@1", 0)));
120 2           }
121              
122             /**************
123             * Legacy API *
124             **************/
125              
126             /*
127             * This enum provides values for the `phase` hook parameter.
128             */
129             enum {
130             /* PRESUSPEND = 0x10, */
131             FAA_PHASE_POSTSUSPEND = 0x20,
132             FAA_PHASE_PRERESUME = 0x30,
133             /* POSTRESUME = 0x40, */
134             FAA_PHASE_FREE = 0xFF,
135             };
136              
137             /*
138             * The type of suspend hook functions.
139             *
140             * `phase` indicates the point in the suspend/resume lifecycle, as one of
141             * the values of the enum above.
142             * `cv` points to the CV being suspended or resumed. This will be after it
143             * has been cloned, if necessary.
144             * `modhookdata` points to an HV associated with the CV state, and may be
145             * used by modules as a scratchpad to store extra data relating to this
146             * function. Callers should prefix keys with their own module name to
147             * avoid collisions.
148             */
149             typedef void SuspendHookFunc(pTHX_ U8 phase, CV *cv, HV *modhookdata);
150              
151             /*
152             * Callers should use this function-like macro to set the value of the hook
153             * function variable, by passing in the address of a new function and a pointer
154             * to a variable to capture the previous value.
155             *
156             * static SuspendHookFunc *oldhook;
157             *
158             * future_asyncawait_wrap_suspendhook(&my_hook_func, &oldhook);
159             *
160             * The hook function itself should remember to chain to the oldhook function,
161             * whose value will never be NULL;
162             *
163             * void my_hook_func(aTHX_ U8 phase, CV *cv, HV *modhookdata)
164             * {
165             * ...
166             * (*oldhook)(phase, cv, modhookdata);
167             * }
168             */
169              
170             static void S_null_suspendhook(pTHX_ U8 phase, CV *cv, HV *modhookdata)
171             {
172             /* empty */
173             }
174              
175             #ifndef OP_CHECK_MUTEX_LOCK /* < 5.15.8 */
176             # define OP_CHECK_MUTEX_LOCK ((void)0)
177             # define OP_CHECK_MUTEX_UNLOCK ((void)0)
178             #endif
179              
180             #define future_asyncawait_wrap_suspendhook(newfunc, oldhookp) S_future_asyncawait_wrap_suspendhook(aTHX_ newfunc, oldhookp)
181             static void S_future_asyncawait_wrap_suspendhook(pTHX_ SuspendHookFunc *newfunc, SuspendHookFunc **oldhookp)
182             {
183             if(*oldhookp)
184             return;
185              
186             warn("future_asyncawait_wrap_suspendhook() is now deprecated; use register_future_asyncawait_hook() instead");
187              
188             /* Rather than define our own mutex for this very-rare usecase, we'll just
189             * abuse core's opcheck mutex for it. At worst this leads to thread
190             * contention at module load time for this very quick test
191             */
192             OP_CHECK_MUTEX_LOCK;
193              
194             if(!*oldhookp) {
195             SV **hookp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/suspendhook", TRUE);
196             if(hookp && SvOK(*hookp))
197             *oldhookp = INT2PTR(SuspendHookFunc *, SvUV(*hookp));
198             else
199             *oldhookp = &S_null_suspendhook;
200              
201             sv_setuv(*hookp, PTR2UV(newfunc));
202             }
203              
204             OP_CHECK_MUTEX_UNLOCK;
205             }
206              
207             #endif