Compare commits

...
7 Commits
Author SHA1 Message Date
bol-van 7a69d56a4b update docs 2026-04-30 17:00:44 +03:00
bol-van b3be87fd7c update docs 2026-04-30 16:58:18 +03:00
bol-van 62b86c3899 zapret-tests: show current time in timer test 2026-04-30 16:44:28 +03:00
bol-van 7db50c018a nfqws2: timer rework. remove timer-res 2026-04-30 13:12:56 +03:00
bol-van eb35bb34f5 update docs 2026-04-29 10:22:25 +03:00
bol-van d0c9fa9b76 copypaste fixes 2026-04-29 08:36:43 +03:00
bol-van 7441ed7119 update LICENSE year 2026-04-28 13:41:44 +03:00
12 changed files with 145 additions and 121 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016-2025 bol-van
Copyright (c) 2016-2026 bol-van
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -11
View File
@@ -653,7 +653,6 @@ General parameters for all versions - nfqws2, dvtws2, winws2.
--ipcache-lifetime=<int> ; IP cache entry lifetime in seconds. 0 - unlimited.
--ipcache-hostname=[0|1] ; 1 or no argument enables hostname caching for use in zero-phase strategies
--reasm-disable=[type[,type]] ; disable fragment reassembly for a list of payloads: tls_client_hello quic_initial. without arguments - disable reasm for everything.
--timer-res=msec ; timer resolution in msec. default - 50
DESYNC ENGINE INIT:
--writeable[=<dir_name>] ; create a directory for Lua with write permissions and store its path in the "WRITEABLE" env variable (only one directory)
@@ -1607,15 +1606,6 @@ nfqws2 is a single-threaded program, like the Lua engine. Timers are called in t
In Linux and Windows, packets are received in blocks, not one at a time. A block is processed until all remaining packets are exhausted.
Timers are called between block processing. If processing takes a significant amount of time, the timer call may not be perfectly timed.
It's important to remember the timer resolution specified with the `--timer-res` parameter. The default is 50 ms.
This means that the timer call check can be performed no more frequently than the specified interval.
If you set the period to 70 ms, the timer will be called staggered. +70 will be called at 100, +140 at 150, and +210 at 250.
Therefore, it's advisable to set the period to a multiple of the timer resolution.
Increasing the resolution (decreasing `--timer-res`) may slightly increase CPU load,
since the processor will wake up every specified period to check all timers.
The more timers, the higher CPU load for checking.
The maximum resolution is 100 checks per second, which corresponds to `--timer-res=10`.
Timers can be useful for handling unreplied packets.
For example, you need to send something somewhere and read the response. But the other party might not respond or the response might not arrive.
To prevent your system from hanging in an undefined state and leaving garbage in memory, a timer can help.
@@ -2434,7 +2424,7 @@ function timer_set(name, func, period, oneshot, data)
* name - a unique timer name. If a timer with this name already exists, it is deleted and replaced with a new one. When replaced, the countdown restarts.
* func - the timer function name (string)
* period - the timer call frequency in milliseconds (see [timer resolution](#timers))
* period - the timer call frequency in milliseconds
* oneshot - a bool indicating whether the timer is single-shot (true) or periodic (false)
* data - an arbitrary variable passed to the [timer-function](#timer-function-prototype)
+1 -11
View File
@@ -712,7 +712,6 @@ nfqws2 использует стандартный парсер getopt_long_only
--ipcache-lifetime=<int> ; время жизни записей кэша IP в секундах. 0 - без ограничений.
--ipcache-hostname=[0|1] ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы
--reasm-disable=[type[,type]] ; отключить сборку фрагментов для списка пейлоадов : tls_client_hello quic_initial . без аргумента - отключить reasm для всего.
--timer-res=msec ; разрешение таймеров в мс. по умолчанию - 50
DESYNC ENGINE INIT:
--writeable[=<dir_name>] ; создать директорию для Lua с разрешением записи и поместить путь к ней в переменную env "WRITEABLE" (только одна директория)
@@ -1753,15 +1752,6 @@ nfqws2 - программа однопоточная, как и движок Lua
Таймеры вызываются между обработкой блоков. Если обработка заняла достаточно много времени, то вызов таймера может быть
не идеальным по времени.
Следует помнить о разрешении таймеров, которое задается в параметре `--timer-res`. По умолчанию - 50 мсек.
Это значит, что проверка необходимости вызова таймера может проводиться не чаще указанного времени.
Если вы сделаете period 70 мсек, таймер будет вызываться "рвано". +70 будет вызван при 100, +140 - при 150, +210 - при 250.
Поэтому период желательно делать кратным разрешению таймеров.
Увеличение разрешения (снижение `--timer-res`) может привести к некоторому увеличению нагрузки на CPU,
поскольку процессор будет просыпаться через каждый указанный период времени для проверки всех таймеров.
Чем больше таймеров, тем выше нагрузка на CPU для проверки.
Максимальное разрешение - 100 проверок в секунду, что соответствует `--timer-res=10`.
Таймеры могут быть полезны для обработки ситуаций отсутствия реакции из сети на отсылаемые пакеты.
Например, вам надо что-то куда-то послать и прочитать ответ. Но оппонент может и не ответить или ответ может не дойти.
Чтобы ваша схема не подвисла в неопределенном состоянии и не оставила мусор в памяти, может помочь таймер.
@@ -2594,7 +2584,7 @@ function timer_set(name, func, period, oneshot, data)
* name - уникальное имя таймера. если таймер с таким именем уже есть, он удаляется и замещается новым. При замещении отсчет времени начинается заново.
* func - имя таймер функции (string)
* period - периодичность вызова таймера в миллисекундах (см. [разрешение таймеров](#таймеры))
* period - периодичность вызова таймера в миллисекундах
* oneshot - bool признак является ли таймер однократным (true) или периодическим (false)
* data - произвольная переменная, передаваемая [таймер-функции](#прототип-функции-таймера)
+8
View File
@@ -907,6 +907,11 @@ function test_ifaddrs(opts)
end
end
function print_current_time()
local sec,nsec = clock_gettime()
local t = sec + nsec/1000000000;
print("time: "..t)
end
function timer_info_print(tinfo)
print(" timer_info.name="..tinfo.name)
print(" timer_info.func="..tinfo.func)
@@ -921,11 +926,13 @@ end
function timer1(name, data)
print("timer "..name.." fired. data="..tostring(data))
print_current_time()
timer_info_print_by_name(name)
end
function timer2(name, data)
data.n = data.n+1
print("timer "..name.." fired. data.n="..tostring(data.n))
print_current_time()
timer_info_print_by_name(name)
if data.n>=4 then
timer_del(name)
@@ -942,6 +949,7 @@ function test_timer(opts)
print("TIMER "..i.." :")
timer_info_print_by_name(name)
end
print_current_time()
print()
end
+39 -24
View File
@@ -20,6 +20,8 @@
#include "nfqws.h"
#ifdef __CYGWIN__
#include "timer.h"
#include <sys/cygwin.h>
#include <wlanapi.h>
@@ -1627,32 +1629,34 @@ static bool windivert_recv_exit(void)
}
return false;
}
static DWORD win_timer_check(t_timer_callback timer_callback, uint64_t *bt_prev)
{
uint64_t bt,dbt;
bt = boottime_ms();
dbt = bt-*bt_prev;
if (dbt>=params.timer_res)
{
timer_callback(bt);
*bt_prev = bt;
return params.timer_res;
}
else
return params.timer_res-(int)dbt;
}
static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev)
static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, uint64_t *bt_next)
{
UINT recv_len;
DWORD rd,twait,tmax;
unsigned int wac;
uint64_t bt;
if (windivert_recv_exit()) return false;
if (windivert_recv_exit())
return false;
tmax = win_timer_check(timer_callback, bt_prev);
if (params.timers)
{
if (!*bt_next) *bt_next = TimerPoolNext(params.timers, &params.timers_dirty);
bt = boottime_ms();
tmax = *bt_next>bt ? (DWORD)(*bt_next-bt) : 0;
if (!tmax || params.timers_dirty)
{
*bt_next = TimerPoolRun(&params.timers, &params.timers_dirty, bt);
bt = boottime_ms();
tmax = *bt_next>bt ? (DWORD)(*bt_next-bt) : 0;
}
}
else
{
*bt_next = 0;
tmax = 0;
}
wac = *wa_count * sizeof(WINDIVERT_ADDRESS);
if (WinDivertRecvEx(hFilter, packet, *len, &recv_len, 0, wa, &wac, &ovl))
@@ -1669,13 +1673,24 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len,
// need to check for signals periodically
for(;;)
{
twait = tmax>50 ? 50 : tmax;
tmax -= twait;
if (params.timers)
{
twait = tmax>50 ? 50 : tmax;
tmax -= twait;
}
else
twait = 50;
// make signals working
if (WaitForSingleObject(ovl.hEvent,twait)!=WAIT_TIMEOUT) break;
if (windivert_recv_exit())
return false;
if (!tmax) tmax = win_timer_check(timer_callback, bt_prev);
if (params.timers && !tmax)
{
bt = boottime_ms();
*bt_next = TimerPoolRun(&params.timers, &params.timers_dirty, bt);
bt = boottime_ms();
tmax = *bt_next>bt ? (DWORD)(*bt_next-bt) : 0;
}
}
if (!GetOverlappedResult(hFilter,&ovl,&rd,FALSE))
{
@@ -1700,9 +1715,9 @@ cancel:
GetOverlappedResult(hFilter, &ovl, &rd, TRUE);
return false;
}
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev)
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, uint64_t *bt_next)
{
return windivert_recv_filter(w_filter,packet,len,wa,wa_count,timer_callback,bt_prev);
return windivert_recv_filter(w_filter,packet,len,wa,wa_count,bt_next);
}
static bool windivert_send_filter(HANDLE hFilter, const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa)
+1 -2
View File
@@ -135,8 +135,7 @@ bool logical_net_filter_present(void);
bool logical_net_filter_match(void);
bool nlm_list(bool bAll);
bool windivert_init(const char *filter);
typedef void (*t_timer_callback)(uint64_t bt);
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev);
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, uint64_t *bt_next);
bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa);
#else
#define ensure_dir_access(dir) ensure_file_access(dir)
+4 -2
View File
@@ -3825,6 +3825,7 @@ static int luacall_timer_set(lua_State *L)
timer->lua_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
DLOG("timer: '%s' %s. function '%s' period %llu oneshot %u\n", timer->str, action, timer->func, timer->period, timer->oneshot);
params.timers_dirty = true;
LUA_STACK_GUARD_RETURN(L,0)
}
@@ -3841,13 +3842,14 @@ static int luacall_timer_del(lua_State *L)
{
DLOG("timer: '%s' deleted\n", timer->str);
TimerPoolDel(&params.timers, timer);
params.timers_dirty = true;
}
else
DLOG("timer: '%s' not found\n", name);
lua_pushboolean(L, !!timer);
LUA_STACK_GUARD_RETURN(L,1)
}
static int lua_push_timer_info(lua_State *L, const timer_pool *timer)
static void lua_push_timer_info(lua_State *L, const timer_pool *timer)
{
lua_newtable(L);
if (timer->str) lua_pushf_str(L, "name", timer->str);
@@ -3858,7 +3860,7 @@ static int lua_push_timer_info(lua_State *L, const timer_pool *timer)
}
static int luacall_timer_info(lua_State *L)
{
// timer_del(name)
// timer_info(name)
lua_check_argc(L,"timer_info",1);
LUA_STACK_GUARD_ENTER(L)
+49 -60
View File
@@ -242,20 +242,27 @@ static int write_pidfile(FILE **Fpid)
void NoInterceptLoop(void)
{
useconds_t usec = params.timer_res * 1000;
uint64_t bt, bt_next;
useconds_t usec;
if (params.timers)
{
DLOG("processing timers\n");
bt_next = TimerPoolNext(params.timers, &params.timers_dirty);
while(params.timers)
{
if (bQuit) goto quit;
usleep(usec);
if (bQuit) goto quit;
bt = boottime_ms();
if (bt_next>bt)
{
usec = (useconds_t)(bt_next-bt)*1000U;
usleep(usec);
if (bQuit) goto quit;
}
ReloadCheck();
lua_do_gc();
TimerPoolRun(&params.timers, 0);
bt_next = TimerPoolRun(&params.timers, &params.timers_dirty, 0);
}
}
return;
@@ -442,8 +449,8 @@ static int nfq_main(void)
uint8_t *buf=NULL, *mod=NULL;
struct nfq_cb_data cbdata = { .sock = -1, .mod = NULL };
fd_set fdset;
struct timeval tv = {.tv_sec = params.timer_res/1000, .tv_usec = params.timer_res%1000*1000};
unsigned int bt,bt_prev,dbt;
struct timeval tv;
uint64_t bt,dbt,bt_next;
if (*params.pidfile && !(Fpid = fopen(params.pidfile, "w")))
{
@@ -519,7 +526,7 @@ static int nfq_main(void)
notify_ready();
fd = nfq_fd(h);
bt_prev=0;
bt_next = bt = 0;
do
{
if (bQuit) goto quit;
@@ -527,6 +534,12 @@ static int nfq_main(void)
{
if (params.timers)
{
if (!bt_next) bt_next = TimerPoolNext(params.timers, &params.timers_dirty);
bt = boottime_ms();
dbt = bt_next>bt ? bt_next-bt : 0;
tv.tv_sec = (time_t)(dbt/1000);
tv.tv_usec = (suseconds_t)(dbt%1000*1000);
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
res = select(fd+1, &fdset, NULL, NULL, &tv);
@@ -540,6 +553,7 @@ static int nfq_main(void)
}
else
{
bt_next = bt = 0;
if (bQuit) goto quit;
res = 1;
}
@@ -566,21 +580,10 @@ static int nfq_main(void)
if (params.timers)
{
bt = boottime_ms();
dbt = bt-bt_prev;
if (dbt>=params.timer_res)
{
TimerPoolRun(&params.timers, bt);
bt_prev = bt;
tv.tv_sec = params.timer_res/1000;
tv.tv_usec = params.timer_res%1000*1000;
}
else
{
dbt = params.timer_res-dbt;
tv.tv_sec = (time_t)(dbt/1000);
tv.tv_usec = (suseconds_t)(dbt%1000*1000);
}
if (bt>=bt_next)
bt_next = TimerPoolRun(&params.timers, &params.timers_dirty, bt);
else if (params.timers_dirty)
bt_next = 0;
}
}
if (errno==EINTR)
@@ -629,8 +632,8 @@ static int dvt_main(void)
struct sockaddr_in6 bp6;
uint8_t buf[RECONSTRUCT_MAX_SIZE] __attribute__((aligned));
fd_set fdset;
struct timeval tv = {.tv_sec = params.timer_res/1000, .tv_usec = params.timer_res%1000*1000};
unsigned int bt,bt_prev,dbt;
struct timeval tv;
uint64_t bt,bt_next,dbt;
if (*params.pidfile && !(Fpid = fopen(params.pidfile, "w")))
{
@@ -714,16 +717,29 @@ static int dvt_main(void)
if (params.daemon) daemonize();
if (!write_pidfile(&Fpid)) goto exiterr;
for (bt_prev=0;;)
for (bt=bt_next=0;;)
{
if (bQuit)
{
DLOG_CONDUP("quit requested\n");
goto exitok;
}
FD_ZERO(&fdset);
for (i = 0; i < fdct; i++) FD_SET(fd[i], &fdset);
r = select(fdmax, &fdset, NULL, NULL, params.timers ? &tv : NULL);
if (params.timers)
{
if (!bt_next) bt_next = TimerPoolNext(params.timers, &params.timers_dirty);
bt = boottime_ms();
dbt = bt_next>bt ? bt_next-bt : 0;
tv.tv_sec = (time_t)(dbt/1000);
tv.tv_usec = (suseconds_t)(dbt%1000*1000);
r = select(fdmax, &fdset, NULL, NULL, &tv);
}
else
r = select(fdmax, &fdset, NULL, NULL, NULL);
if (bQuit)
{
DLOG_CONDUP("quit requested\n");
@@ -810,21 +826,10 @@ static int dvt_main(void)
if (params.timers)
{
bt = boottime_ms();
dbt = bt-bt_prev;
if (dbt>=params.timer_res)
{
TimerPoolRun(&params.timers, bt);
bt_prev = bt;
tv.tv_sec = params.timer_res/1000;
tv.tv_usec = params.timer_res%1000*1000;
}
else
{
dbt = params.timer_res-dbt;
tv.tv_sec = (time_t)(dbt/1000);
tv.tv_usec = (suseconds_t)(dbt%1000*1000);
}
if (bt>=bt_next)
bt_next = TimerPoolRun(&params.timers, &params.timers_dirty, bt);
else if (params.timers_dirty)
bt_next = 0;
}
}
@@ -846,11 +851,6 @@ exiterr:
// do not make it less than 65536 - loopback packets can be up to 64K
#define WINDIVERT_PACKET_BUF_SIZE 196608 // 3*64K, 128*1500=192000
static void win_timer_callback(uint64_t bt)
{
TimerPoolRun(&params.timers, bt);
}
static int win_main(void)
{
size_t len, packet_len, left, modlen;
@@ -863,7 +863,7 @@ static int win_main(void)
WINDIVERT_ADDRESS wa[WINDIVERT_BULK_MAX];
uint8_t *packets = NULL, *packet, *mod=NULL;
unsigned int n,wa_count;
uint64_t bt_prev = 0;
uint64_t bt_next;
// windows emulated fork logic does not cover objects outside of cygwin world. have to daemonize before inits
if (params.daemon) daemonize();
@@ -933,11 +933,12 @@ static int win_main(void)
goto ex;
}
bt_next = 0;
for (id = 0;;)
{
len = WINDIVERT_PACKET_BUF_SIZE;
wa_count = WINDIVERT_BULK_MAX;
if (!windivert_recv(packets, &len, wa, &wa_count, win_timer_callback, &bt_prev))
if (!windivert_recv(packets, &len, wa, &wa_count, &bt_next))
{
if (errno == ENOBUFS)
{
@@ -1839,7 +1840,6 @@ static void exithelp(void)
" --ipcache-lifetime=<int>\t\t\t\t; time in seconds to keep cached hop count and domain name (default %u). 0 = no expiration\n"
" --ipcache-hostname=[0|1]\t\t\t\t; 1 or no argument enables ip->hostname caching\n"
" --reasm-disable=[type[,type]]\t\t\t\t; disable reasm for these L7 payloads : tls_client_hello quic_initial . if no argument - disable all reasm.\n"
" --timer-res=msec\t\t\t\t\t; Lua timer resolution. default %d ms\n"
#ifdef __CYGWIN__
"\nWINDIVERT FILTER:\n"
" --wf-iface=<int>[.<int>]\t\t\t\t; numeric network interface and subinterface indexes\n"
@@ -1915,7 +1915,6 @@ static void exithelp(void)
#endif
CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN, CTRACK_T_UDP,
IPCACHE_LIFETIME,
TIMER_RES_DEFAULT,
LUA_GC_INTERVAL,
all_protos,
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT,
@@ -1998,7 +1997,6 @@ enum opt_indices {
IDX_IPCACHE_LIFETIME,
IDX_IPCACHE_HOSTNAME,
IDX_REASM_DISABLE,
IDX_TIMER_RES,
#ifdef __linux__
IDX_FWMARK,
#elif defined(SO_USER_COOKIE)
@@ -2104,7 +2102,6 @@ static const struct option long_options[] = {
[IDX_IPCACHE_LIFETIME] = {"ipcache-lifetime", required_argument, 0, 0},
[IDX_IPCACHE_HOSTNAME] = {"ipcache-hostname", optional_argument, 0, 0},
[IDX_REASM_DISABLE] = {"reasm-disable", optional_argument, 0, 0},
[IDX_TIMER_RES] = {"timer-res", required_argument, 0, 0},
#ifdef __linux__
[IDX_FWMARK] = {"fwmark", required_argument, 0, 0},
#elif defined(SO_USER_COOKIE)
@@ -2507,14 +2504,6 @@ int main(int argc, char **argv)
else
params.reasm_payload_disable = L7P_ALL;
break;
case IDX_TIMER_RES:
params.timer_res = atoi(optarg);
if (params.timer_res<10)
{
DLOG_ERR("Invalid timer resolution. must be >=10 ms\n");
exit_clean(1);
}
break;
#if defined(__linux__)
case IDX_FWMARK:
#elif defined(SO_USER_COOKIE)
-1
View File
@@ -588,7 +588,6 @@ void init_params(struct params_s *params)
params->ctrack_t_fin = CTRACK_T_FIN;
params->ctrack_t_udp = CTRACK_T_UDP;
params->ipcache_lifetime = IPCACHE_LIFETIME;
params->timer_res = TIMER_RES_DEFAULT;
params->lua_gc = LUA_GC_INTERVAL;
LIST_INIT(&params->hostlists);
+1 -3
View File
@@ -37,8 +37,6 @@
#define IPCACHE_LIFETIME 7200
#define TIMER_RES_DEFAULT 50
#define MAX_GIDS 64
#define MAX_BLOB_SIZE (16*1024)
@@ -195,8 +193,8 @@ struct params_s
bool writeable_dir_enable;
char writeable_dir[PATH_MAX];
int timer_res; // timer resolution in msec
timer_pool *timers;
bool timers_dirty; // changed something in timers
int lua_gc;
int ref_desync_ctx; // desync ctx userdata registry ref
+37 -5
View File
@@ -77,22 +77,40 @@ static bool TimerPoolRunTimer(timer_pool *p)
}
return true;
}
void TimerPoolRun(timer_pool **pp, uint64_t bt)
uint64_t TimerPoolNext(const timer_pool *p, bool *dirty)
{
if (!pp) return; // no timers
const timer_pool *elem, *tmp;
uint64_t nexttime, mintime=0x7FFFFFFFFFFFFFFF;
if (!bt) bt = boottime_ms();
if (!p) return 0;
HASH_ITER(hh, p, elem, tmp)
{
nexttime = elem->bt_prev + elem->period;
if (nexttime<mintime) mintime = nexttime;
}
*dirty = false;
return mintime;
}
uint64_t TimerPoolRun(timer_pool **pp, bool *dirty, uint64_t bt)
{
*dirty = false;
if (!*pp) return 0; // no timers
timer_pool *elem, *tmp, *p;
char *name;
const char *del;
unsigned int n;
uint64_t nexttime, mintime=0x7FFFFFFFFFFFFFFF;
if (!bt) bt = boottime_ms();
HASH_ITER(hh, *pp, elem, tmp)
{
if (bt >= (elem->bt_prev + elem->period))
nexttime = elem->bt_prev + elem->period;
if (bt >= nexttime)
{
if (!(name = strdup(elem->str)))
return;
return 0;
n = elem->n;
del = NULL;
@@ -110,12 +128,26 @@ void TimerPoolRun(timer_pool **pp, uint64_t bt)
{
DLOG(del,name);
TimerPoolDel(pp, elem);
elem = NULL;
}
else
{
elem->bt_prev = bt;
nexttime = elem->bt_prev + elem->period;
}
}
free(name);
}
if (elem)
{
if (nexttime<mintime) mintime = nexttime;
}
}
if (!*pp) return 0; // no timers
if (*dirty)
// something changed in the timer pool (deleted or added in the lua timer code)
// rescan all timers for mintime
mintime = TimerPoolNext(*pp, dirty);
return mintime;
}
+3 -1
View File
@@ -20,4 +20,6 @@ void TimerPoolDestroy(timer_pool **pp);
struct timer_pool *TimerPoolSearch(timer_pool *p, const char *str);
struct timer_pool *TimerPoolAdd(timer_pool **pp, const char *str, const char *func, uint64_t period, bool oneshot);
void TimerPoolDel(timer_pool **pp, timer_pool *p);
void TimerPoolRun(timer_pool **pp, uint64_t bt);
// return next timer fire time
uint64_t TimerPoolRun(timer_pool **pp, bool *dirty, uint64_t bt);
uint64_t TimerPoolNext(const timer_pool *p, bool *dirty);