#ifndef __FUTURE_ASYNCAWAIT_H__
#define __FUTURE_ASYNCAWAIT_H__
#include "perl.h"
#define FUTURE_ASYNCAWAIT_ABI_VERSION 2
/*
* The API contained in this file is even more experimental than the rest of
* Future::AsyncAwait. It is primarily designed to allow suspend-aware dynamic
* variables in Syntax::Keyword::Dynamically, but may be useful for other
* tasks.
*
* There are no unit tests for these hooks inside this distribution, as testing
* it would require more XS code. It is tested as a side-effect of the
* integration with Syntax::Keyword::Dynamically.
*/
struct AsyncAwaitHookFuncs
{
U32 flags; /* currently unused but reserve the ABI space just in case */
void (*post_cv_copy)(pTHX_ CV *runcv, CV *cv, HV *modhookdata, void *hookdata);
void (*pre_suspend) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
void (*post_suspend)(pTHX_ CV *cv, HV *modhookdata, void *hookdata);
void (*pre_resume) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
void (*post_resume) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
void (*free) (pTHX_ CV *cv, HV *modhookdata, void *hookdata);
};
static void (*register_future_asyncawait_hook_func)(pTHX_ const struct AsyncAwaitHookFuncs *hookfuncs, void *hookdata);
#define register_future_asyncawait_hook(hookfuncs, hookdata) S_register_future_asyncawait_hook(aTHX_ hookfuncs, hookdata)
static void S_register_future_asyncawait_hook(pTHX_ const struct AsyncAwaitHookFuncs *hookfuncs, void *hookdata)
{
if(!register_future_asyncawait_hook_func)
croak("Must call boot_future_asyncawait() first");
(*register_future_asyncawait_hook_func)(aTHX_ hookfuncs, hookdata);
}
#define future_asyncawait_on_activate(func, data) S_future_asyncawait_on_activate(aTHX_ func, data)
static void S_future_asyncawait_on_activate(pTHX_ void (*func)(pTHX_ void *data), void *data)
{
SV **svp;
if((svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/loaded", FALSE)) && SvOK(*svp)) {
(*func)(aTHX_ data);
}
else {
AV *av;
svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/on_loaded", FALSE);
if(svp)
av = (AV *)*svp;
else {
av = newAV();
hv_stores(PL_modglobal, "Future::AsyncAwait/on_loaded", (SV *)av);
}
av_push(av, newSVuv(PTR2UV(func)));
av_push(av, newSVuv(PTR2UV(data)));
}
}
/* flags constants for future_asyncawait_get_modhookdata() */
enum {
FAA_MODHOOK_CREATE = (1<<0),
};
static HV *(*future_asyncawait_get_modhookdata_func)(pTHX_ CV *cv, U32 flags, PADOFFSET precreate_padix);
#define future_asyncawait_get_modhookdata(cv, flags, precreate_padix) \
S_future_asyncawait_get_modhookdata(aTHX_ cv, flags, precreate_padix)
static HV *S_future_asyncawait_get_modhookdata(pTHX_ CV *cv, U32 flags, PADOFFSET precreate_padix)
{
if(!future_asyncawait_get_modhookdata_func)
croak("Must call boot_future_asyncawait() first");
return (*future_asyncawait_get_modhookdata_func)(aTHX_ cv, flags, precreate_padix);
}
static PADOFFSET (*future_asyncawait_make_precreate_padix_func)(pTHX);
#define future_asyncawait_make_precreate_padix() S_future_asyncawait_make_precreate_padix(aTHX)
PADOFFSET S_future_asyncawait_make_precreate_padix(pTHX)
{
if(!future_asyncawait_make_precreate_padix_func)
croak("Must call boot_future_asyncawait() first");
return (*future_asyncawait_make_precreate_padix_func)(aTHX);
}
#define boot_future_asyncawait(ver) S_boot_future_asyncawait(aTHX_ ver)
static void S_boot_future_asyncawait(pTHX_ double ver)
{
SV **svp;
SV *versv = ver ? newSVnv(ver) : NULL;
load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("Future::AsyncAwait"), versv, NULL);
svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/ABIVERSION_MIN", 0);
if(!svp)
croak("Future::AsyncAwait ABI minimum version missing");
int abi_ver = SvIV(*svp);
if(abi_ver > FUTURE_ASYNCAWAIT_ABI_VERSION)
croak("Future::AsyncAwait ABI version mismatch - library supports >= %d, compiled for %d",
abi_ver, FUTURE_ASYNCAWAIT_ABI_VERSION);
svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/ABIVERSION_MAX", 0);
abi_ver = SvIV(*svp);
if(abi_ver < FUTURE_ASYNCAWAIT_ABI_VERSION)
croak("Future::AsyncAwait ABI version mismatch - library supports <= %d, compiled for %d",
abi_ver, FUTURE_ASYNCAWAIT_ABI_VERSION);
register_future_asyncawait_hook_func = INT2PTR(void (*)(pTHX_ const struct AsyncAwaitHookFuncs *, void *),
SvUV(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/register()@2", 0)));
future_asyncawait_get_modhookdata_func = INT2PTR(HV *(*)(pTHX_ CV *, U32, PADOFFSET),
SvUV(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/get_modhookdata()@1", 0)));
future_asyncawait_make_precreate_padix_func = INT2PTR(PADOFFSET (*)(pTHX),
SvUV(*hv_fetchs(PL_modglobal, "Future::AsyncAwait/make_precreate_padix()@1", 0)));
}
/**************
* Legacy API *
**************/
/*
* This enum provides values for the `phase` hook parameter.
*/
enum {
/* PRESUSPEND = 0x10, */
FAA_PHASE_POSTSUSPEND = 0x20,
FAA_PHASE_PRERESUME = 0x30,
/* POSTRESUME = 0x40, */
FAA_PHASE_FREE = 0xFF,
};
/*
* The type of suspend hook functions.
*
* `phase` indicates the point in the suspend/resume lifecycle, as one of
* the values of the enum above.
* `cv` points to the CV being suspended or resumed. This will be after it
* has been cloned, if necessary.
* `modhookdata` points to an HV associated with the CV state, and may be
* used by modules as a scratchpad to store extra data relating to this
* function. Callers should prefix keys with their own module name to
* avoid collisions.
*/
typedef void SuspendHookFunc(pTHX_ U8 phase, CV *cv, HV *modhookdata);
/*
* Callers should use this function-like macro to set the value of the hook
* function variable, by passing in the address of a new function and a pointer
* to a variable to capture the previous value.
*
* static SuspendHookFunc *oldhook;
*
* future_asyncawait_wrap_suspendhook(&my_hook_func, &oldhook);
*
* The hook function itself should remember to chain to the oldhook function,
* whose value will never be NULL;
*
* void my_hook_func(aTHX_ U8 phase, CV *cv, HV *modhookdata)
* {
* ...
* (*oldhook)(phase, cv, modhookdata);
* }
*/
static void S_null_suspendhook(pTHX_ U8 phase, CV *cv, HV *modhookdata)
{
/* empty */
}
#ifndef OP_CHECK_MUTEX_LOCK /* < 5.15.8 */
# define OP_CHECK_MUTEX_LOCK ((void)0)
# define OP_CHECK_MUTEX_UNLOCK ((void)0)
#endif
#define future_asyncawait_wrap_suspendhook(newfunc, oldhookp) S_future_asyncawait_wrap_suspendhook(aTHX_ newfunc, oldhookp)
static void S_future_asyncawait_wrap_suspendhook(pTHX_ SuspendHookFunc *newfunc, SuspendHookFunc **oldhookp)
{
if(*oldhookp)
return;
warn("future_asyncawait_wrap_suspendhook() is now deprecated; use register_future_asyncawait_hook() instead");
/* Rather than define our own mutex for this very-rare usecase, we'll just
* abuse core's opcheck mutex for it. At worst this leads to thread
* contention at module load time for this very quick test
*/
OP_CHECK_MUTEX_LOCK;
if(!*oldhookp) {
SV **hookp = hv_fetchs(PL_modglobal, "Future::AsyncAwait/suspendhook", TRUE);
if(hookp && SvOK(*hookp))
*oldhookp = INT2PTR(SuspendHookFunc *, SvUV(*hookp));
else
*oldhookp = &S_null_suspendhook;
sv_setuv(*hookp, PTR2UV(newfunc));
}
OP_CHECK_MUTEX_UNLOCK;
}
#endif