engines/libblkio: Add option libblkio_wait_mode
[fio.git] / helper_thread.c
index f6cdddb8f0532af5be1a0463bb7d9fadf694500f..b9b83db3057435f6fd2918d84cc3e96bdb7c4252 100644 (file)
@@ -1,11 +1,18 @@
 #include <signal.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"
@@ -13,6 +20,7 @@
 #include "pshared.h"
 
 static int sleep_accuracy_ms;
+static int timerfd = -1;
 
 enum action {
        A_EXIT          = 1,
@@ -179,6 +187,7 @@ static uint8_t wait_for_action(int fd, unsigned int timeout_ms)
        };
        fd_set rfds, efds;
        uint8_t action = 0;
+       uint64_t exp;
        int res;
 
        res = read_from_pipe(fd, &action, sizeof(action));
@@ -188,7 +197,25 @@ static uint8_t wait_for_action(int fd, unsigned int timeout_ms)
        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));
@@ -196,6 +223,10 @@ static uint8_t wait_for_action(int fd, unsigned int timeout_ms)
        }
        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;
 }
 
@@ -256,29 +287,28 @@ static void *helper_thread_main(void *data)
                }
        };
        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);
 
@@ -296,9 +326,9 @@ static void *helper_thread_main(void *data)
 
                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)
@@ -317,6 +347,11 @@ static void *helper_thread_main(void *data)
                        print_thread_status();
        }
 
+       if (timerfd >= 0) {
+               close(timerfd);
+               timerfd = -1;
+       }
+
        fio_writeout_logs(false);
 
        sk_out_drop();