+#include <errno.h>
#include <signal.h>
+#include <stdio.h>
+#include <string.h>
#include <unistd.h>
+#ifdef CONFIG_HAVE_TIMERFD_CREATE
+#include <sys/timerfd.h>
+#endif
#ifdef CONFIG_VALGRIND_DEV
#include <valgrind/drd.h>
#else
#define DRD_IGNORE_VAR(x) do { } while (0)
#endif
+#ifdef WIN32
+#include "os/os-windows.h"
+#endif
+
#include "fio.h"
#include "smalloc.h"
#include "helper_thread.h"
#include "pshared.h"
static int sleep_accuracy_ms;
+static int timerfd = -1;
enum action {
A_EXIT = 1,
static void block_signals(void)
{
-#ifdef HAVE_PTHREAD_SIGMASK
+#ifdef CONFIG_PTHREAD_SIGMASK
sigset_t sigmask;
+ int ret;
+
ret = pthread_sigmask(SIG_UNBLOCK, NULL, &sigmask);
assert(ret == 0);
ret = pthread_sigmask(SIG_BLOCK, &sigmask, NULL);
- assert(ret == 0);
#endif
}
return;
ret = write_to_pipe(helper_data->pipe[1], &data, sizeof(data));
- assert(ret == 1);
+ if (ret != 1) {
+ log_err("failed to write action into pipe, err %i:%s", errno, strerror(errno));
+ assert(0);
+ }
}
void helper_reset(void)
return;
helper_data->exit = 1;
- submit_action(A_EXIT);
pthread_join(helper_data->thread, NULL);
}
};
fd_set rfds, efds;
uint8_t action = 0;
+ uint64_t exp;
int res;
res = read_from_pipe(fd, &action, sizeof(action));
FD_SET(fd, &rfds);
FD_ZERO(&efds);
FD_SET(fd, &efds);
- res = select(fd + 1, &rfds, NULL, &efds, &timeout);
+#ifdef CONFIG_HAVE_TIMERFD_CREATE
+ {
+ /*
+ * If the timer frequency is 100 Hz, select() will round up
+ * `timeout` to the next multiple of 1 / 100 Hz = 10 ms. Hence
+ * use a high-resolution timer if possible to increase
+ * select() timeout accuracy.
+ */
+ struct itimerspec delta = {};
+
+ delta.it_value.tv_sec = timeout.tv_sec;
+ delta.it_value.tv_nsec = timeout.tv_usec * 1000;
+ res = timerfd_settime(timerfd, 0, &delta, NULL);
+ assert(res == 0);
+ FD_SET(timerfd, &rfds);
+ }
+#endif
+ res = select(max(fd, timerfd) + 1, &rfds, NULL, &efds,
+ timerfd >= 0 ? NULL : &timeout);
if (res < 0) {
log_err("fio: select() call in helper thread failed: %s",
strerror(errno));
}
if (FD_ISSET(fd, &rfds))
read_from_pipe(fd, &action, sizeof(action));
+ if (timerfd >= 0 && FD_ISSET(timerfd, &rfds)) {
+ res = read(timerfd, &exp, sizeof(exp));
+ assert(res == sizeof(exp));
+ }
return action;
}
},
{
.name = "steadystate",
- .interval_ms = steadystate_enabled ? STEADYSTATE_MSEC :
+ .interval_ms = steadystate_enabled ? ss_check_interval :
0,
.func = steadystate_check,
}
};
struct timespec ts;
- int clk_tck, ret = 0;
+ long clk_tck;
+ int ret = 0;
-#ifdef _SC_CLK_TCK
- clk_tck = sysconf(_SC_CLK_TCK);
-#else
- /*
- * The timer frequence is variable on Windows. Instead of trying to
- * query it, use 64 Hz, the clock frequency lower bound. See also
- * https://carpediemsystems.co.uk/2019/07/18/windows-system-timer-granularity/.
- */
- clk_tck = 64;
-#endif
- dprint(FD_HELPERTHREAD, "clk_tck = %d\n", clk_tck);
+ os_clk_tck(&clk_tck);
+
+ dprint(FD_HELPERTHREAD, "clk_tck = %ld\n", clk_tck);
assert(clk_tck > 0);
sleep_accuracy_ms = (1000 + clk_tck - 1) / clk_tck;
+#ifdef CONFIG_HAVE_TIMERFD_CREATE
+ timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
+ assert(timerfd >= 0);
+ sleep_accuracy_ms = 1;
+#endif
+
sk_out_assign(hd->sk_out);
/* Let another thread handle signals. */
block_signals();
fio_get_mono_time(&ts);
- msec_to_next_event = reset_timers(timer, ARRAY_SIZE(timer), &ts);
+ msec_to_next_event = reset_timers(timer, FIO_ARRAY_SIZE(timer), &ts);
fio_sem_up(hd->startup_sem);
if (action == A_RESET)
msec_to_next_event = reset_timers(timer,
- ARRAY_SIZE(timer), &ts);
+ FIO_ARRAY_SIZE(timer), &ts);
- for (i = 0; i < ARRAY_SIZE(timer); ++i)
+ for (i = 0; i < FIO_ARRAY_SIZE(timer); ++i)
ret = eval_timer(&timer[i], &ts, &msec_to_next_event);
if (action == A_DO_STAT)
print_thread_status();
}
+ if (timerfd >= 0) {
+ close(timerfd);
+ timerfd = -1;
+ }
+
fio_writeout_logs(false);
sk_out_drop();