/* You may distribute under the terms of either the GNU General Public License
* or the Artistic License (the same terms as Perl itself)
*
* (C) Paul Evans, 2022 -- leonerd@leonerd.org.uk
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "AsyncAwait.h"
static SV *S_call_metrics_method(pTHX_ U8 gimme, SV *metrics, const char *method, SV *arg1, SV *arg2)
{
dSP;
ENTER;
SAVETMPS;
EXTEND(SP, 3);
PUSHMARK(SP);
PUSHs(metrics);
mPUSHs(arg1);
if(arg2)
mPUSHs(arg2);
PUTBACK;
call_method(method, gimme);
SV *ret;
if(gimme > G_VOID) {
SPAGAIN;
ret = POPs;
SvREFCNT_inc(ret);
PUTBACK;
}
FREETMPS;
LEAVE;
return ret;
}
#define call_metrics_method_pvn(gimme, metrics, method, pv, len) \
S_call_metrics_method(aTHX_ gimme, metrics, method, newSVpvn(pv, len), NULL)
#define call_metrics_method_pvn_iv(gimme, metrics, method, pv, len, iv) \
S_call_metrics_method(aTHX_ gimme, metrics, method, newSVpvn(pv, len), newSViv(iv))
struct FAAMetricsState
{
SV *metrics;
bool use_batch_mode;
UV states_created_counter;
UV suspends_counter;
UV resumes_counter;
UV states_destroyed_counter;
IV current_states_gauge;
IV current_subs_gauge;
};
XS_INTERNAL(flush_metrics);
static struct FAAMetricsState *S_get_state(pTHX)
{
struct FAAMetricsState *state;
SV **svp = hv_fetchs(PL_modglobal, "Future::AsyncAwait::Metrics/state", GV_ADD);
if(SvOK(*svp))
state = INT2PTR(struct FAAMetricsState *, SvUV(*svp));
else {
Newx(state, 1, struct FAAMetricsState);
sv_setuv(*svp, PTR2UV(state));
state->metrics = get_sv("Future::AsyncAwait::Metrics::metrics", 0);
SV *r = S_call_metrics_method(aTHX_ G_SCALAR, state->metrics,
"add_batch_mode_callback",
newRV_noinc((SV *)newXS_flags("flush_metrics", flush_metrics, __FILE__, NULL, 0)),
NULL
);
if(r && SvTRUE(r)) {
state->use_batch_mode = TRUE;
state->states_created_counter = 0;
state->suspends_counter = 0;
state->resumes_counter = 0;
state->states_destroyed_counter = 0;
state->current_states_gauge = 0;
state->current_subs_gauge = 0;
}
else
state->use_batch_mode = FALSE;
}
return state;
}
#define get_state() S_get_state(aTHX)
XS_INTERNAL(flush_metrics)
{
struct FAAMetricsState *state = get_state();
#define FLUSH_COUNTER(name) \
if(state->name##_counter) { \
call_metrics_method_pvn_iv(G_VOID, state->metrics, "inc_counter_by", \
"" #name "", sizeof(#name)-1, state->name##_counter); \
state->name##_counter = 0; \
}
FLUSH_COUNTER(states_created);
FLUSH_COUNTER(suspends);
FLUSH_COUNTER(resumes);
FLUSH_COUNTER(states_destroyed);
#define FLUSH_GAUGE(name) \
if(state->name##_gauge) { \
call_metrics_method_pvn_iv(G_VOID, state->metrics, "inc_gauge_by", \
"" #name "", sizeof(#name)-1, state->name##_gauge); \
state->name##_gauge = 0; \
}
FLUSH_GAUGE(current_states);
FLUSH_GAUGE(current_subs);
}
#define INC_COUNTER(name) \
if(state->use_batch_mode) \
state->name##_counter++; \
else \
call_metrics_method_pvn(G_VOID, state->metrics, "inc_counter", "" #name "", sizeof(#name)-1)
#define INC_GAUGE(name) \
if(state->use_batch_mode) \
state->name##_gauge++; \
else \
call_metrics_method_pvn(G_VOID, state->metrics, "inc_gauge", "" #name "", sizeof(#name)-1)
#define DEC_GAUGE(name) \
if(state->use_batch_mode) \
state->name##_gauge--; \
else \
call_metrics_method_pvn(G_VOID, state->metrics, "dec_gauge", "" #name "", sizeof(#name)-1)
static void hook_post_cvcopy(pTHX_ CV *runcv, CV *cv, HV *modhookdata, void *hookdata)
{
struct FAAMetricsState *state = get_state();
INC_COUNTER(states_created);
INC_GAUGE(current_states);
}
static void hook_post_suspend(pTHX_ CV *cv, HV *modhookdata, void *hookdata)
{
struct FAAMetricsState *state = get_state();
INC_COUNTER(suspends);
INC_GAUGE(current_subs);
}
static void hook_pre_resume(pTHX_ CV *cv, HV *modhookdata, void *hookdata)
{
struct FAAMetricsState *state = get_state();
INC_COUNTER(resumes);
DEC_GAUGE(current_subs);
}
static void hook_free(pTHX_ CV *cv, HV *modhookdata, void *hookdata)
{
struct FAAMetricsState *state = get_state();
INC_COUNTER(states_destroyed);
DEC_GAUGE(current_states);
}
static const struct AsyncAwaitHookFuncs hooks = {
.post_cv_copy = &hook_post_cvcopy,
.post_suspend = &hook_post_suspend,
.pre_resume = &hook_pre_resume,
.free = &hook_free,
};
MODULE = Future::AsyncAwait::Metrics PACKAGE = Future::AsyncAwait::Metrics
BOOT:
boot_future_asyncawait(0.60);
register_future_asyncawait_hook(&hooks, NULL);