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