summaryrefslogtreecommitdiffhomepage
path: root/compat
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2017-05-01 10:59:19 +0200
committerStanislaw Halik <sthalik@misaki.pl>2017-05-02 23:19:38 +0200
commitaaa3a04577a7d4633ef0eb9eb7dc2aa33db0d0eb (patch)
treed83709d246b390c7279720105f2269fb2b373259 /compat
parent81d11949122c63feb14e6595fc59e49cd264c89e (diff)
compat/timer-resolution: use undocumented windows API
Unlike timeBeginPeriod in winmm, this one sets the timer resolution for the calling process, only.
Diffstat (limited to 'compat')
-rw-r--r--compat/timer-resolution.cpp103
-rw-r--r--compat/timer-resolution.hpp19
2 files changed, 122 insertions, 0 deletions
diff --git a/compat/timer-resolution.cpp b/compat/timer-resolution.cpp
new file mode 100644
index 00000000..597ee91e
--- /dev/null
+++ b/compat/timer-resolution.cpp
@@ -0,0 +1,103 @@
+/* Copyright (c) 2017 Stanislaw Halik <sthalik@misaki.pl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include "timer-resolution.hpp"
+
+#if defined _WIN32
+# include <windows.h>
+
+# include <QLibrary>
+# include <QDebug>
+
+typedef LONG (__stdcall *funptr_NtSetTimerResolution) (ULONG, BOOLEAN, PULONG);
+static funptr_NtSetTimerResolution init_timer_resolution_funptr();
+static funptr_NtSetTimerResolution get_funptr();
+
+static funptr_NtSetTimerResolution init_timer_resolution_funptr()
+{
+ static QLibrary ntdll;
+ ntdll.setLoadHints(QLibrary::PreventUnloadHint);
+ ntdll.setFileName("ntdll.dll");
+ const bool load = ntdll.load();
+ if (!load)
+ {
+ qDebug() << "can't load ntdll:" << ntdll.errorString();
+ return nullptr;
+ }
+
+ auto ret = reinterpret_cast<funptr_NtSetTimerResolution>(ntdll.resolve("NtSetTimerResolution"));
+ if (ret == nullptr)
+ {
+ qDebug() << "can't find NtSetTimerResolution in ntdll:" << ntdll.errorString();
+ return nullptr;
+ }
+
+ return ret;
+}
+
+static funptr_NtSetTimerResolution get_funptr()
+{
+ static auto ret = init_timer_resolution_funptr();
+ return ret;
+}
+
+timer_resolution::timer_resolution(int msecs) : old_value(-1)
+{
+ if (msecs <= 0 || msecs > 30)
+ {
+ qDebug() << "can't set timer resolution to" << msecs << "ms";
+ return;
+ }
+
+ funptr_NtSetTimerResolution f = get_funptr();
+ if (f == nullptr)
+ return;
+
+ // hundredth of a nanosecond
+ const ULONG value = msecs * ULONG(10000);
+ NTSTATUS error = f(value, TRUE, &old_value);
+ ULONG old_value_ = -1;
+
+ if (error != 0)
+ {
+ old_value = -1;
+ qDebug() << "NtSetTimerResolution erred with" << error;
+ return;
+ }
+
+ // see if it stuck
+
+ error = f(value, TRUE, &old_value_);
+ if (error != 0)
+ {
+ old_value = -1;
+ qDebug() << "NtSetTimerResolution check erred with" << error;
+ return;
+ }
+
+ if (old_value_ != old_value)
+ {
+ qDebug() << "NtSetTimerResolution:"
+ << "old value didn't stick"
+ << "current resolution" << old_value_
+ << "* 100 ns";
+ old_value = -1;
+ return;
+ }
+}
+
+timer_resolution::~timer_resolution()
+{
+ if (old_value != ULONG(-1))
+ {
+ funptr_NtSetTimerResolution f = get_funptr();
+ ULONG fuzz = -1;
+ (void) f(old_value, TRUE, &fuzz);
+ }
+}
+
+#endif
diff --git a/compat/timer-resolution.hpp b/compat/timer-resolution.hpp
new file mode 100644
index 00000000..5db877c0
--- /dev/null
+++ b/compat/timer-resolution.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#if defined _WIN32
+# include "export.hpp"
+
+class OTR_COMPAT_EXPORT timer_resolution final
+{
+ unsigned long old_value;
+
+public:
+ timer_resolution(int msecs);
+ ~timer_resolution();
+};
+#else
+struct timer_resolution final
+{
+ inline timer_resolution(int) {}
+};
+#endif