diff --git a/lib/context.h b/lib/context.h index 9783678b..329903e7 100644 --- a/lib/context.h +++ b/lib/context.h @@ -175,6 +175,7 @@ struct exec_context { /* check_interrupt() */ const atomic_uint *intrp; struct cluster *cluster; + uint32_t check_interval; /* scheduler */ struct sched *sched; diff --git a/lib/exec.c b/lib/exec.c index 4574c9c5..668968b4 100644 --- a/lib/exec.c +++ b/lib/exec.c @@ -886,8 +886,6 @@ restart_insn(struct exec_context *ctx) #endif } -#define CHECK_INTERVAL 1000 - int check_interrupt(struct exec_context *ctx) { @@ -938,6 +936,34 @@ check_interrupt(struct exec_context *ctx) return 0; } +#define CHECK_INTERVAL_MAX UINT32_MAX +#define CHECK_INTERVAL_DEFAULT 1000 +#define CHECK_INTERVAL_MIN 1 + +static void +adjust_check_interval(struct exec_context *ctx, const struct timespec *now, + const struct timespec *last) +{ + struct timespec diff; + timespec_sub(now, last, &diff); + uint64_t diff_ms = timespec_to_ms(&diff); + uint32_t check_interval = ctx->check_interval; + if (diff_ms < CHECK_INTERRUPT_INTERVAL_MS / 2) { + if (check_interval <= CHECK_INTERVAL_MAX / 2) { + check_interval *= 2; + } else { + check_interval = CHECK_INTERVAL_MAX; + } + } else if (diff_ms / 2 > CHECK_INTERRUPT_INTERVAL_MS) { + check_interval /= 2; + if (check_interval < CHECK_INTERVAL_MIN) { + check_interval = CHECK_INTERVAL_MIN; + } + } + xlog_trace("check_interval %" PRIu32, check_interval); + ctx->check_interval = check_interval; +} + int exec_expr(uint32_t funcidx, const struct expr *expr, const struct localtype *localtype, @@ -965,7 +991,10 @@ exec_expr(uint32_t funcidx, const struct expr *expr, int exec_expr_continue(struct exec_context *ctx) { - uint32_t n = 0; + struct timespec last; + bool has_last = false; + uint32_t n = ctx->check_interval; + assert(n > 0); while (true) { int ret; switch (ctx->event) { @@ -1029,8 +1058,8 @@ exec_expr_continue(struct exec_context *ctx) if (ctx->frames.lsize == 0) { break; } - n++; - if (__predict_false(n > CHECK_INTERVAL)) { + n--; + if (__predict_false(n == 0)) { n = 0; ret = check_interrupt(ctx); if (ret != 0) { @@ -1039,6 +1068,18 @@ exec_expr_continue(struct exec_context *ctx) } return ret; } + struct timespec now; + ret = timespec_now(CLOCK_MONOTONIC, &now); + if (ret != 0) { + return ret; + } + if (has_last) { + adjust_check_interval(ctx, &now, &last); + } + last = now; + has_last = true; + n = ctx->check_interval; + assert(n > 0); } struct cell *stack = &VEC_NEXTELEM(ctx->stack); ret = fetch_exec_next_insn(ctx->p, stack, ctx); @@ -1261,6 +1302,7 @@ exec_context_init(struct exec_context *ctx, struct instance *inst) report_init(&ctx->report0); ctx->report = &ctx->report0; ctx->restart_type = RESTART_NONE; + ctx->check_interval = CHECK_INTERVAL_DEFAULT; exec_options_set_defaults(&ctx->options); } diff --git a/lib/timeutil.c b/lib/timeutil.c index bfb8fdab..52c7a048 100644 --- a/lib/timeutil.c +++ b/lib/timeutil.c @@ -217,3 +217,17 @@ convert_timespec(clockid_t from_id, clockid_t to_id, fail: return ret; } + +uint64_t +timespec_to_ms(const struct timespec *tv) +{ + if (UINT64_MAX / 1000 < tv->tv_sec) { + return UINT64_MAX; + } + uint64_t ms1 = tv->tv_sec * 1000; + uint64_t ms2 = tv->tv_nsec / 1000000; + if (UINT64_MAX - ms1 < ms2) { + return UINT64_MAX; + } + return ms1 + ms2; +} diff --git a/lib/timeutil.h b/lib/timeutil.h index a08fb7f3..436b56cb 100644 --- a/lib/timeutil.h +++ b/lib/timeutil.h @@ -20,3 +20,5 @@ int abstime_to_reltime_ms_roundup(clockid_t id, const struct timespec *abstime, int *reltime_ms); int convert_timespec(clockid_t from_id, clockid_t to_id, const struct timespec *from_ts, struct timespec *result); + +uint64_t timespec_to_ms(const struct timespec *tv);