include/systools/win32/retry_if_failed.hxx |   40 ++++++++++++++++
 sal/CppunitTest_sal_retry_if_failed.mk     |   16 ++++++
 sal/Module_sal.mk                          |    1 
 sal/qa/systools/test_retry_if_failed.cxx   |   72 +++++++++++++++++++++++++++++
 vcl/win/dtrans/MtaOleClipb.cxx             |    9 ++-
 5 files changed, 135 insertions(+), 3 deletions(-)

New commits:
commit 316bebbbbd19cdccde05eba5f6098d301f032df2
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Mar 2 15:00:56 2021 +0300
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Tue Mar 9 17:00:51 2021 +0100

    tdf#116983 tdf#136175: retry if failed
    
    Debugging the test case from the latter bug report shows that indeed
    the call to OleGetClipboard may fail first time, as jasonkres had
    suspected in the former bug. So follow the suggestion in tdf#116983,
    and retry the failing calls several times in case of failure.
    
    Many thanks to Telesto for preparing a clear bug report with reliable
    test case.
    
    Co-authored-by: jasonkres
    
    Change-Id: Ib3c497da830bc5faac586bcfe1eededa54bfa117
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111825
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    (cherry picked from commit cf1c835e8016f8f1eefea6d625a913c0ac343a63)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112075
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/include/systools/win32/retry_if_failed.hxx 
b/include/systools/win32/retry_if_failed.hxx
new file mode 100644
index 000000000000..11a7e5372037
--- /dev/null
+++ b/include/systools/win32/retry_if_failed.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <systools/win32/uwinapi.h>
+
+#pragma comment(lib, "Kernel32.lib") // for Sleep
+
+namespace sal::systools
+{
+// Some system calls (e.g., clipboard access functions) may fail first time, 
because the resource
+// may only be accessed by one process at a time. This function allows to 
retry failed call up to
+// specified number of times with a specified timeout (in ms), until the call 
succeeds or the limit
+// of attempts is exceeded.
+// Usage:
+//     HRESULT hr = sal::systools::RetryIfFailed(10, 100, []{ return 
OleFlushClipboard(); });
+template <typename Func> HRESULT RetryIfFailed(unsigned times, unsigned 
msTimeout, Func func)
+{
+    HRESULT hr = E_FAIL;
+    for (unsigned i = 0; i < times; ++i)
+    {
+        hr = func();
+        if (SUCCEEDED(hr))
+            break;
+        if (i < times - 1)
+            Sleep(msTimeout);
+    }
+    return hr;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sal/CppunitTest_sal_retry_if_failed.mk 
b/sal/CppunitTest_sal_retry_if_failed.mk
new file mode 100644
index 000000000000..6e131afb50dc
--- /dev/null
+++ b/sal/CppunitTest_sal_retry_if_failed.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 
100 -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,sal_retry_if_failed))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sal_retry_if_failed,\
+    sal/qa/systools/test_retry_if_failed \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sal/Module_sal.mk b/sal/Module_sal.mk
index 7611bc950f07..1a190037f05b 100644
--- a/sal/Module_sal.mk
+++ b/sal/Module_sal.mk
@@ -26,6 +26,7 @@ $(eval $(call gb_Module_add_targets,sal,\
 $(eval $(call gb_Module_add_check_targets,sal,\
        $(if $(filter TRUE,$(DISABLE_DYNLOADING)),,CppunitTest_Module_DLL) \
        $(if $(filter WNT,$(OS)),CppunitTest_sal_comtools) \
+       $(if $(filter WNT,$(OS)),CppunitTest_sal_retry_if_failed) \
        CppunitTest_sal_osl_security \
        CppunitTest_sal_osl \
        CppunitTest_sal_rtl \
diff --git a/sal/qa/systools/test_retry_if_failed.cxx 
b/sal/qa/systools/test_retry_if_failed.cxx
new file mode 100644
index 000000000000..845cba83092d
--- /dev/null
+++ b/sal/qa/systools/test_retry_if_failed.cxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <systools/win32/retry_if_failed.hxx>
+
+namespace test_systools
+{
+class test_retry_if_failed : public CppUnit::TestFixture
+{
+public:
+    void test_success()
+    {
+        const DWORD nTicksBefore = GetTickCount();
+        HRESULT hr = sal::systools::RetryIfFailed(10, 100, Tester(5));
+        const DWORD nTicksAfter = GetTickCount();
+        const DWORD nTicksElapsed = nTicksAfter > nTicksBefore ? nTicksAfter - 
nTicksBefore
+                                                               : 
std::numeric_limits<DWORD>::max()
+                                                                     - 
nTicksBefore + nTicksAfter;
+        CPPUNIT_ASSERT(SUCCEEDED(hr));
+        CPPUNIT_ASSERT(nTicksElapsed >= 400); // 5 attempts, 4 sleeps by 100 ms
+    }
+
+    void test_failure()
+    {
+        const DWORD nTicksBefore = GetTickCount();
+        HRESULT hr = sal::systools::RetryIfFailed(10, 100, Tester(15));
+        const DWORD nTicksAfter = GetTickCount();
+        const DWORD nTicksElapsed = nTicksAfter > nTicksBefore ? nTicksAfter - 
nTicksBefore
+                                                               : 
std::numeric_limits<DWORD>::max()
+                                                                     - 
nTicksBefore + nTicksAfter;
+        CPPUNIT_ASSERT(FAILED(hr));
+        CPPUNIT_ASSERT(nTicksElapsed >= 900); // 10 attempts, 9 sleeps by 100 
ms
+    }
+
+    CPPUNIT_TEST_SUITE(test_retry_if_failed);
+    CPPUNIT_TEST(test_success);
+    CPPUNIT_TEST(test_failure);
+    CPPUNIT_TEST_SUITE_END();
+
+private:
+    struct Tester
+    {
+        Tester(unsigned triesBeforeSuccess)
+            : m_nTriesBeforeSuccess(triesBeforeSuccess)
+        {
+        }
+
+        HRESULT operator()()
+        {
+            return ++m_nTriesAttempted >= m_nTriesBeforeSuccess ? S_OK : 
E_FAIL;
+        }
+
+        unsigned m_nTriesBeforeSuccess;
+        unsigned m_nTriesAttempted = 0;
+    };
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_systools::test_retry_if_failed);
+
+} // namespace test_systools
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/vcl/win/dtrans/MtaOleClipb.cxx b/vcl/win/dtrans/MtaOleClipb.cxx
index d894ae7b5a48..d5c73ef4e4cd 100644
--- a/vcl/win/dtrans/MtaOleClipb.cxx
+++ b/vcl/win/dtrans/MtaOleClipb.cxx
@@ -44,6 +44,7 @@
 #include <process.h>
 
 #include <systools/win32/comtools.hxx>
+#include <systools/win32/retry_if_failed.hxx>
 
 //  namespace directives
 
@@ -463,7 +464,8 @@ bool CMtaOleClipboard::onRegisterClipViewer( 
LPFNC_CLIPVIEWER_CALLBACK_t pfncCli
 
 HRESULT CMtaOleClipboard::onSetClipboard( IDataObject* pIDataObject )
 {
-    return OleSetClipboard( pIDataObject );
+    return sal::systools::RetryIfFailed(10, 100,
+                                        [pIDataObject] { return 
OleSetClipboard(pIDataObject); });
 }
 
 HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream )
@@ -473,7 +475,8 @@ HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* 
ppStream )
     IDataObjectPtr pIDataObject;
 
     // forward the request to the OleClipboard
-    HRESULT hr = OleGetClipboard( &pIDataObject );
+    HRESULT hr
+        = sal::systools::RetryIfFailed(10, 100, [p = &pIDataObject] { return 
OleGetClipboard(p); });
     if ( SUCCEEDED( hr ) )
     {
         hr = MarshalIDataObjectInStream(pIDataObject.get(), ppStream);
@@ -486,7 +489,7 @@ HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* 
ppStream )
 
 HRESULT CMtaOleClipboard::onFlushClipboard( )
 {
-    return OleFlushClipboard();
+    return sal::systools::RetryIfFailed(10, 100, [] { return 
OleFlushClipboard(); });
 }
 
 // handle clipboard update event
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to