[GTK][WPE] UserInteractive threads are not Real-time in Linux master
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Oct 2021 08:26:42 +0000 (08:26 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Oct 2021 08:26:42 +0000 (08:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=220115

Reviewed by Michael Catanzaro.

Source/WebKit:

Enable real time threads in the web process only when there's a visible page.

* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::createWebPage):
(WebKit::WebProcess::removeWebPage):
(WebKit::WebProcess::pageActivityStateDidChange):
(WebKit::WebProcess::platformInitializeProcess): Deleted.
* WebProcess/glib/WebProcessGLib.cpp:
(WebKit::WebProcess::platformInitializeProcess):
* WebProcess/playstation/WebProcessPlayStation.cpp:
(WebKit::WebProcess::platformInitializeProcess):
* WebProcess/win/WebProcessWin.cpp:
(WebKit::WebProcess::platformInitializeProcess):

Source/WTF:

Add RealTimeThreads singleton to register real time threads.

* wtf/PlatformGTK.cmake:
* wtf/PlatformJSCOnly.cmake:
* wtf/PlatformWPE.cmake:
* wtf/Threading.cpp:
(WTF::Thread::setCurrentThreadIsUserInteractive):
* wtf/generic/WorkQueueGeneric.cpp:
(WTF::WorkQueueBase::platformInitialize):
* wtf/linux/RealTimeThreads.cpp: Added.
(WTF::RealTimeThreads::singleton):
(WTF::RealTimeThreads::RealTimeThreads):
(WTF::RealTimeThreads::registerThread):
(WTF::RealTimeThreads::setEnabled):
(WTF::RealTimeThreads::promoteThreadToRealTime):
(WTF::RealTimeThreads::demoteThreadFromRealTime):
(WTF::RealTimeThreads::demoteAllThreadsFromRealTime):
(WTF::isRunningInSandbox):
(WTF::realTimeKitGetProperty):
(WTF::RealTimeThreads::realTimeKitMakeThreadRealTime):
(WTF::RealTimeThreads::scheduleDiscardRealTimeKitProxy):
(WTF::RealTimeThreads::discardRealTimeKitProxyTimerFired):
* wtf/linux/RealTimeThreads.h: Added.
* wtf/posix/ThreadingPOSIX.cpp:
(WTF::schedPolicy):
(WTF::Thread::establishHandle):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@284525 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/PlatformGTK.cmake
Source/WTF/wtf/PlatformJSCOnly.cmake
Source/WTF/wtf/PlatformWPE.cmake
Source/WTF/wtf/Threading.cpp
Source/WTF/wtf/generic/WorkQueueGeneric.cpp
Source/WTF/wtf/linux/RealTimeThreads.cpp [new file with mode: 0644]
Source/WTF/wtf/linux/RealTimeThreads.h [new file with mode: 0644]
Source/WTF/wtf/posix/ThreadingPOSIX.cpp
Source/WebKit/ChangeLog
Source/WebKit/WebProcess/WebProcess.cpp
Source/WebKit/WebProcess/glib/WebProcessGLib.cpp
Source/WebKit/WebProcess/playstation/WebProcessPlayStation.cpp
Source/WebKit/WebProcess/win/WebProcessWin.cpp

index 8c02afbd5b7b253d53b7d05776dc29df83399e3a..c7c76f9582799552e549670b068ddc1ba1beab84 100644 (file)
@@ -1,3 +1,37 @@
+2021-10-20  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK][WPE] UserInteractive threads are not Real-time in Linux
+        https://bugs.webkit.org/show_bug.cgi?id=220115
+
+        Reviewed by Michael Catanzaro.
+
+        Add RealTimeThreads singleton to register real time threads.
+
+        * wtf/PlatformGTK.cmake:
+        * wtf/PlatformJSCOnly.cmake:
+        * wtf/PlatformWPE.cmake:
+        * wtf/Threading.cpp:
+        (WTF::Thread::setCurrentThreadIsUserInteractive):
+        * wtf/generic/WorkQueueGeneric.cpp:
+        (WTF::WorkQueueBase::platformInitialize):
+        * wtf/linux/RealTimeThreads.cpp: Added.
+        (WTF::RealTimeThreads::singleton):
+        (WTF::RealTimeThreads::RealTimeThreads):
+        (WTF::RealTimeThreads::registerThread):
+        (WTF::RealTimeThreads::setEnabled):
+        (WTF::RealTimeThreads::promoteThreadToRealTime):
+        (WTF::RealTimeThreads::demoteThreadFromRealTime):
+        (WTF::RealTimeThreads::demoteAllThreadsFromRealTime):
+        (WTF::isRunningInSandbox):
+        (WTF::realTimeKitGetProperty):
+        (WTF::RealTimeThreads::realTimeKitMakeThreadRealTime):
+        (WTF::RealTimeThreads::scheduleDiscardRealTimeKitProxy):
+        (WTF::RealTimeThreads::discardRealTimeKitProxyTimerFired):
+        * wtf/linux/RealTimeThreads.h: Added.
+        * wtf/posix/ThreadingPOSIX.cpp:
+        (WTF::schedPolicy):
+        (WTF::Thread::establishHandle):
+
 2021-10-19  Yusuke Suzuki  <ysuzuki@apple.com>
 
         Remove old GCC workaround for Allocator
index 7f6f7722d2b90c6bbbc541ef2b960fca7371a46f..d609129ef1b2899b27a3a43f5271ea360925efec 100644 (file)
@@ -11,6 +11,8 @@ list(APPEND WTF_PUBLIC_HEADERS
     glib/Sandbox.h
     glib/SocketConnection.h
     glib/WTFGType.h
+
+    linux/RealTimeThreads.h
 )
 
 if (CMAKE_SYSTEM_NAME MATCHES "Linux")
@@ -52,6 +54,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
     list(APPEND WTF_SOURCES
         linux/CurrentProcessMemoryStatus.cpp
         linux/MemoryFootprintLinux.cpp
+        linux/RealTimeThreads.cpp
 
         unix/MemoryPressureHandlerUnix.cpp
     )
index e93a285a2d871757815eee26bf5b65d4a39ccae8..2f4fc6b6b9868232e23567ec886962a3cdc461fd 100644 (file)
@@ -99,6 +99,7 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
     list(APPEND WTF_SOURCES
         linux/CurrentProcessMemoryStatus.cpp
         linux/MemoryFootprintLinux.cpp
+        linux/RealTimeThreads.cpp
 
         unix/MemoryPressureHandlerUnix.cpp
     )
index d4d241517a57e3ada4c96402a021b43b41ab6b45..3b47997e5cf9cc4b04e01e1f69d0eb1185789636 100644 (file)
@@ -12,6 +12,7 @@ list(APPEND WTF_PUBLIC_HEADERS
 
     linux/ProcessMemoryFootprint.h
     linux/CurrentProcessMemoryStatus.h
+    linux/RealTimeThreads.h
 )
 
 list(APPEND WTF_SOURCES
@@ -29,6 +30,7 @@ list(APPEND WTF_SOURCES
     glib/URLGLib.cpp
 
     linux/CurrentProcessMemoryStatus.cpp
+    linux/RealTimeThreads.cpp
 
     posix/CPUTimePOSIX.cpp
     posix/OSAllocatorPOSIX.cpp
index d66668b17969e09a809eb6d546fe21354888e081..bfbcba98ab75939b434b430d866e9445cb6e80f1 100644 (file)
 #include <bmalloc/bmalloc.h>
 #endif
 
+#if OS(LINUX)
+#include <wtf/linux/RealTimeThreads.h>
+#endif
+
 namespace WTF {
 
 Lock Thread::s_allThreadsLock;
@@ -330,6 +334,12 @@ void Thread::setCurrentThreadIsUserInteractive(int relativePriority)
     ASSERT(relativePriority <= 0);
     ASSERT(relativePriority >= QOS_MIN_RELATIVE_PRIORITY);
     pthread_set_qos_class_self_np(adjustedQOSClass(QOS_CLASS_USER_INTERACTIVE), relativePriority);
+#elif OS(LINUX)
+    // We don't allow to make the main thread real time. This is used by secondary processes to match the
+    // UI process, but in linux the UI process is not real time.
+    if (!isMainThread())
+        RealTimeThreads::singleton().registerThread(current());
+    UNUSED_PARAM(relativePriority);
 #else
     UNUSED_PARAM(relativePriority);
 #endif
index d997fde73ad4f55580c0a7e3ff02e60c87b1e859..d257d3a7204efb1cb3a5d8954610f2b027a8de51 100644 (file)
@@ -39,14 +39,14 @@ WorkQueueBase::WorkQueueBase(RunLoop& runLoop)
 {
 }
 
-void WorkQueueBase::platformInitialize(const char* name, Type, QOS)
+void WorkQueueBase::platformInitialize(const char* name, Type, QOS qos)
 {
     BinarySemaphore semaphore;
     Thread::create(name, [&] {
         m_runLoop = &RunLoop::current();
         semaphore.signal();
         m_runLoop->run();
-    })->detach();
+    }, ThreadType::Unknown, qos)->detach();
     semaphore.wait();
 }
 
diff --git a/Source/WTF/wtf/linux/RealTimeThreads.cpp b/Source/WTF/wtf/linux/RealTimeThreads.cpp
new file mode 100644 (file)
index 0000000..4cdec67
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <wtf/linux/RealTimeThreads.h>
+
+#include <sched.h>
+#include <signal.h>
+#include <string.h>
+#include <wtf/MainThread.h>
+
+#if USE(GLIB)
+#include <gio/gio.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <wtf/Seconds.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/glib/RunLoopSourcePriority.h>
+#include <wtf/glib/Sandbox.h>
+#endif
+
+#ifndef SCHED_RESET_ON_FORK
+#define SCHED_RESET_ON_FORK 0x40000000
+#endif
+
+namespace WTF {
+
+static const int s_realTimeThreadPriority = 5;
+
+RealTimeThreads& RealTimeThreads::singleton()
+{
+    static LazyNeverDestroyed<RealTimeThreads> realTimeThreads;
+    static std::once_flag onceFlag;
+    std::call_once(onceFlag, [&] {
+        realTimeThreads.construct();
+    });
+    return realTimeThreads;
+}
+
+RealTimeThreads::RealTimeThreads()
+    : m_threadGroup(ThreadGroup::create())
+#if USE(GLIB)
+    , m_discardRealTimeKitProxyTimer(RunLoop::main(), this, &RealTimeThreads::discardRealTimeKitProxyTimerFired)
+#endif
+{
+#if USE(GLIB)
+    m_discardRealTimeKitProxyTimer.setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer);
+#endif
+
+    callOnMainThread([] {
+        struct sigaction action;
+        sigemptyset(&action.sa_mask);
+        action.sa_sigaction = +[](int, siginfo_t*, void*) {
+            // We don't know which thread caused the limit to be reached,
+            // so we demote all real time threads to avoid SIGKILL.
+            RealTimeThreads::singleton().demoteAllThreadsFromRealTime();
+        };
+        action.sa_flags = SA_SIGINFO;
+        sigaction(SIGXCPU, &action, nullptr);
+    });
+}
+
+void RealTimeThreads::registerThread(Thread& thread)
+{
+    ThreadGroupAddResult addResult;
+    {
+        Locker locker { m_threadGroup->getLock() };
+        addResult = m_threadGroup->add(locker, thread);
+    }
+
+    if (addResult != ThreadGroupAddResult::NewlyAdded)
+        return;
+
+    callOnMainThread([this, thread = Ref { thread }] {
+        if (m_enabled)
+            promoteThreadToRealTime(thread);
+    });
+}
+
+void RealTimeThreads::setEnabled(bool enabled)
+{
+    ASSERT(isMainThread());
+    if (m_enabled == enabled)
+        return;
+
+    m_enabled = enabled;
+
+    Locker locker { m_threadGroup->getLock() };
+    for (const auto& thread : m_threadGroup->threads(locker)) {
+        if (m_enabled)
+            promoteThreadToRealTime(thread);
+        else
+            demoteThreadFromRealTime(thread);
+    }
+}
+
+void RealTimeThreads::promoteThreadToRealTime(const Thread& thread)
+{
+    ASSERT(isMainThread());
+
+    struct sched_param param;
+    param.sched_priority = std::clamp(s_realTimeThreadPriority, sched_get_priority_min(SCHED_RR), sched_get_priority_max(SCHED_RR));
+    auto error = sched_setscheduler(thread.id(), SCHED_RR | SCHED_RESET_ON_FORK, &param);
+    if (error) {
+#if USE(GLIB)
+        realTimeKitMakeThreadRealTime(getpid(), thread.id(), param.sched_priority);
+#else
+        LOG_ERROR("Failed to set thread %d as real time: %s", thread.id(), strerror(error));
+#endif
+    }
+}
+
+void RealTimeThreads::demoteThreadFromRealTime(const Thread& thread)
+{
+    ASSERT(isMainThread());
+
+    struct sched_param param = { 0 };
+    sched_setscheduler(thread.id(), SCHED_OTHER | SCHED_RESET_ON_FORK, &param);
+}
+
+void RealTimeThreads::demoteAllThreadsFromRealTime()
+{
+    Locker locker { m_threadGroup->getLock() };
+    for (const auto& thread : m_threadGroup->threads(locker))
+        demoteThreadFromRealTime(thread);
+}
+
+#if USE(GLIB)
+static const Seconds s_dbusCallTimeout = 20_ms;
+
+static int64_t realTimeKitGetProperty(GDBusProxy* proxy, const char* propertyName, GError** error)
+{
+    const char* interfaceName = shouldUsePortal() ? "org.freedesktop.portal.Realtime" : "org.freedesktop.RealtimeKit1";
+    GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(proxy, "org.freedesktop.DBus.Properties.Get",
+        g_variant_new("(ss)", interfaceName, propertyName), G_DBUS_CALL_FLAGS_NONE, s_dbusCallTimeout.millisecondsAs<int>(), nullptr, error));
+    if (!result)
+        return -1;
+
+    GRefPtr<GVariant> property;
+    g_variant_get(result.get(), "(v)", &property.outPtr());
+    if (g_variant_is_of_type(property.get(), G_VARIANT_TYPE_INT64))
+        return g_variant_get_int64(property.get());
+    if (g_variant_is_of_type(property.get(), G_VARIANT_TYPE_INT32))
+        return g_variant_get_int32(property.get());
+    g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid property type received for property %s at interface %s", propertyName, interfaceName);
+    return -1;
+}
+
+void RealTimeThreads::realTimeKitMakeThreadRealTime(uint64_t processID, uint64_t threadID, uint32_t priority)
+{
+    m_discardRealTimeKitProxyTimer.stop();
+
+    GUniqueOutPtr<GError> error;
+    if (!m_realTimeKitProxy) {
+        if (shouldUsePortal()) {
+            m_realTimeKitProxy = adoptGRef(g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
+                static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES), nullptr,
+                "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.Realtime", nullptr, &error.outPtr()));
+        } else {
+            m_realTimeKitProxy = adoptGRef(g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
+                static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES), nullptr,
+                "org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1", "org.freedesktop.RealtimeKit1", nullptr, &error.outPtr()));
+        }
+
+        if (!m_realTimeKitProxy.value()) {
+            LOG_ERROR("Failed to connect to RealtimeKit: %s", error->message);
+            return;
+        }
+    }
+
+    if (!m_realTimeKitProxy.value())
+        return;
+
+#ifdef RLIMIT_RTTIME
+    // RealTimeKit requires the client to have RLIMIT_RTTIME set.
+    struct rlimit rl;
+    if (getrlimit(RLIMIT_RTTIME, &rl) >= 0) {
+        auto rttimeMax = realTimeKitGetProperty(m_realTimeKitProxy->get(), "RTTimeUSecMax", &error.outPtr());
+        if (error) {
+            LOG_ERROR("Failed to get RTTimeUSecMax from RealtimeKit: %s", error->message);
+            if (!g_error_matches(error.get(), G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_INTERFACE))
+                m_realTimeKitProxy = nullptr;
+
+            scheduleDiscardRealTimeKitProxy();
+            return;
+        }
+
+        if (rl.rlim_max > static_cast<uint64_t>(rttimeMax)) {
+            rl.rlim_cur = rl.rlim_max = rttimeMax;
+            setrlimit(RLIMIT_RTTIME, &rl);
+        }
+    }
+#endif
+
+    GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(m_realTimeKitProxy->get(), "MakeThreadRealtimeWithPID",
+        g_variant_new("(ttu)", processID, threadID, priority), G_DBUS_CALL_FLAGS_NONE, s_dbusCallTimeout.millisecondsAs<int>(), nullptr, &error.outPtr()));
+    if (!result) {
+        LOG_ERROR("Failed to make thread real time: %s", error->message);
+        if (!g_error_matches(error.get(), G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_INTERFACE))
+            m_realTimeKitProxy = nullptr;
+    }
+
+    scheduleDiscardRealTimeKitProxy();
+}
+
+void RealTimeThreads::scheduleDiscardRealTimeKitProxy()
+{
+    if (!m_realTimeKitProxy || !m_realTimeKitProxy.value())
+        return;
+    m_discardRealTimeKitProxyTimer.startOneShot(30_s);
+}
+
+void RealTimeThreads::discardRealTimeKitProxyTimerFired()
+{
+    m_realTimeKitProxy = std::nullopt;
+}
+#endif // USE(GLIB)
+
+} // namespace WTF
diff --git a/Source/WTF/wtf/linux/RealTimeThreads.h b/Source/WTF/wtf/linux/RealTimeThreads.h
new file mode 100644 (file)
index 0000000..efa805c
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 Igalia S.L.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this library; see the file COPYING.LIB.  If not, write to
+ *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <wtf/FastMalloc.h>
+#include <wtf/ThreadGroup.h>
+
+#if USE(GLIB)
+#include <optional>
+#include <wtf/RunLoop.h>
+#include <wtf/glib/GRefPtr.h>
+
+typedef struct _GDBusProxy GDBusProxy;
+#endif
+
+namespace WTF {
+
+class RealTimeThreads {
+    WTF_MAKE_FAST_ALLOCATED;
+    friend class LazyNeverDestroyed<RealTimeThreads>;
+public:
+    WTF_EXPORT_PRIVATE static RealTimeThreads& singleton();
+
+    void registerThread(Thread&);
+
+    WTF_EXPORT_PRIVATE void setEnabled(bool);
+
+private:
+    RealTimeThreads();
+
+    void promoteThreadToRealTime(const WTF::Thread&);
+    void demoteThreadFromRealTime(const WTF::Thread&);
+    void demoteAllThreadsFromRealTime();
+
+#if USE(GLIB)
+    void realTimeKitMakeThreadRealTime(uint64_t processID, uint64_t threadID, uint32_t priority);
+    void scheduleDiscardRealTimeKitProxy();
+    void discardRealTimeKitProxyTimerFired();
+#endif
+
+    std::shared_ptr<ThreadGroup> m_threadGroup;
+    bool m_enabled { true };
+#if USE(GLIB)
+    std::optional<GRefPtr<GDBusProxy>> m_realTimeKitProxy;
+    RunLoop::Timer<RealTimeThreads> m_discardRealTimeKitProxyTimer;
+#endif
+};
+
+} // namespace WTF
+
+using WTF::RealTimeThreads;
index edb9b51848f890ed4e5d094014f6dd7f4291a8e9..947439ab77f67f748fa827510d83f2d0baf6fe5c 100644 (file)
 #include <wtf/WordLock.h>
 
 #if OS(LINUX)
+#include <sched.h>
 #include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <wtf/linux/RealTimeThreads.h>
+#ifndef SCHED_RESET_ON_FORK
+#define SCHED_RESET_ON_FORK 0x40000000
+#endif
 #endif
 
 #if !COMPILER(MSVC)
 #include <mach/thread_switch.h>
 #endif
 
-#if OS(LINUX)
-#include <sys/syscall.h>
-#endif
-
 namespace WTF {
 
 static Lock globalSuspendLock;
@@ -260,6 +262,24 @@ dispatch_qos_class_t Thread::dispatchQOSClass(QOS qos)
 }
 #endif
 
+#if OS(LINUX)
+static int schedPolicy(Thread::QOS qos)
+{
+    switch (qos) {
+    case Thread::QOS::UserInteractive:
+        return SCHED_RR;
+    case Thread::QOS::UserInitiated:
+    case Thread::QOS::Default:
+        return SCHED_OTHER;
+    case Thread::QOS::Utility:
+        return SCHED_BATCH;
+    case Thread::QOS::Background:
+        return SCHED_IDLE;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+#endif
+
 bool Thread::establishHandle(NewThreadContext* context, std::optional<size_t> stackSize, QOS qos)
 {
     pthread_t threadHandle;
@@ -267,8 +287,6 @@ bool Thread::establishHandle(NewThreadContext* context, std::optional<size_t> st
     pthread_attr_init(&attr);
 #if HAVE(QOS_CLASSES)
     pthread_attr_set_qos_class_np(&attr, dispatchQOSClass(qos), 0);
-#else
-    UNUSED_PARAM(qos);
 #endif
     if (stackSize)
         pthread_attr_setstacksize(&attr, stackSize.value());
@@ -278,6 +296,21 @@ bool Thread::establishHandle(NewThreadContext* context, std::optional<size_t> st
         LOG_ERROR("Failed to create pthread at entry point %p with context %p", wtfThreadEntryPoint, context);
         return false;
     }
+
+#if OS(LINUX)
+    int policy = schedPolicy(qos);
+    if (policy == SCHED_RR)
+        RealTimeThreads::singleton().registerThread(*this);
+    else {
+        struct sched_param param = { 0 };
+        error = pthread_setschedparam(threadHandle, policy | SCHED_RESET_ON_FORK, &param);
+        if (error)
+            LOG_ERROR("Failed to set sched policy %d for thread %d: %s", policy, threadHandle, strerror(error));
+    }
+#elif !HAVE(QOS_CLASSES)
+    UNUSED_PARAM(qos);
+#endif
+
     establishPlatformSpecificHandle(threadHandle);
     return true;
 }
index ad73305af6c0575bab1e76af86ef58de56f77f75..581a65db33a0da9b6e87955ac3cee96899c568ff 100644 (file)
@@ -1,3 +1,24 @@
+2021-10-20  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK][WPE] UserInteractive threads are not Real-time in Linux
+        https://bugs.webkit.org/show_bug.cgi?id=220115
+
+        Reviewed by Michael Catanzaro.
+
+        Enable real time threads in the web process only when there's a visible page.
+
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::createWebPage):
+        (WebKit::WebProcess::removeWebPage):
+        (WebKit::WebProcess::pageActivityStateDidChange):
+        (WebKit::WebProcess::platformInitializeProcess): Deleted.
+        * WebProcess/glib/WebProcessGLib.cpp:
+        (WebKit::WebProcess::platformInitializeProcess):
+        * WebProcess/playstation/WebProcessPlayStation.cpp:
+        (WebKit::WebProcess::platformInitializeProcess):
+        * WebProcess/win/WebProcessWin.cpp:
+        (WebKit::WebProcess::platformInitializeProcess):
+
 2021-10-20  Michael Catanzaro  <mcatanzaro@gnome.org>
 
         REGRESSION(r276635): [GTK] local HTML file is downloaded instead of displayed if any application has ever called g_desktop_app_info_set_as_default_for_extension(info, "html")
index f85ca363f4ef25f2921da585fd1b64c13e993644..462c3c27814a1296cd3d3a8d4dcc29e20d01fdbc 100644 (file)
 #include <WebCore/VP9UtilitiesCocoa.h>
 #endif
 
+#if OS(LINUX)
+#include <wtf/linux/RealTimeThreads.h>
+#endif
+
 #undef WEBPROCESS_RELEASE_LOG
 #define RELEASE_LOG_SESSION_ID (m_sessionID ? m_sessionID->toUInt64() : 0)
 #if RELEASE_LOG_DISABLED
@@ -809,6 +813,9 @@ void WebProcess::createWebPage(PageIdentifier pageID, WebPageCreationParameters&
         // Balanced by an enableTermination in removeWebPage.
         disableTermination();
         updateCPULimit();
+#if OS(LINUX)
+        RealTimeThreads::singleton().setEnabled(hasVisibleWebPage());
+#endif
     } else
         result.iterator->value->reinitializeWebPage(WTFMove(parameters));
 
@@ -826,6 +833,9 @@ void WebProcess::removeWebPage(PageIdentifier pageID)
 
     enableTermination();
     updateCPULimit();
+#if OS(LINUX)
+    RealTimeThreads::singleton().setEnabled(hasVisibleWebPage());
+#endif
 }
 
 bool WebProcess::shouldTerminate()
@@ -1466,10 +1476,6 @@ void WebProcess::initializeSandbox(const AuxiliaryProcessInitializationParameter
 {
 }
 
-void WebProcess::platformInitializeProcess(const AuxiliaryProcessInitializationParameters&)
-{
-}
-
 void WebProcess::updateActivePages(const String& overrideDisplayName)
 {
 }
@@ -1491,8 +1497,12 @@ void WebProcess::updateCPUMonitorState(CPUMonitorUpdateReason)
 
 void WebProcess::pageActivityStateDidChange(PageIdentifier, OptionSet<WebCore::ActivityState::Flag> changed)
 {
-    if (changed & WebCore::ActivityState::IsVisible)
+    if (changed & WebCore::ActivityState::IsVisible) {
         updateCPUMonitorState(CPUMonitorUpdateReason::VisibilityHasChanged);
+#if OS(LINUX)
+        RealTimeThreads::singleton().setEnabled(hasVisibleWebPage());
+#endif
+    }
 }
 
 #if PLATFORM(IOS_FAMILY)
index 0b95eea30d499614b5635c09807c5c91cca71783..7729ff79e60f0410bc36ef9c803759696e294e00 100644 (file)
 #include "UserMediaCaptureManager.h"
 #endif
 
+#if OS(LINUX)
+#include <wtf/linux/RealTimeThreads.h>
+#endif
+
 namespace WebKit {
 
 using namespace WebCore;
@@ -64,6 +68,14 @@ void WebProcess::platformSetCacheModel(CacheModel cacheModel)
     WebCore::MemoryCache::singleton().setDisabled(cacheModel == CacheModel::DocumentViewer);
 }
 
+void WebProcess::platformInitializeProcess(const AuxiliaryProcessInitializationParameters&)
+{
+#if OS(LINUX)
+    // Disable RealTimeThreads in WebProcess initially, since it depends on having a visible web page.
+    RealTimeThreads::singleton().setEnabled(false);
+#endif
+}
+
 void WebProcess::platformInitializeWebProcess(WebProcessCreationParameters& parameters)
 {
 #if ENABLE(MEDIA_STREAM)
index ef601220e556179b3118a78461481b5e0320cdf2..c44f0dd4cf89a7cb8fac781896f3551cf2454fb9 100644 (file)
@@ -43,6 +43,10 @@ void WebProcess::platformInitializeWebProcess(WebProcessCreationParameters& para
 #endif
 }
 
+void WebProcess::platformInitializeProcess(const AuxiliaryProcessInitializationParameters&)
+{
+}
+
 void WebProcess::platformSetWebsiteDataStoreParameters(WebProcessDataStoreParameters&&)
 {
 }
index 05b35cad50a896f5158f9c396a8d9c7307741057..d3d38cac41211d7e29b57f2452364e37b423c31a 100644 (file)
@@ -39,6 +39,10 @@ void WebProcess::platformInitializeWebProcess(WebProcessCreationParameters&)
 {
 }
 
+void WebProcess::platformInitializeProcess(const AuxiliaryProcessInitializationParameters&)
+{
+}
+
 void WebProcess::platformSetWebsiteDataStoreParameters(WebProcessDataStoreParameters&&)
 {
 }