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.kaganski@collabora.com>
This commit is contained in:
parent
9b56b718f6
commit
cf1c835e80
5 changed files with 137 additions and 3 deletions
42
include/systools/win32/retry_if_failed.hxx
Normal file
42
include/systools/win32/retry_if_failed.hxx
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* -*- 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>
|
||||
std::enable_if_t<std::is_same_v<std::invoke_result_t<Func>, HRESULT>, 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: */
|
16
sal/CppunitTest_sal_retry_if_failed.mk
Normal file
16
sal/CppunitTest_sal_retry_if_failed.mk
Normal file
|
@ -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:
|
|
@ -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 \
|
||||
|
|
72
sal/qa/systools/test_retry_if_failed.cxx
Normal file
72
sal/qa/systools/test_retry_if_failed.cxx
Normal file
|
@ -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: */
|
|
@ -44,6 +44,7 @@
|
|||
#include <process.h>
|
||||
|
||||
#include <systools/win32/comtools.hxx>
|
||||
#include <systools/win32/retry_if_failed.hxx>
|
||||
|
||||
#include <comphelper/windowserrorstring.hxx>
|
||||
|
||||
|
@ -465,7 +466,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 )
|
||||
|
@ -475,7 +477,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);
|
||||
|
@ -488,7 +491,7 @@ HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream )
|
|||
|
||||
HRESULT CMtaOleClipboard::onFlushClipboard( )
|
||||
{
|
||||
return OleFlushClipboard();
|
||||
return sal::systools::RetryIfFailed(10, 100, [] { return OleFlushClipboard(); });
|
||||
}
|
||||
|
||||
// handle clipboard update event
|
||||
|
|
Loading…
Reference in a new issue