[Libreoffice-commits] core.git: Branch 'libreoffice-5-0' - vcl/inc vcl/source vcl/workben

Michael Meeks michael.meeks at collabora.com
Wed Aug 26 00:26:01 PDT 2015


 vcl/inc/opengl/watchdog.hxx        |   31 ++++++++
 vcl/inc/opengl/zone.hxx            |   31 +++++---
 vcl/source/app/svmain.cxx          |    4 +
 vcl/source/opengl/OpenGLHelper.cxx |  130 ++++++++++++++++++++++++++++++++-----
 vcl/workben/vcldemo.cxx            |   69 +++++++++++++++++++
 5 files changed, 239 insertions(+), 26 deletions(-)

New commits:
commit e207dffe24aaec4d446baca67ffbbd6f67f52484
Author: Michael Meeks <michael.meeks at collabora.com>
Date:   Tue Aug 25 11:44:41 2015 +0100

    tdf#93614 - detect hanging OpenGL drivers with a watchdog.
    
    If an OpenGL zone takes >2s to make progress, disable OpenGL.
    If an OpenGL zone takes >5s to make progress, abort the app.
    
    Reviewed-on: https://gerrit.libreoffice.org/17986
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>
    Tested-by: Michael Meeks <michael.meeks at collabora.com>
    
    Conflicts:
    	vcl/workben/vcldemo.cxx
    
    Change-Id: I776c06a3f8ba460ff9842a9130c21f9ee2147eee
    Reviewed-on: https://gerrit.libreoffice.org/17995
    Reviewed-by: Markus Mohrhard <markus.mohrhard at googlemail.com>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>
    Tested-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/vcl/inc/opengl/watchdog.hxx b/vcl/inc/opengl/watchdog.hxx
new file mode 100644
index 0000000..b88c468
--- /dev/null
+++ b/vcl/inc/opengl/watchdog.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_VCL_INC_OPENGL_WATCHDOG_H
+#define INCLUDED_VCL_INC_OPENGL_WATCHDOG_H
+
+#include <sal/config.h>
+#include <sal/types.h>
+#include <rtl/ref.hxx>
+#include <salhelper/thread.hxx>
+
+class OpenGLWatchdogThread : private salhelper::Thread
+{
+    OpenGLWatchdogThread();
+    virtual void execute() SAL_OVERRIDE;
+public:
+    using salhelper::Thread::acquire;
+    using salhelper::Thread::release;
+    static void start();
+    static void stop();
+};
+
+#endif // INCLUDED_VCL_INC_OPENGL_WATCHDOG_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/opengl/zone.hxx b/vcl/inc/opengl/zone.hxx
index c251c4f..44265c5 100644
--- a/vcl/inc/opengl/zone.hxx
+++ b/vcl/inc/opengl/zone.hxx
@@ -7,28 +7,39 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#ifndef INCLUDED_VCL_INC_OPENGL_GUARD_H
-#define INCLUDED_VCL_INC_OPENGL_GUARD_H
+#ifndef INCLUDED_VCL_INC_OPENGL_ZONE_H
+#define INCLUDED_VCL_INC_OPENGL_ZONE_H
 
 #include <sal/config.h>
+#include <sal/types.h>
+#include <vcl/dllapi.h>
+
+class OpenGLZoneTest;
+class OpenGLWatchdogThread;
 
 /**
  * We want to be able to detect if a given crash came
  * from the OpenGL code, so use this helper to track that.
  */
-class OpenGLSalGraphicsImpl;
 class OpenGLZone {
-    static int gnInOpenGLZone;
+    friend class OpenGLZoneTest;
+    friend class OpenGLWatchdogThread;
     friend class OpenGLSalGraphicsImpl;
-    static void enter() { gnInOpenGLZone++; }
-    static void leave() { gnInOpenGLZone--; }
+
+    /// how many times have we entered a GL zone
+    static volatile sal_uInt64 gnEnterCount;
+    /// how many times have we left a new GL zone
+    static volatile sal_uInt64 gnLeaveCount;
+
+    static VCL_DLLPUBLIC void enter();
+    static VCL_DLLPUBLIC void leave();
 public:
-     OpenGLZone() { enter(); }
-    ~OpenGLZone() { leave(); }
-    static bool isInZone() { return gnInOpenGLZone > 0; }
+     OpenGLZone() { gnEnterCount++; }
+    ~OpenGLZone() { gnLeaveCount++; }
+    static bool isInZone() { return gnEnterCount != gnLeaveCount; }
     static void hardDisable();
 };
 
-#endif // INCLUDED_VCL_INC_OPENGL_PROGRAM_H
+#endif // INCLUDED_VCL_INC_OPENGL_ZONE_H
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx
index 4c3ef9a..b24714d 100644
--- a/vcl/source/app/svmain.cxx
+++ b/vcl/source/app/svmain.cxx
@@ -82,6 +82,7 @@
 #include "uno/current_context.hxx"
 
 #include "opengl/zone.hxx"
+#include "opengl/watchdog.hxx"
 
 #if OSL_DEBUG_LEVEL > 0
 #include <typeinfo>
@@ -189,7 +190,10 @@ int ImplSVMain()
       pSVData->mxAccessBridge.clear();
     }
 
+    OpenGLWatchdogThread::stop();
+
     DeInitVCL();
+
     return nReturn;
 }
 
diff --git a/vcl/source/opengl/OpenGLHelper.cxx b/vcl/source/opengl/OpenGLHelper.cxx
index 967d4c5..daa2af0 100644
--- a/vcl/source/opengl/OpenGLHelper.cxx
+++ b/vcl/source/opengl/OpenGLHelper.cxx
@@ -26,6 +26,8 @@
 #include <vector>
 
 #include "opengl/zone.hxx"
+#include "opengl/watchdog.hxx"
+#include <osl/conditn.h>
 
 #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
 #include "opengl/x11/X11DeviceInfo.hxx"
@@ -446,11 +448,101 @@ bool OpenGLHelper::supportsVCLOpenGL()
         return true;
 }
 
-/// How many nested OpenGL code-paths are we inside ?
-int OpenGLZone::gnInOpenGLZone = 0;
+sal_uInt64 volatile OpenGLZone::gnEnterCount = 0;
+sal_uInt64 volatile OpenGLZone::gnLeaveCount = 0;
+
+void OpenGLZone::enter() { gnEnterCount++; }
+void OpenGLZone::leave() { gnLeaveCount++; }
+
+namespace {
+    static volatile bool gbWatchdogFiring = false;
+    static oslCondition gpWatchdogExit = NULL;
+    static rtl::Reference<OpenGLWatchdogThread> gxWatchdog;
+}
+
+OpenGLWatchdogThread::OpenGLWatchdogThread()
+    : salhelper::Thread("OpenGL Watchdog")
+{
+}
+
+void OpenGLWatchdogThread::execute()
+{
+    static const int nDisableEntries = 4; // 2 seconds - disable GL
+    static const int nAbortAfter = 10;   // 5 seconds - not coming back; abort
+    int nUnchanged = 0; // how many unchanged nEnters
+
+    TimeValue aHalfSecond;
+    aHalfSecond.Seconds = 0;
+    aHalfSecond.Nanosec = 1000*1000*1000/2;
+
+    do {
+        sal_uInt64 nLastEnters = OpenGLZone::gnEnterCount;
+
+        osl_waitCondition(gpWatchdogExit, &aHalfSecond);
+
+        if (OpenGLZone::isInZone())
+        {
+            if (nLastEnters == OpenGLZone::gnEnterCount)
+                nUnchanged++;
+            else
+                nUnchanged = 0;
+            SAL_INFO("vcl.opengl", "GL watchdog - unchanged " <<
+                     nUnchanged << " enter count " <<
+                     OpenGLZone::gnEnterCount);
+
+            // Not making progress
+            if (nUnchanged == nDisableEntries)
+            {
+                gbWatchdogFiring = true;
+                SAL_WARN("vcl.opengl", "Watchdog triggered: hard disable GL");
+                OpenGLZone::hardDisable();
+                gbWatchdogFiring = false;
+            }
+
+            if (nUnchanged == nAbortAfter)
+            {
+                SAL_WARN("vcl.opengl", "Watchdog gave up: aborting");
+                gbWatchdogFiring = true;
+                std::abort();
+            }
+        }
+        else
+        {
+            nUnchanged = 0;
+        }
+    } while (!osl_checkCondition(gpWatchdogExit));
+}
+
+void OpenGLWatchdogThread::start()
+{
+    assert (gxWatchdog == NULL);
+    gpWatchdogExit = osl_createCondition();
+    gxWatchdog = rtl::Reference<OpenGLWatchdogThread>(new OpenGLWatchdogThread());
+    gxWatchdog->launch();
+}
+
+void OpenGLWatchdogThread::stop()
+{
+    if (gbWatchdogFiring)
+        return; // in watchdog thread
+
+    if (gpWatchdogExit)
+        osl_setCondition(gpWatchdogExit);
+
+    if (gxWatchdog.is())
+    {
+        gxWatchdog->join();
+        gxWatchdog.clear();
+    }
+
+    if (gpWatchdogExit)
+        osl_destroyCondition(gpWatchdogExit);
+    gpWatchdogExit = NULL;
+}
 
 /**
- * Called from a signal handler if we get a crash in some GL code
+ * Called from a signal handler or watchdog thread if we get
+ * a crash or hang in some GL code.
  */
 void OpenGLZone::hardDisable()
 {
@@ -471,6 +563,8 @@ void OpenGLZone::hardDisable()
             css::configuration::theDefaultProvider::get(
                 comphelper::getProcessComponentContext()),
             css::uno::UNO_QUERY_THROW)->flush();
+
+        OpenGLWatchdogThread::stop();
     }
 }
 
@@ -502,25 +596,31 @@ bool OpenGLHelper::isVCLOpenGLEnabled()
 
     bSet = true;
     bForceOpenGL = !!getenv("SAL_FORCEGL") || officecfg::Office::Common::VCL::ForceOpenGL::get();
+
+    bool bRet = false;
     if (bForceOpenGL)
-        return true;
+        bRet = true;
 
-    if (!supportsVCLOpenGL())
+    else if (!supportsVCLOpenGL())
+        bRet = false;
+    else
     {
-        return false;
-    }
+        static bool bEnableGLEnv = !!getenv("SAL_ENABLEGL");
 
-    static bool bEnableGLEnv = !!getenv("SAL_ENABLEGL");
+        bEnable = bEnableGLEnv;
 
-    bEnable = bEnableGLEnv;
+        static bool bDuringBuild = getenv("VCL_HIDE_WINDOWS");
+        if (bDuringBuild && !bEnable /* env. enable overrides */)
+            bEnable = false;
+        else if (officecfg::Office::Common::VCL::UseOpenGL::get())
+            bEnable = true;
 
-    static bool bDuringBuild = getenv("VCL_HIDE_WINDOWS");
-    if (bDuringBuild && !bEnable /* env. enable overrides */)
-        bEnable = false;
-    else if (officecfg::Office::Common::VCL::UseOpenGL::get())
-        bEnable = true;
+        bRet = bEnable;
+    }
+    if (bRet)
+        OpenGLWatchdogThread::start();
 
-    return bEnable;
+    return bRet;
 }
 
 #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined(LIBO_HEADLESS)
diff --git a/vcl/workben/vcldemo.cxx b/vcl/workben/vcldemo.cxx
index a80409a..e0218e0 100644
--- a/vcl/workben/vcldemo.cxx
+++ b/vcl/workben/vcldemo.cxx
@@ -41,6 +41,7 @@
 #include <basegfx/numeric/ftools.hxx>
 #include <basegfx/matrix/b2dhommatrix.hxx>
 #include <vcldemo-debug.hxx>
+#include <opengl/zone.hxx>
 
 #include <rtl/math.hxx>
 
@@ -1457,13 +1458,23 @@ class DemoWidgets : public WorkWindow
     VclPtr<VclBox> mpBox;
     VclPtr<ToolBox> mpToolbox;
     VclPtr<PushButton> mpButton;
+    VclPtr<VclHBox> mpHBox;
+    VclPtr<CheckBox> mpGLCheck;
+    VclPtr<ComboBox> mpGLCombo;
+    VclPtr<PushButton> mpGLButton;
+
+    DECL_LINK(GLTestClick, void *);
 
 public:
     DemoWidgets() :
         WorkWindow(NULL, WB_STDWORK),
         mpBox(VclPtrInstance<VclVBox>(this, false, 3)),
         mpToolbox(VclPtrInstance<ToolBox>(mpBox.get())),
-        mpButton(VclPtrInstance<PushButton>(mpBox.get()))
+        mpButton(VclPtrInstance<PushButton>(mpBox.get())),
+        mpHBox(VclPtrInstance<VclHBox>(mpBox.get(), true, 3)),
+        mpGLCheck(VclPtrInstance<CheckBox>(mpHBox.get())),
+        mpGLCombo(VclPtrInstance<ComboBox>(mpHBox.get())),
+        mpGLButton(VclPtrInstance<PushButton>(mpHBox.get()))
     {
         SetText("VCL widget demo");
 
@@ -1484,11 +1495,27 @@ public:
         mpButton->SetText("Click me; go on");
         mpButton->Show();
 
+        mpGLCheck->SetText("Test in OGL zone");
+        mpGLCheck->Show();
+        mpGLCombo->InsertEntry("sleep 1 second");
+        mpGLCombo->InsertEntry("sleep 3 seconds");
+        mpGLCombo->InsertEntry("sleep 7 seconds");
+        mpGLCombo->SelectEntryPos(2);
+        mpGLCombo->Show();
+        mpGLButton->SetText("Execute test");
+        mpGLButton->SetClickHdl(LINK(this,DemoWidgets,GLTestClick));
+        mpGLButton->Show();
+        mpHBox->Show();
+
         Show();
     }
     virtual ~DemoWidgets() { disposeOnce(); }
     virtual void dispose() SAL_OVERRIDE
     {
+        mpGLButton.disposeAndClear();
+        mpGLCombo.disposeAndClear();
+        mpGLCheck.disposeAndClear();
+        mpHBox.disposeAndClear();
         mpBox.disposeAndClear();
         mpToolbox.disposeAndClear();
         mpButton.disposeAndClear();
@@ -1521,6 +1548,46 @@ public:
     }
 };
 
+class OpenGLZoneTest {
+public:
+    static void enter() { OpenGLZone::enter(); }
+    static void leave() { OpenGLZone::leave(); }
+};
+
+IMPL_LINK_NOARG(DemoWidgets,GLTestClick)
+{
+    sal_Int32 nSelected = mpGLCombo->GetSelectEntryPos();
+
+    TimeValue aDelay;
+    aDelay.Seconds = 0;
+    aDelay.Nanosec = 0;
+    switch (nSelected)
+    {
+    case 0:
+        aDelay.Seconds = 1;
+        break;
+    case 1:
+        aDelay.Seconds = 3;
+        break;
+    case 2:
+        aDelay.Seconds = 7;
+        break;
+    default:
+        break;
+    }
+
+    bool bEnterLeave = mpGLCheck->IsChecked();
+    if (bEnterLeave)
+        OpenGLZoneTest::enter();
+
+    osl_waitThread(&aDelay);
+
+    if (bEnterLeave)
+        OpenGLZoneTest::leave();
+
+    return 0;
+}
+
 class DemoPopup : public FloatingWindow
 {
  public:


More information about the Libreoffice-commits mailing list