Kaydet (Commit) 25cc0ab3 authored tarafından Dennis Francis's avatar Dennis Francis Kaydeden (comit) Julien Nabet

Calc threading : Check for "self" references...

...on indirect dependencies too.

Here a self reference to any formula-group
means if there are any references in a formula
(of the formula-group itself or any of its dependencies)
that points to any element inside the formula-group.
If there are any self-references, then that formula-group
can't be computed in parallel.

For example, with this patch we can detect the following case:-
  Suppose the formula-group that we want to check is:
  "=(F2+G2-10)*10.0" spanning A2:A100. Let the formula-group
  starting at F2 be "=A1*0.1-10". The indirect dependency
  formula-group starting at F2, references back the elements of
  our original formula-group at A2. This makes the F.G at
  A2 unsafe for parallel computation.

Concretly, this patch fixes a recalc crash on tdf#63638/1

Change-Id: I7b999a34571b191d2f70da6a3831f78b24a6b0a7
Reviewed-on: https://gerrit.libreoffice.org/54433Reviewed-by: 's avatarMichael Meeks <michael.meeks@collabora.com>
Tested-by: 's avatarJenkins <ci@libreoffice.org>
üst 1d16a256
...@@ -66,6 +66,7 @@ public: ...@@ -66,6 +66,7 @@ public:
SvNumFormatType mnFormatType; SvNumFormatType mnFormatType;
bool mbInvariant:1; bool mbInvariant:1;
bool mbSubTotal:1; bool mbSubTotal:1;
bool mbSeenInPath:1; // For detecting cycle of formula groups
sal_uInt8 meCalcState; sal_uInt8 meCalcState;
......
...@@ -23,9 +23,11 @@ ...@@ -23,9 +23,11 @@
#include "formularesult.hxx" #include "formularesult.hxx"
#include <list> #include <list>
#include <vector>
#include <stack> #include <stack>
class ScFormulaCell; class ScFormulaCell;
struct ScFormulaCellGroup;
struct ScFormulaRecursionEntry struct ScFormulaRecursionEntry
{ {
...@@ -44,10 +46,12 @@ typedef ::std::list< ScFormulaRecursionEntry > ScFormulaRecursionList; ...@@ -44,10 +46,12 @@ typedef ::std::list< ScFormulaRecursionEntry > ScFormulaRecursionList;
class ScRecursionHelper class ScRecursionHelper
{ {
typedef ::std::stack< ScFormulaCell* > ScRecursionInIterationStack; typedef ::std::stack< ScFormulaCell* > ScRecursionInIterationStack;
typedef ::std::vector< ScFormulaCellGroup* > ScFGList;
ScFormulaRecursionList aRecursionFormulas; ScFormulaRecursionList aRecursionFormulas;
ScFormulaRecursionList::iterator aInsertPos; ScFormulaRecursionList::iterator aInsertPos;
ScFormulaRecursionList::iterator aLastIterationStart; ScFormulaRecursionList::iterator aLastIterationStart;
ScRecursionInIterationStack aRecursionInIterationStack; ScRecursionInIterationStack aRecursionInIterationStack;
ScFGList aFGList;
sal_uInt16 nRecursionCount; sal_uInt16 nRecursionCount;
sal_uInt16 nIteration; sal_uInt16 nIteration;
bool bInRecursionReturn; bool bInRecursionReturn;
...@@ -94,6 +98,10 @@ public: ...@@ -94,6 +98,10 @@ public:
ScRecursionInIterationStack& GetRecursionInIterationStack() { return aRecursionInIterationStack; } ScRecursionInIterationStack& GetRecursionInIterationStack() { return aRecursionInIterationStack; }
void Clear(); void Clear();
/** Detects whether the formula-group is part of a simple cycle of formula-groups. */
bool PushFormulaGroup(ScFormulaCellGroup* pGrp);
void PopFormulaGroup();
}; };
#endif // INCLUDED_SC_INC_RECURSIONHELPER_HXX #endif // INCLUDED_SC_INC_RECURSIONHELPER_HXX
......
...@@ -528,6 +528,7 @@ ScFormulaCellGroup::ScFormulaCellGroup() : ...@@ -528,6 +528,7 @@ ScFormulaCellGroup::ScFormulaCellGroup() :
mnFormatType(SvNumFormatType::NUMBER), mnFormatType(SvNumFormatType::NUMBER),
mbInvariant(false), mbInvariant(false),
mbSubTotal(false), mbSubTotal(false),
mbSeenInPath(false),
meCalcState(sc::GroupCalcEnabled) meCalcState(sc::GroupCalcEnabled)
{ {
} }
...@@ -1527,6 +1528,14 @@ void ScFormulaCell::Interpret() ...@@ -1527,6 +1528,14 @@ void ScFormulaCell::Interpret()
else else
{ {
pDocument->IncInterpretLevel(); pDocument->IncInterpretLevel();
bool bCheckForFGCycle = mxGroup && !pDocument->mbThreadedGroupCalcInProgress &&
mxGroup->meCalcState == sc::GroupCalcEnabled &&
ScCalcConfig::isThreadingEnabled();
bool bPopFormulaGroup = false;
if (bCheckForFGCycle)
bPopFormulaGroup = rRecursionHelper.PushFormulaGroup(mxGroup.get());
#if DEBUG_CALCULATION #if DEBUG_CALCULATION
aDC.enterGroup(); aDC.enterGroup();
bool bGroupInterpreted = InterpretFormulaGroup(); bool bGroupInterpreted = InterpretFormulaGroup();
...@@ -1537,6 +1546,10 @@ void ScFormulaCell::Interpret() ...@@ -1537,6 +1546,10 @@ void ScFormulaCell::Interpret()
if (!InterpretFormulaGroup()) if (!InterpretFormulaGroup())
InterpretTail( pDocument->GetNonThreadedContext(), SCITP_NORMAL); InterpretTail( pDocument->GetNonThreadedContext(), SCITP_NORMAL);
#endif #endif
if (bPopFormulaGroup)
rRecursionHelper.PopFormulaGroup();
pDocument->DecInterpretLevel(); pDocument->DecInterpretLevel();
} }
...@@ -4129,14 +4142,14 @@ struct ScDependantsCalculator ...@@ -4129,14 +4142,14 @@ struct ScDependantsCalculator
SCROW nEndRow = mrPos.Row() + mnLen - 1; SCROW nEndRow = mrPos.Row() + mnLen - 1;
if (nRelRow < 0) if (nRelRow <= 0)
{ {
SCROW nTest = nEndRow; SCROW nTest = nEndRow;
nTest += nRelRow; nTest += nRelRow;
if (nTest >= mrPos.Row()) if (nTest >= mrPos.Row())
return true; return true;
} }
else if (nRelRow > 0) else
{ {
SCROW nTest = mrPos.Row(); // top row. SCROW nTest = mrPos.Row(); // top row.
nTest += nRelRow; nTest += nRelRow;
...@@ -4406,6 +4419,12 @@ bool ScFormulaCell::InterpretFormulaGroup() ...@@ -4406,6 +4419,12 @@ bool ScFormulaCell::InterpretFormulaGroup()
return false; return false;
} }
if (mxGroup->meCalcState == sc::GroupCalcDisabled)
{
aScope.addMessage("found circular formula-group dependencies");
return false;
}
static bool bHyperThreadingActive = tools::cpuid::hasHyperThreading(); static bool bHyperThreadingActive = tools::cpuid::hasHyperThreading();
// Then do the threaded calculation // Then do the threaded calculation
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
*/ */
#include <recursionhelper.hxx> #include <recursionhelper.hxx>
#include <formulacell.hxx>
void ScRecursionHelper::Init() void ScRecursionHelper::Init()
{ {
...@@ -15,6 +16,7 @@ void ScRecursionHelper::Init() ...@@ -15,6 +16,7 @@ void ScRecursionHelper::Init()
bInRecursionReturn = bDoingRecursion = bInIterationReturn = false; bInRecursionReturn = bDoingRecursion = bInIterationReturn = false;
aInsertPos = GetIterationEnd(); aInsertPos = GetIterationEnd();
ResetIteration(); ResetIteration();
aFGList.clear();
} }
void ScRecursionHelper::ResetIteration() void ScRecursionHelper::ResetIteration()
...@@ -94,4 +96,39 @@ void ScRecursionHelper::Clear() ...@@ -94,4 +96,39 @@ void ScRecursionHelper::Clear()
Init(); Init();
} }
bool ScRecursionHelper::PushFormulaGroup(ScFormulaCellGroup* pGrp)
{
assert(pGrp);
if (pGrp->mbSeenInPath)
{
// Found a simple cycle of formula-groups.
// Disable group calc for all elements of this cycle.
sal_Int32 nIdx = aFGList.size();
assert(nIdx > 0);
do
{
--nIdx;
assert(nIdx >= 0);
auto& eCalcState = aFGList[nIdx]->meCalcState;
if (eCalcState == sc::GroupCalcEnabled)
eCalcState = sc::GroupCalcDisabled;
} while (aFGList[nIdx] != pGrp);
return false;
}
pGrp->mbSeenInPath = true;
aFGList.push_back(pGrp);
return true;
}
void ScRecursionHelper::PopFormulaGroup()
{
if (aFGList.empty())
return;
ScFormulaCellGroup* pGrp = aFGList.back();
pGrp->mbSeenInPath = false;
aFGList.pop_back();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment