Commit cc93a683ca for freeswitch.com
commit cc93a683ca6e1f8f47c1d6e38bd2daf8fa6a7b1c
Author: Dmitry Verenitsin <morbit85@gmail.com>
Date: Fri Aug 21 19:10:14 2026 +0500
[core] Fix _timerfd_check using wrong syscall to detect timer expiration. Add unit-tests. (#3129)
`timerfd_gettime()` returns the time remaining until the next periodic tick,
regardless of how many previous ticks have already fired and remain unread.
Replace with `poll()` which correctly checks for pending unread expirations.
diff --git a/src/switch_time.c b/src/switch_time.c
index 4a70b60fca..fef5cfd3d7 100644
--- a/src/switch_time.c
+++ b/src/switch_time.c
@@ -38,6 +38,7 @@
#ifdef HAVE_TIMERFD_CREATE
#include <sys/timerfd.h>
+#include <poll.h>
#endif
//#if defined(DARWIN)
@@ -497,27 +498,27 @@ static switch_status_t _timerfd_next(switch_timer_t *timer)
static switch_status_t _timerfd_check(switch_timer_t *timer, switch_bool_t step)
{
interval_timer_t *it = timer->private_info;
- struct itimerspec val;
- int diff;
+ struct pollfd pfd = { .events = POLLIN };
if (!it) {
return SWITCH_STATUS_GENERR;
}
- timerfd_gettime(it->fd, &val);
- diff = val.it_value.tv_nsec / 1000;
+ pfd.fd = it->fd;
- if (diff > 0) {
- /* still pending */
- timer->diff = diff;
- return SWITCH_STATUS_FALSE;
- } else {
- /* timer pending */
+ if (poll(&pfd, 1, 0) > 0) {
+ /* timer has fired */
timer->diff = 0;
if (step) {
_timerfd_step(timer);
}
+
return SWITCH_STATUS_SUCCESS;
+ } else {
+ /* still pending */
+ timer->diff = 1;
+
+ return SWITCH_STATUS_FALSE;
}
}
diff --git a/tests/unit/.gitignore b/tests/unit/.gitignore
index 966fc80eb9..37a7064a16 100644
--- a/tests/unit/.gitignore
+++ b/tests/unit/.gitignore
@@ -36,6 +36,7 @@ switch_ulp_recover1
switch_ulp_recover2
switch_ulp_recover3
switch_ulp_recover4
+switch_timer
switch_utils
switch_vad
switch_vpx
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index a3c59059b3..cb974ee3a1 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -9,6 +9,7 @@ noinst_PROGRAMS+= switch_hold switch_sip
noinst_PROGRAMS += switch_core_media
noinst_PROGRAMS += test_mod_verto
noinst_PROGRAMS += test_mod_event_socket
+noinst_PROGRAMS += switch_timer
if HAVE_PCAP
noinst_PROGRAMS += switch_rtp_pcap
diff --git a/tests/unit/switch_timer.c b/tests/unit/switch_timer.c
new file mode 100644
index 0000000000..73e1bcb3e1
--- /dev/null
+++ b/tests/unit/switch_timer.c
@@ -0,0 +1,116 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2026, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Dmitry Verenitsin <dmitry.verenitsin@signalwire.com>
+ *
+ *
+ * switch_timer.c -- timer tests
+ *
+ */
+#include <switch.h>
+#include <test/switch_test.h>
+
+static int module_loaded = 0;
+
+FST_MINCORE_BEGIN("./conf")
+
+FST_SUITE_BEGIN(switch_timer)
+
+FST_SETUP_BEGIN()
+{
+ if (!module_loaded) {
+ const char *err = NULL;
+ switch_loadable_module_init(SWITCH_FALSE);
+ switch_loadable_module_load_module("", "CORE_SOFTTIMER_MODULE", SWITCH_TRUE, &err);
+ module_loaded = 1;
+ }
+}
+FST_SETUP_END()
+
+FST_TEARDOWN_BEGIN()
+{
+}
+FST_TEARDOWN_END()
+
+/* timer_check returns FALSE with non-zero diff when no tick has elapsed,
+ * returns SUCCESS with diff=0 after sleeping past the interval */
+FST_TEST_BEGIN(test_timer_check)
+{
+ switch_timer_t timer = { 0 };
+ switch_status_t status;
+
+ fst_requires(switch_core_timer_init(&timer, "soft", 20, 160, fst_pool) == SWITCH_STATUS_SUCCESS);
+
+ /* immediately after init - no tick yet */
+ status = switch_core_timer_check(&timer, SWITCH_FALSE);
+ fst_check(status == SWITCH_STATUS_FALSE);
+ fst_check(timer.diff != 0);
+
+ /* sleep past the 20ms interval */
+ switch_sleep(50000); /* 50ms */
+
+ /* now the tick should be detected */
+ status = switch_core_timer_check(&timer, SWITCH_FALSE);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+ fst_check(timer.diff == 0);
+
+ switch_core_timer_destroy(&timer);
+}
+FST_TEST_END()
+
+/* step=FALSE does not advance tick/samplecount, step=TRUE advances by exactly 1 tick */
+FST_TEST_BEGIN(test_timer_check_step)
+{
+ switch_timer_t timer = { 0 };
+ switch_size_t tick_before;
+ uint32_t samplecount_before;
+ switch_status_t status;
+
+ fst_requires(switch_core_timer_init(&timer, "soft", 20, 160, fst_pool) == SWITCH_STATUS_SUCCESS);
+
+ switch_sleep(50000); /* 50ms - let the timer tick */
+
+ tick_before = timer.tick;
+ samplecount_before = timer.samplecount;
+
+ /* step=FALSE: reports ready but does not advance */
+ status = switch_core_timer_check(&timer, SWITCH_FALSE);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+ fst_check(timer.tick == tick_before);
+ fst_check(timer.samplecount == samplecount_before);
+
+ /* step=TRUE: advances tick by 1, samplecount by samples */
+ status = switch_core_timer_check(&timer, SWITCH_TRUE);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+ fst_check(timer.tick == tick_before + 1);
+ fst_check(timer.samplecount == samplecount_before + 160);
+
+ switch_core_timer_destroy(&timer);
+}
+FST_TEST_END()
+
+FST_SUITE_END()
+
+FST_MINCORE_END()