File Coverage

AsyncAwait.h
Criterion Covered Total %
statement 14 18 77.7
branch 8 16 50.0
condition n/a
subroutine n/a
pod n/a
total 22 34 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             #define boot_future_asyncawait(ver) S_boot_future_asyncawait(aTHX_ ver)
63 2           static void S_boot_future_asyncawait(pTHX_ double ver)
64             {
65             SV **svp;
66 2 50         SV *versv = ver ? newSVnv(ver) : NULL;
67              
68 2           load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("Future::AsyncAwait"), versv, NULL);
69              
70 2           svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/ABIVERSION_MIN", 0);
71 2 50         if(!svp)
72 0           croak("Future::AsyncAwait ABI minimum version missing");
73 2 50         int abi_ver = SvIV(*svp);
74 2 50         if(abi_ver > FUTURE_ASYNCAWAIT_ABI_VERSION)
75 0           croak("Future::AsyncAwait ABI version mismatch - library supports >= %d, compiled for %d",
76             abi_ver, FUTURE_ASYNCAWAIT_ABI_VERSION);
77              
78 2           svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/ABIVERSION_MAX", 0);
79 2 50         abi_ver = SvIV(*svp);
80 2 50         if(abi_ver < FUTURE_ASYNCAWAIT_ABI_VERSION)
81 0           croak("Future::AsyncAwait ABI version mismatch - library supports <= %d, compiled for %d",
82             abi_ver, FUTURE_ASYNCAWAIT_ABI_VERSION);
83              
84 2 50         register_future_asyncawait_hook_func = INT2PTR(void (*)(pTHX_ const struct AsyncAwaitHookFuncs *, void *),
85             SvUV(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/register()@1", 0)));
86 2           }
87              
88             /**************
89             * Legacy API *
90             **************/
91              
92             /*
93             * This enum provides values for the `phase` hook parameter.
94             */
95             enum {
96             /* PRESUSPEND = 0x10, */
97             FAA_PHASE_POSTSUSPEND = 0x20,
98             FAA_PHASE_PRERESUME = 0x30,
99             /* POSTRESUME = 0x40, */
100             FAA_PHASE_FREE = 0xFF,
101             };
102              
103             /*
104             * The type of suspend hook functions.
105             *
106             * `phase` indicates the point in the suspend/resume lifecycle, as one of
107             * the values of the enum above.
108             * `cv` points to the CV being suspended or resumed. This will be after it
109             * has been cloned, if necessary.
110             * `modhookdata` points to an HV associated with the CV state, and may be
111             * used by modules as a scratchpad to store extra data relating to this
112             * function. Callers should prefix keys with their own module name to
113             * avoid collisions.
114             */
115             typedef void SuspendHookFunc(pTHX_ U8 phase, CV *cv, HV *modhookdata);
116              
117             /*
118             * Callers should use this function-like macro to set the value of the hook
119             * function variable, by passing in the address of a new function and a pointer
120             * to a variable to capture the previous value.
121             *
122             * static SuspendHookFunc *oldhook;
123             *
124             * future_asyncawait_wrap_suspendhook(&my_hook_func, &oldhook);
125             *
126             * The hook function itself should remember to chain to the oldhook function,
127             * whose value will never be NULL;
128             *
129             * void my_hook_func(aTHX_ U8 phase, CV *cv, HV *modhookdata)
130             * {
131             * ...
132             * (*oldhook)(phase, cv, modhookdata);
133             * }
134             */
135              
136             static void S_null_suspendhook(pTHX_ U8 phase, CV *cv, HV *modhookdata)
137             {
138             /* empty */
139             }
140              
141             #ifndef OP_CHECK_MUTEX_LOCK /* < 5.15.8 */
142             # define OP_CHECK_MUTEX_LOCK ((void)0)
143             # define OP_CHECK_MUTEX_UNLOCK ((void)0)
144             #endif
145              
146             #define future_asyncawait_wrap_suspendhook(newfunc, oldhookp) S_future_asyncawait_wrap_suspendhook(aTHX_ newfunc, oldhookp)
147             static void S_future_asyncawait_wrap_suspendhook(pTHX_ SuspendHookFunc *newfunc, SuspendHookFunc **oldhookp)
148             {
149             if(*oldhookp)
150             return;
151              
152             /* Rather than define our own mutex for this very-rare usecase, we'll just
153             * abuse core's opcheck mutex for it. At worst this leads to thread
154             * contention at module load time for this very quick test
155             */
156             OP_CHECK_MUTEX_LOCK;
157              
158             if(!*oldhookp) {
159             SV **hookp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/suspendhook", TRUE);
160             if(hookp && SvOK(*hookp))
161             *oldhookp = INT2PTR(SuspendHookFunc *, SvUV(*hookp));
162             else
163             *oldhookp = &S_null_suspendhook;
164              
165             sv_setuv(*hookp, PTR2UV(newfunc));
166             }
167              
168             OP_CHECK_MUTEX_UNLOCK;
169             }
170              
171             #endif