[Libreoffice-commits] core.git: Branch 'feature/calc-parallel' - 54 commits - basctl/source cui/source cui/uiconfig external/pdfium formula/source include/formula include/sal include/tools officecfg/registry sal/rtl sc/inc sc/source solenv/vs svl/source sw/inc sw/source tools/source

Dennis Francis dennis.francis at collabora.co.uk
Mon Nov 20 15:39:45 UTC 2017


Rebased ref, commits from common ancestor:
commit b3e943b465cc5e4165e03d803c1eb27fa9827610
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Fri Nov 17 15:30:35 2017 +0530

    Move token-cache for doubles to ScInterpreterContext...
    
    ...from ScInterpreter and in the s/w interpreter, create
    a ScInterpreterContext for each thread for passing into
    per thread ScInterpreter constructor.
    
    Change-Id: I4e0abce043c7e1e70859efb2e5001fc284f416a9

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 44bee716bf18..acd899e7658a 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -583,7 +583,7 @@ public:
     void SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen );
     void SetFormulaResults( SCROW nRow, const formula::FormulaConstTokenRef* pResults, size_t nLen );
 
-    void CalculateInThread( const ScInterpreterContext& rContext, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal );
+    void CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal );
     void HandleStuffAfterParallelCalculation( SCROW nRow, size_t nLen );
 
     void SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat );
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 8b138378dda3..c4db749eab6a 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -458,6 +458,8 @@ private:
     // plain thread_local static member.
     thread_local static ScDocumentThreadSpecific maThreadSpecific;
 
+    mutable ScInterpreterContext maInterpreterContext;
+
     sal_uInt16              nSrcVer;                        // file version (load/save)
     sal_uInt16              nFormulaTrackCount;
     HardRecalcState         eHardRecalcState;               // off, temporary, eternal
@@ -560,10 +562,11 @@ public:
 
     SC_DLLPUBLIC void  InitDrawLayer( SfxObjectShell* pDocShell = nullptr );
 
-    ScInterpreterContext GetNonThreadedContext() const
+    ScInterpreterContext& GetNonThreadedContext() const
     {
         // GetFormatTable() asserts that we are not in a threaded calculation
-        return ScInterpreterContext(*this, GetFormatTable());
+        maInterpreterContext.mpFormatter = GetFormatTable();
+        return maInterpreterContext;
     }
 
     SC_DLLPUBLIC sfx2::LinkManager*       GetLinkManager();
@@ -2056,7 +2059,7 @@ public:
     void SC_DLLPUBLIC SetFormulaResults( const ScAddress& rTopPos, const double* pResults, size_t nLen );
     void SC_DLLPUBLIC SetFormulaResults( const ScAddress& rTopPos, const formula::FormulaConstTokenRef* pResults, size_t nLen );
 
-    ScDocumentThreadSpecific CalculateInColumnInThread( const ScInterpreterContext& rContext, const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
+    ScDocumentThreadSpecific CalculateInColumnInThread( ScInterpreterContext& rContext, const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
     void HandleStuffAfterParallelCalculation( const ScAddress& rTopPos, size_t nLen );
 
     /**
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index 0185e2629010..e75416de8e56 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -150,7 +150,7 @@ public:
                         SCITP_FROM_ITERATION,
                         SCITP_CLOSE_ITERATION_CIRCLE
                     };
-                    void InterpretTail( const ScInterpreterContext&, ScInterpretTailParameter );
+                    void InterpretTail( ScInterpreterContext&, ScInterpretTailParameter );
 
     void            HandleStuffAfterParallelCalculation();
 
diff --git a/sc/inc/interpretercontext.hxx b/sc/inc/interpretercontext.hxx
index cbf05349ca5f..ff52267dfb26 100644
--- a/sc/inc/interpretercontext.hxx
+++ b/sc/inc/interpretercontext.hxx
@@ -10,6 +10,11 @@
 #ifndef INCLUDED_SC_INC_INTERPRETERCONTEXT_HXX
 #define INCLUDED_SC_INC_INTERPRETERCONTEXT_HXX
 
+#include <vector>
+#include <formula/token.hxx>
+
+#define TOKEN_CACHE_SIZE 8
+
 class ScDocument;
 class SvNumberFormatter;
 
@@ -17,15 +22,22 @@ struct ScInterpreterContext
 {
     const ScDocument& mrDoc;
     SvNumberFormatter* mpFormatter;
+    size_t mnTokenCachePos;
+    std::vector<formula::FormulaToken*> maTokens;
 
     ScInterpreterContext(const ScDocument& rDoc, SvNumberFormatter* pFormatter) :
         mrDoc(rDoc),
-        mpFormatter(pFormatter)
+        mpFormatter(pFormatter),
+        mnTokenCachePos(0),
+        maTokens(TOKEN_CACHE_SIZE, nullptr)
     {
     }
 
     ~ScInterpreterContext()
     {
+        for ( auto p : maTokens )
+            if ( p )
+                p->DecRef();
     }
 
     SvNumberFormatter* GetFormatTable() const
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 1f77ae4d2e39..3721e2226a5a 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -998,7 +998,7 @@ public:
     void SetFormulaResults( SCCOL nCol, SCROW nRow, const double* pResults, size_t nLen );
     void SetFormulaResults( SCCOL nCol, SCROW nRow, const formula::FormulaConstTokenRef* pResults, size_t nLen );
 
-    void CalculateInColumnInThread( const ScInterpreterContext& rContext, SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
+    void CalculateInColumnInThread( ScInterpreterContext& rContext, SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
     void HandleStuffAfterParallelCalculation( SCCOL nCol, SCROW nRow, size_t nLen);
 
     /**
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index daeda62bd881..fea8d080dd13 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -2877,7 +2877,7 @@ void ScColumn::SetFormulaResults( SCROW nRow, const formula::FormulaConstTokenRe
     }
 }
 
-void ScColumn::CalculateInThread( const ScInterpreterContext& rContext, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
+void ScColumn::CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
 {
     assert(pDocument->mbThreadedGroupCalcInProgress);
 
diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx
index b15ee591a7e2..9bf0fcf73194 100644
--- a/sc/source/core/data/documen2.cxx
+++ b/sc/source/core/data/documen2.cxx
@@ -177,6 +177,7 @@ ScDocument::ScDocument( ScDocumentMode eMode, SfxObjectShell* pDocShell ) :
         nInterpretLevel(0),
         nMacroInterpretLevel(0),
         nInterpreterTableOpLevel(0),
+        maInterpreterContext( *this, nullptr ),
         nSrcVer( SC_CURRENT_VERSION ),
         nFormulaTrackCount(0),
         eHardRecalcState(HardRecalcState::OFF),
diff --git a/sc/source/core/data/documen8.cxx b/sc/source/core/data/documen8.cxx
index 8bf1de3fb191..c7be637fb073 100644
--- a/sc/source/core/data/documen8.cxx
+++ b/sc/source/core/data/documen8.cxx
@@ -427,7 +427,7 @@ void ScDocument::SetFormulaResults(
     pTab->SetFormulaResults(rTopPos.Col(), rTopPos.Row(), pResults, nLen);
 }
 
-ScDocumentThreadSpecific ScDocument::CalculateInColumnInThread( const ScInterpreterContext& rContext, const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
+ScDocumentThreadSpecific ScDocument::CalculateInColumnInThread( ScInterpreterContext& rContext, const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
 {
     ScTable* pTab = FetchTable(rTopPos.Tab());
     if (!pTab)
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index f90e83cb6c06..dde15e97ad0e 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -1750,7 +1750,7 @@ class StackCleaner
 };
 }
 
-void ScFormulaCell::InterpretTail( const ScInterpreterContext& rContext, ScInterpretTailParameter eTailParam )
+void ScFormulaCell::InterpretTail( ScInterpreterContext& rContext, ScInterpretTailParameter eTailParam )
 {
     RecursionCounter aRecursionCounter( pDocument->GetRecursionHelper(), this);
     nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx
index e43ab0efc7ac..0b23c7474e91 100644
--- a/sc/source/core/data/table1.cxx
+++ b/sc/source/core/data/table1.cxx
@@ -2338,7 +2338,7 @@ void ScTable::SetFormulaResults(
     aCol[nCol].SetFormulaResults(nRow, pResults, nLen);
 }
 
-void ScTable::CalculateInColumnInThread( const ScInterpreterContext& rContext, SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
+void ScTable::CalculateInColumnInThread( ScInterpreterContext& rContext, SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
 {
     if (!ValidCol(nCol))
         return;
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
index a6eaf5cd78ae..d6a77f2b9c1a 100644
--- a/sc/source/core/inc/interpre.hxx
+++ b/sc/source/core/inc/interpre.hxx
@@ -97,7 +97,6 @@ class SharedStringPool;
 }
 
 #define MAXSTACK      (4096 / sizeof(formula::FormulaToken*))
-#define TOKEN_CACHE_SIZE 8
 
 class ScTokenStack
 {
@@ -200,7 +199,7 @@ private:
     formula::FormulaTokenIterator aCode;
     ScAddress   aPos;
     ScTokenArray& rArr;
-    const ScInterpreterContext& mrContext;
+    ScInterpreterContext& mrContext;
     ScDocument* pDok;
     sfx2::LinkManager* mpLinkManager;
     svl::SharedStringPool& mrStrPool;
@@ -230,8 +229,6 @@ private:
     bool        bMatrixFormula;         // formula cell is a matrix formula
 
     VolatileType meVolatileType;
-    size_t       mnTokenCachePos;
-    std::vector<formula::FormulaToken*> maTokenCache;
 
     /// Merge global and document specific settings.
     void MergeCalcConfig();
@@ -994,7 +991,7 @@ private:
     double GetTInv( double fAlpha, double fSize, int nType );
 
 public:
-    ScInterpreter( ScFormulaCell* pCell, ScDocument* pDoc, const ScInterpreterContext& rContext,
+    ScInterpreter( ScFormulaCell* pCell, ScDocument* pDoc, ScInterpreterContext& rContext,
                     const ScAddress&, ScTokenArray& );
     ~ScInterpreter();
 
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index 326392f6099e..3acc38d68051 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -155,6 +155,7 @@ public:
                             ScAddress aBatchTopPos,
                             const ScAddress& rTopPos,
                             ScDocument& rDoc,
+                            SvNumberFormatter* pFormatter,
                             std::vector<formula::FormulaConstTokenRef>& rRes,
                             SCROW nIndex,
                             SCROW nLastIndex) :
@@ -162,6 +163,7 @@ public:
         maBatchTopPos(aBatchTopPos),
         mrTopPos(rTopPos),
         mrDoc(rDoc),
+        mpFormatter(pFormatter),
         mrResults(rRes),
         mnIdx(nIndex),
         mnLastIdx(nLastIndex)
@@ -296,7 +298,8 @@ public:
 
             ScCompiler aComp(&mrDoc, maBatchTopPos, aCode2);
             aComp.CompileTokenArray();
-            ScInterpreter aInterpreter(pDest, &mrDoc, mrDoc.GetNonThreadedContext(), maBatchTopPos, aCode2);
+            ScInterpreterContext aContext(mrDoc, mpFormatter);
+            ScInterpreter aInterpreter(pDest, &mrDoc, aContext, maBatchTopPos, aCode2);
             aInterpreter.Interpret();
             mrResults[i] = aInterpreter.GetResultToken();
         } // Row iteration for loop end
@@ -307,6 +310,7 @@ private:
     ScAddress maBatchTopPos;
     const ScAddress& mrTopPos;
     ScDocument& mrDoc;
+    SvNumberFormatter* mpFormatter;
     std::vector<formula::FormulaConstTokenRef>& mrResults;
     SCROW mnIdx;
     SCROW mnLastIdx;
@@ -333,11 +337,12 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
                  ScAddress aBatchTopPos,
                  const ScAddress& rTopPos2,
                  ScDocument& rDoc2,
+                 SvNumberFormatter* pFormatter2,
                  std::vector<formula::FormulaConstTokenRef>& rRes,
                  SCROW nIndex,
                  SCROW nLastIndex) :
             comphelper::ThreadTask(rTag),
-            maSWIFunc(rCode2, aBatchTopPos, rTopPos2, rDoc2, rRes, nIndex, nLastIndex)
+            maSWIFunc(rCode2, aBatchTopPos, rTopPos2, rDoc2, pFormatter2, rRes, nIndex, nLastIndex)
         {
         }
         virtual void doWork() override
@@ -353,6 +358,8 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
 
     bool bUseThreading = !bThreadingProhibited && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get();
 
+    SvNumberFormatter* pFormatter = rDoc.GetNonThreadedContext().GetFormatTable();
+
     if (bUseThreading)
     {
         comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
@@ -381,7 +388,7 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
             if ( nRemaining )
                 --nRemaining;
             SCROW nLast = nStart + nCount - 1;
-            rThreadPool.pushTask(new Executor(aTag, rCode, aTmpPos, rTopPos, rDoc, aResults, nStart, nLast));
+            rThreadPool.pushTask(new Executor(aTag, rCode, aTmpPos, rTopPos, rDoc, pFormatter, aResults, nStart, nLast));
             aTmpPos.IncRow(nCount);
             nLeft -= nCount;
             nStart = nLast + 1;
@@ -392,7 +399,7 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
     }
     else
     {
-        SoftwareInterpreterFunc aSWIFunc(rCode, aTmpPos, rTopPos, rDoc, aResults, 0, xGroup->mnLength - 1);
+        SoftwareInterpreterFunc aSWIFunc(rCode, aTmpPos, rTopPos, rDoc, pFormatter, aResults, 0, xGroup->mnLength - 1);
         aSWIFunc();
     }
 
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index 106e3b6f3403..ec9f9fb0e16c 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -1730,15 +1730,13 @@ void ScInterpreter::QueryMatrixType(const ScMatrixRef& xMat, short& rRetTypeExpr
 
 formula::FormulaToken* ScInterpreter::CreateFormulaDoubleToken( double fVal, short nFmt )
 {
-    if ( maTokenCache.size() != TOKEN_CACHE_SIZE )
-        maTokenCache.resize( TOKEN_CACHE_SIZE );
+    assert( mrContext.maTokens.size() == TOKEN_CACHE_SIZE );
 
     // Find a spare token
-    for ( auto p : maTokenCache )
+    for ( auto p : mrContext.maTokens )
     {
         if (p && p->GetRef() == 1)
         {
-            p->IncRef();
             p->GetDoubleAsReference() = fVal;
             p->SetDoubleType( nFmt );
             return p;
@@ -1747,12 +1745,11 @@ formula::FormulaToken* ScInterpreter::CreateFormulaDoubleToken( double fVal, sho
 
     // Allocate a new token
     auto p = new FormulaTypedDoubleToken( fVal, nFmt );
-    size_t pos = (mnTokenCachePos++) % TOKEN_CACHE_SIZE;
-    if ( maTokenCache[pos] )
-        maTokenCache[pos]->DecRef();
-    maTokenCache[pos] = p;
+    if ( mrContext.maTokens[mrContext.mnTokenCachePos] )
+        mrContext.maTokens[mrContext.mnTokenCachePos]->DecRef();
+    mrContext.maTokens[mrContext.mnTokenCachePos] = p;
     p->IncRef();
-
+    mrContext.mnTokenCachePos = (mrContext.mnTokenCachePos + 1) % TOKEN_CACHE_SIZE;
     return p;
 }
 
@@ -3810,7 +3807,7 @@ void ScInterpreter::ScTTT()
     PushError(FormulaError::NoValue);
 }
 
-ScInterpreter::ScInterpreter( ScFormulaCell* pCell, ScDocument* pDoc, const ScInterpreterContext& rContext,
+ScInterpreter::ScInterpreter( ScFormulaCell* pCell, ScDocument* pDoc, ScInterpreterContext& rContext,
         const ScAddress& rPos, ScTokenArray& r )
     : aCode(r)
     , aPos(rPos)
@@ -3870,11 +3867,6 @@ ScInterpreter::~ScInterpreter()
     else
         delete pStackObj;
     delete pTokenMatrixMap;
-
-    for ( auto p : maTokenCache )
-        if ( p && p->GetRef() == 1 )
-            p->DecRef();
-
 }
 
 ScCalcConfig& ScInterpreter::GetOrCreateGlobalConfig()
commit 5ab9b543269070e41d021c24e2b552a13b9fae37
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Fri Nov 17 14:05:56 2017 +0530

    Type check the tokens before reuse
    
    If the exisiting token is of wrong type, create and use a fresh new
    token instead.
    
    Change-Id: I348b0972306497dfe7eae0655c9b93d5830cb740

diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index e77554d89805..326392f6099e 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -206,13 +206,26 @@ public:
                             if ( !pTargetTok )
                                 aCode2.AddString(rPool.intern(OUString(pStr)));
                             else
-                                pTargetTok->SetString(rPool.intern(OUString(pStr)));
+                            {
+                                if ( pTargetTok->GetType() == formula::svString )
+                                    pTargetTok->SetString(rPool.intern(OUString(pStr)));
+                                else
+                                {
+                                    formula::FormulaStringToken* pStrTok = new formula::FormulaStringToken(rPool.intern(OUString(pStr)));
+                                    aCode2.ReplaceToken(nTokIdx, pStrTok, formula::FormulaTokenArray::CODE_ONLY);
+                                }
+                            }
                         }
                         else if (rtl::math::isNan(fVal))
                         {
                             // Value of NaN represents an empty cell.
                             if ( !pTargetTok )
                                 aCode2.AddToken(ScEmptyCellToken(false, false));
+                            else if ( pTargetTok->GetType() != formula::svEmptyCell )
+                            {
+                                ScEmptyCellToken* pEmptyTok = new ScEmptyCellToken(false, false);
+                                aCode2.ReplaceToken(nTokIdx, pEmptyTok, formula::FormulaTokenArray::CODE_ONLY);
+                            }
                         }
                         else
                         {
@@ -220,7 +233,15 @@ public:
                             if ( !pTargetTok )
                                 aCode2.AddDouble(fVal);
                             else
-                                pTargetTok->GetDoubleAsReference() = fVal;
+                            {
+                                if ( pTargetTok->GetType() == formula::svDouble )
+                                    pTargetTok->GetDoubleAsReference() = fVal;
+                                else
+                                {
+                                    formula::FormulaDoubleToken* pDoubleTok = new formula::FormulaDoubleToken( fVal );
+                                    aCode2.ReplaceToken(nTokIdx, pDoubleTok, formula::FormulaTokenArray::CODE_ONLY);
+                                }
+                            }
                         }
                     }
                     break;
commit f42dc9786efba0a0f07d745773bfc8bea42f0b88
Author: Eike Rathke <erack at redhat.com>
Date:   Wed Nov 15 18:08:30 2017 +0100

    Assert also new FormulaToken::SetDoubleType() virtual dummy
    
    Change-Id: I4532d0329dc7cd2609bc96abba140aba3f3d36f3

diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx
index 2ed68521e720..07ff7cc59b6e 100644
--- a/formula/source/core/api/token.cxx
+++ b/formula/source/core/api/token.cxx
@@ -218,7 +218,7 @@ short FormulaToken::GetDoubleType() const
 
 void FormulaToken::SetDoubleType( short )
 {
-    SAL_WARN( "formula.core", "FormulaToken::SetDoubleType: virtual dummy called" );
+    assert( !"virtual dummy called" );
 }
 
 svl::SharedString FormulaToken::GetString() const
commit dc90516a2e3665a527adcd8a08fcac7795c3e54c
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 21:08:44 2017 +0530

    cache FormulaToken for doubles
    
    Change-Id: Ic0b4dff6f03ef3f88bd150e798fa2d83dfb0f486

diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx
index 04c4a203284d..2ed68521e720 100644
--- a/formula/source/core/api/token.cxx
+++ b/formula/source/core/api/token.cxx
@@ -216,6 +216,11 @@ short FormulaToken::GetDoubleType() const
     return 0;
 }
 
+void FormulaToken::SetDoubleType( short )
+{
+    SAL_WARN( "formula.core", "FormulaToken::SetDoubleType: virtual dummy called" );
+}
+
 svl::SharedString FormulaToken::GetString() const
 {
     SAL_WARN( "formula.core", "FormulaToken::GetString: virtual dummy called" );
@@ -1797,6 +1802,11 @@ short FormulaTypedDoubleToken::GetDoubleType() const
     return mnType;
 }
 
+void FormulaTypedDoubleToken::SetDoubleType( short nType )
+{
+    mnType = nType;
+}
+
 bool FormulaTypedDoubleToken::operator==( const FormulaToken& r ) const
 {
     return FormulaDoubleToken::operator==( r ) && mnType == r.GetDoubleType();
diff --git a/include/formula/token.hxx b/include/formula/token.hxx
index 9b06ae48182c..3de7c761d658 100644
--- a/include/formula/token.hxx
+++ b/include/formula/token.hxx
@@ -181,6 +181,7 @@ public:
     virtual double              GetDouble() const;
     virtual double&             GetDoubleAsReference();
     virtual short               GetDoubleType() const;
+    virtual void                SetDoubleType( short nType );
     virtual svl::SharedString   GetString() const;
     virtual void                SetString( const svl::SharedString& rStr );
     virtual sal_uInt16          GetIndex() const;
@@ -321,6 +322,7 @@ public:
 
     virtual FormulaToken*       Clone() const override { return new FormulaTypedDoubleToken(*this); }
     virtual short               GetDoubleType() const override;
+    virtual void                SetDoubleType( short nType ) override;
     virtual bool                operator==( const FormulaToken& rToken ) const override;
 
     DECL_FIXEDMEMPOOL_NEWDEL_DLL( FormulaTypedDoubleToken )
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
index 8a240e617261..a6eaf5cd78ae 100644
--- a/sc/source/core/inc/interpre.hxx
+++ b/sc/source/core/inc/interpre.hxx
@@ -97,6 +97,7 @@ class SharedStringPool;
 }
 
 #define MAXSTACK      (4096 / sizeof(formula::FormulaToken*))
+#define TOKEN_CACHE_SIZE 8
 
 class ScTokenStack
 {
@@ -229,6 +230,8 @@ private:
     bool        bMatrixFormula;         // formula cell is a matrix formula
 
     VolatileType meVolatileType;
+    size_t       mnTokenCachePos;
+    std::vector<formula::FormulaToken*> maTokenCache;
 
     /// Merge global and document specific settings.
     void MergeCalcConfig();
@@ -395,6 +398,7 @@ private:
     sc::RangeMatrix PopRangeMatrix();
     void QueryMatrixType(const ScMatrixRef& xMat, short& rRetTypeExpr, sal_uLong& rRetIndexExpr);
 
+    formula::FormulaToken* CreateFormulaDoubleToken( double fVal, short nFmt = css::util::NumberFormat::NUMBER );
     formula::FormulaToken* CreateDoubleOrTypedToken( double fVal );
 
     void PushDouble(double nVal);
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index a95d76b42cf0..106e3b6f3403 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -731,7 +731,7 @@ void ScInterpreter::PushCellResultToken( bool bDisplayEmptyAsString,
         {
             TreatDoubleError( fVal);
             if (!IfErrorPushError())
-                PushTempTokenWithoutError( new FormulaDoubleToken( fVal));
+                PushTempTokenWithoutError( CreateFormulaDoubleToken( fVal));
         }
         else
         {
@@ -1687,7 +1687,7 @@ void ScInterpreter::QueryMatrixType(const ScMatrixRef& xMat, short& rRetTypeExpr
         {
             if ( xMat->IsEmptyPath( 0, 0))
             {   // result of empty FALSE jump path
-                FormulaTokenRef xRes = new FormulaDoubleToken( 0.0);
+                FormulaTokenRef xRes = CreateFormulaDoubleToken( 0.0);
                 PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
                 rRetTypeExpr = css::util::NumberFormat::LOGICAL;
             }
@@ -1716,7 +1716,7 @@ void ScInterpreter::QueryMatrixType(const ScMatrixRef& xMat, short& rRetTypeExpr
             if (nErr != FormulaError::NONE)
                 xRes = new FormulaErrorToken( nErr);
             else
-                xRes = new FormulaDoubleToken( nMatVal.fVal);
+                xRes = CreateFormulaDoubleToken( nMatVal.fVal);
             PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
             if ( rRetTypeExpr != css::util::NumberFormat::LOGICAL )
                 rRetTypeExpr = css::util::NumberFormat::NUMBER;
@@ -1728,14 +1728,42 @@ void ScInterpreter::QueryMatrixType(const ScMatrixRef& xMat, short& rRetTypeExpr
         SetError( FormulaError::UnknownStackVariable);
 }
 
+formula::FormulaToken* ScInterpreter::CreateFormulaDoubleToken( double fVal, short nFmt )
+{
+    if ( maTokenCache.size() != TOKEN_CACHE_SIZE )
+        maTokenCache.resize( TOKEN_CACHE_SIZE );
+
+    // Find a spare token
+    for ( auto p : maTokenCache )
+    {
+        if (p && p->GetRef() == 1)
+        {
+            p->IncRef();
+            p->GetDoubleAsReference() = fVal;
+            p->SetDoubleType( nFmt );
+            return p;
+        }
+    }
+
+    // Allocate a new token
+    auto p = new FormulaTypedDoubleToken( fVal, nFmt );
+    size_t pos = (mnTokenCachePos++) % TOKEN_CACHE_SIZE;
+    if ( maTokenCache[pos] )
+        maTokenCache[pos]->DecRef();
+    maTokenCache[pos] = p;
+    p->IncRef();
+
+    return p;
+}
+
 formula::FormulaToken* ScInterpreter::CreateDoubleOrTypedToken( double fVal )
 {
     // NumberFormat::NUMBER is the default untyped double.
     if (nFuncFmtType && nFuncFmtType != css::util::NumberFormat::NUMBER &&
             nFuncFmtType != css::util::NumberFormat::UNDEFINED)
-        return new FormulaTypedDoubleToken( fVal, nFuncFmtType);
+        return CreateFormulaDoubleToken( fVal, nFuncFmtType);
     else
-        return new FormulaDoubleToken( fVal);
+        return CreateFormulaDoubleToken( fVal);
 }
 
 void ScInterpreter::PushDouble(double nVal)
@@ -3842,6 +3870,11 @@ ScInterpreter::~ScInterpreter()
     else
         delete pStackObj;
     delete pTokenMatrixMap;
+
+    for ( auto p : maTokenCache )
+        if ( p && p->GetRef() == 1 )
+            p->DecRef();
+
 }
 
 ScCalcConfig& ScInterpreter::GetOrCreateGlobalConfig()
@@ -4583,7 +4616,7 @@ StackVar ScInterpreter::Interpret()
                                     nRetIndexExpr = 0;  // carry format index only for matching type
                                 nRetTypeExpr = nFuncFmtType = nCurFmtType;
                             }
-                            PushTempToken( new FormulaDoubleToken( fVal));
+                            PushTempToken( CreateFormulaDoubleToken( fVal));
                         }
                         if ( nFuncFmtType == css::util::NumberFormat::UNDEFINED )
                         {
commit 246b97c3daee18bf2fffa8d1d5ffbf3d24129502
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 17:09:08 2017 +0530

    halve thread count if HT active for group interpreter too
    
    Change-Id: Iacc93122191152183127500a4172358a14e96c8b

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index e0ee5a1827ad..f90e83cb6c06 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -49,6 +49,7 @@
 #include <tokenarray.hxx>
 
 #include <comphelper/threadpool.hxx>
+#include <tools/cpuid.hxx>
 #include <formula/errorcodes.hxx>
 #include <formula/vectortoken.hxx>
 #include <svl/intitem.hxx>
@@ -4364,6 +4365,8 @@ bool ScFormulaCell::InterpretFormulaGroup()
             return false;
         }
 
+        static bool bHyperThreadingActive = tools::cpuid::hasHyperThreading();
+
         // Then do the threaded calculation
 
         class Executor : public comphelper::ThreadTask
@@ -4408,6 +4411,9 @@ bool ScFormulaCell::InterpretFormulaGroup()
         comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
         sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
 
+        if ( bHyperThreadingActive && nThreadCount >= 2 )
+            nThreadCount /= 2;
+
         SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
 
         {
commit 7ab90bd124cb1f2948da9aff214f1cec68433ea0
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 16:52:44 2017 +0530

    Disable custom allocator
    
    This has big positive effect on software interpreter threading
    performance scaling.
    
    Change-Id: I8fbb6bf8f7ed410fd53278acee63bf65f13bac38

diff --git a/sal/rtl/alloc_global.cxx b/sal/rtl/alloc_global.cxx
index 3d74287bc7e5..47643f3ea74a 100644
--- a/sal/rtl/alloc_global.cxx
+++ b/sal/rtl/alloc_global.cxx
@@ -35,7 +35,7 @@ AllocMode alloc_mode = AllocMode::UNSET;
 static void determine_alloc_mode()
 {
     assert(alloc_mode == AllocMode::UNSET);
-    alloc_mode = (getenv("G_SLICE") == nullptr ? AllocMode::CUSTOM : AllocMode::SYSTEM);
+    alloc_mode = AllocMode::SYSTEM;
 }
 
 static const sal_Size g_alloc_sizes[] =
commit 8f3a77c05420dfb1bb22b6fe9a13c596ab7fc261
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 16:37:45 2017 +0530

    halve the number of threads if HT is active
    
    added hasHyperThreading() function to tools::cpuid
    to detect hyperthreading.
    
    Change-Id: I13fab4b6c649e681c329b7e3f4c9f36bda879d84

diff --git a/include/tools/cpuid.hxx b/include/tools/cpuid.hxx
index d7aa07d54258..419d05714ae4 100644
--- a/include/tools/cpuid.hxx
+++ b/include/tools/cpuid.hxx
@@ -25,6 +25,7 @@ namespace tools
 namespace cpuid
 {
     TOOLS_DLLPUBLIC bool hasSSE2();
+    TOOLS_DLLPUBLIC bool hasHyperThreading();
 }
 }
 
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index 7be682322221..e77554d89805 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -19,6 +19,7 @@
 #include <scmatrix.hxx>
 #include <globalnames.hxx>
 #include <comphelper/threadpool.hxx>
+#include <tools/cpuid.hxx>
 
 #include <formula/vectortoken.hxx>
 #include <officecfg/Office/Common.hxx>
@@ -299,6 +300,7 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
     // The caller must ensure that the top position is the start position of
     // the group.
 
+    static bool bHyperThreadingActive = tools::cpuid::hasHyperThreading();
     ScAddress aTmpPos = rTopPos;
     std::vector<formula::FormulaConstTokenRef> aResults(xGroup->mnLength);
 
@@ -335,6 +337,9 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
         comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
         sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
 
+        if ( bHyperThreadingActive && nThreadCount >= 2 )
+            nThreadCount /= 2;
+
         SCROW nLen = xGroup->mnLength;
         SCROW nBatchSize = nLen / nThreadCount;
         if (nLen < nThreadCount)
diff --git a/tools/source/misc/cpuid.cxx b/tools/source/misc/cpuid.cxx
index 1518dfc175c4..e3ba82dffda5 100644
--- a/tools/source/misc/cpuid.cxx
+++ b/tools/source/misc/cpuid.cxx
@@ -16,8 +16,6 @@ namespace tools
 namespace cpuid
 {
 
-#if defined(LO_SSE2_AVAILABLE)
-
 namespace
 {
 #if defined(_MSC_VER)
@@ -35,6 +33,8 @@ void getCpuId(uint32_t array[4])
 #endif
 }
 
+#if defined(LO_SSE2_AVAILABLE)
+
 bool hasSSE2()
 {
     uint32_t cpuInfoArray[] = {0, 0, 0, 0};
@@ -48,6 +48,13 @@ bool hasSSE2() { return false; }
 
 #endif
 
+bool hasHyperThreading()
+{
+    uint32_t cpuInfoArray[] = {0, 0, 0, 0};
+    getCpuId(cpuInfoArray);
+    return (cpuInfoArray[3] & (1 << 28)) != 0;
+}
+
 }
 }
 
commit 059d0a814d5f2933d7a6508afd4f9aac267ffdff
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 16:14:32 2017 +0530

    Avoid ScTokenArray thrash
    
    Allocate ScTokenArray object only once per worker thread, fill it
    for the first row/cell and reuse them for subsequent rows/cells
    if possible.
    
    Change-Id: If8f20da618938d0e189224f189b4763815702e10

diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index e22e294b9262..7be682322221 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -171,12 +171,14 @@ public:
     {
         double fNan;
         rtl::math::setNan(&fNan);
+        ScTokenArray aCode2;
         for (SCROW i = mnIdx; i <= mnLastIdx; ++i, maBatchTopPos.IncRow())
         {
-            ScTokenArray aCode2;
             formula::FormulaTokenArrayPlainIterator aIter(mrCode);
-            for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+            size_t nTokIdx = 0;
+            for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next(), ++nTokIdx)
             {
+                formula::FormulaToken* pTargetTok = aCode2.TokenAt(nTokIdx);
                 switch (p->GetType())
                 {
                     case formula::svSingleVectorRef:
@@ -200,14 +202,25 @@ public:
                         {
                             // This is a string cell.
                             svl::SharedStringPool& rPool = mrDoc.GetSharedStringPool();
-                            aCode2.AddString(rPool.intern(OUString(pStr)));
+                            if ( !pTargetTok )
+                                aCode2.AddString(rPool.intern(OUString(pStr)));
+                            else
+                                pTargetTok->SetString(rPool.intern(OUString(pStr)));
                         }
                         else if (rtl::math::isNan(fVal))
+                        {
                             // Value of NaN represents an empty cell.
-                            aCode2.AddToken(ScEmptyCellToken(false, false));
+                            if ( !pTargetTok )
+                                aCode2.AddToken(ScEmptyCellToken(false, false));
+                        }
                         else
+                        {
                             // Numeric cell.
-                            aCode2.AddDouble(fVal);
+                            if ( !pTargetTok )
+                                aCode2.AddDouble(fVal);
+                            else
+                                pTargetTok->GetDoubleAsReference() = fVal;
+                        }
                     }
                     break;
                     case formula::svDoubleVectorRef:
@@ -229,17 +242,29 @@ public:
                             aRefRange.aEnd.SetRow(mrTopPos.Row() + nRowEnd);
                             aRef.InitRange(aRefRange);
                             formula::FormulaTokenRef xTok(new ScMatrixRangeToken(pMat, aRef));
-                            aCode2.AddToken(*xTok);
+                            if ( !pTargetTok )
+                                aCode2.AddToken(*xTok);
+                            else
+                                aCode2.ReplaceToken(nTokIdx, xTok.get(), formula::FormulaTokenArray::CODE_ONLY);
                         }
                         else
                         {
-                            ScMatrixToken aTok(pMat);
-                            aCode2.AddToken(aTok);
+                            if ( !pTargetTok )
+                            {
+                                ScMatrixToken aTok(pMat);
+                                aCode2.AddToken(aTok);
+                            }
+                            else
+                            {
+                                ScMatrixToken* pMatTok = new ScMatrixToken(pMat);
+                                aCode2.ReplaceToken(nTokIdx, pMatTok, formula::FormulaTokenArray::CODE_ONLY);
+                            }
                         }
                     }
                     break;
                     default:
-                        aCode2.AddToken(*p);
+                        if ( !pTargetTok )
+                            aCode2.AddToken(*p);
                 } // end of switch statement
             } // end of formula token for loop
 
commit 64f8f2ef4d41bcd96b35ef203be5eb9e1de49bab
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 1 13:45:09 2017 +0530

    Thread the software interpreter
    
    Also introduce new state
    
    ScFormulaVectorState::FormulaVectorEnabledForThreading
    
    to indicate that using the “traditional” vectoring is disabled,
    but threading should be tried.
    
    Change-Id: I552d9e29e1ab9e5721534e07f4a45fdd5a23f399

diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx
index 294794f959e0..8b352b447f09 100644
--- a/sc/inc/tokenarray.hxx
+++ b/sc/inc/tokenarray.hxx
@@ -92,6 +92,7 @@ public:
         svl::SharedStringPool& rSPool,
         formula::ExternalReferenceHelper* _pRef) override;
     virtual void CheckToken( const formula::FormulaToken& r ) override;
+    void CheckForThreading( OpCode eOp );
     virtual formula::FormulaToken* AddOpCode( OpCode eCode ) override;
     /** ScSingleRefToken with ocPush. */
     formula::FormulaToken* AddSingleReference( const ScSingleRefData& rRef );
diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx
index 51898c291fde..cc82e363765d 100644
--- a/sc/inc/types.hxx
+++ b/sc/inc/types.hxx
@@ -59,6 +59,7 @@ enum ScFormulaVectorState
 
     FormulaVectorEnabled,
     FormulaVectorCheckReference,
+    FormulaVectorEnabledForThreading,
     FormulaVectorUnknown
 };
 
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 36d4997ba157..e0ee5a1827ad 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -4345,7 +4345,9 @@ bool ScFormulaCell::InterpretFormulaGroup()
         return false;
     }
 
-    if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() && pCode->GetVectorState() != FormulaVectorDisabledNotInSubSet && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
+    if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() &&
+        pCode->GetVectorState() == FormulaVectorEnabledForThreading &&
+        officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
     {
         // iterate over code in the formula ...
         // ensure all input is pre-calculated -
@@ -4434,26 +4436,30 @@ bool ScFormulaCell::InterpretFormulaGroup()
         return true;
     }
 
+    bool bCanVectorize = false;
     switch (pCode->GetVectorState())
     {
         case FormulaVectorEnabled:
         case FormulaVectorCheckReference:
-            // Good.
+            bCanVectorize = true; // Good.
         break;
 
         // Not good.
         case FormulaVectorDisabledByOpCode:
             aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
-            return false;
+            break;
         case FormulaVectorDisabledNotInSoftwareSubset:
             aScope.addMessage("group calc disabled due to vector state (opcode not in software subset)");
-            return false;
+            break;
         case FormulaVectorDisabledByStackVariable:
             aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
-            return false;
+            break;
         case FormulaVectorDisabledNotInSubSet:
             aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
-            return false;
+            break;
+        case FormulaVectorEnabledForThreading:
+            aScope.addMessage("group calc disabled due to vector state (wanted to try threading but couldn't)");
+            break;
         case FormulaVectorDisabled:
         case FormulaVectorUnknown:
         default:
@@ -4461,6 +4467,9 @@ bool ScFormulaCell::InterpretFormulaGroup()
             return false;
     }
 
+    if (!bCanVectorize)
+        return false;
+
     if (!ScCalcConfig::isOpenCLEnabled() && !ScCalcConfig::isSwInterpreterEnabled())
     {
         aScope.addMessage("opencl not enabled and sw interpreter not enabled");
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index f637ab5753bc..e22e294b9262 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -18,9 +18,11 @@
 #include <interpre.hxx>
 #include <scmatrix.hxx>
 #include <globalnames.hxx>
+#include <comphelper/threadpool.hxx>
 
 #include <formula/vectortoken.hxx>
 #include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Calc.hxx>
 #if HAVE_FEATURE_OPENCL
 #include <opencl/platforminfo.hxx>
 #endif
@@ -145,117 +147,208 @@ ScMatrixRef FormulaGroupInterpreterSoftware::inverseMatrix(const ScMatrix& /*rMa
     return ScMatrixRef();
 }
 
+class SoftwareInterpreterFunc
+{
+public:
+    SoftwareInterpreterFunc(ScTokenArray& rCode,
+                            ScAddress aBatchTopPos,
+                            const ScAddress& rTopPos,
+                            ScDocument& rDoc,
+                            std::vector<formula::FormulaConstTokenRef>& rRes,
+                            SCROW nIndex,
+                            SCROW nLastIndex) :
+        mrCode(rCode),
+        maBatchTopPos(aBatchTopPos),
+        mrTopPos(rTopPos),
+        mrDoc(rDoc),
+        mrResults(rRes),
+        mnIdx(nIndex),
+        mnLastIdx(nLastIndex)
+    {
+    }
+
+    void operator() ()
+    {
+        double fNan;
+        rtl::math::setNan(&fNan);
+        for (SCROW i = mnIdx; i <= mnLastIdx; ++i, maBatchTopPos.IncRow())
+        {
+            ScTokenArray aCode2;
+            formula::FormulaTokenArrayPlainIterator aIter(mrCode);
+            for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+            {
+                switch (p->GetType())
+                {
+                    case formula::svSingleVectorRef:
+                    {
+                        const formula::SingleVectorRefToken* p2 = static_cast<const formula::SingleVectorRefToken*>(p);
+                        const formula::VectorRefArray& rArray = p2->GetArray();
+
+                        rtl_uString* pStr = nullptr;
+                        double fVal = fNan;
+                        if (static_cast<size_t>(i) < p2->GetArrayLength())
+                        {
+                            if (rArray.mpStringArray)
+                                // See if the cell is of string type.
+                                pStr = rArray.mpStringArray[i];
+
+                            if (!pStr && rArray.mpNumericArray)
+                                fVal = rArray.mpNumericArray[i];
+                        }
+
+                        if (pStr)
+                        {
+                            // This is a string cell.
+                            svl::SharedStringPool& rPool = mrDoc.GetSharedStringPool();
+                            aCode2.AddString(rPool.intern(OUString(pStr)));
+                        }
+                        else if (rtl::math::isNan(fVal))
+                            // Value of NaN represents an empty cell.
+                            aCode2.AddToken(ScEmptyCellToken(false, false));
+                        else
+                            // Numeric cell.
+                            aCode2.AddDouble(fVal);
+                    }
+                    break;
+                    case formula::svDoubleVectorRef:
+                    {
+                        const formula::DoubleVectorRefToken* p2 = static_cast<const formula::DoubleVectorRefToken*>(p);
+                        size_t nRowStart = p2->IsStartFixed() ? 0 : i;
+                        size_t nRowEnd = p2->GetRefRowSize() - 1;
+                        if (!p2->IsEndFixed())
+                            nRowEnd += i;
+
+                        assert(nRowStart <= nRowEnd);
+                        ScMatrixRef pMat(new ScVectorRefMatrix(p2, nRowStart, nRowEnd - nRowStart + 1));
+
+                        if (p2->IsStartFixed() && p2->IsEndFixed())
+                        {
+                            // Cached the converted token for absolute range reference.
+                            ScComplexRefData aRef;
+                            ScRange aRefRange = mrTopPos;
+                            aRefRange.aEnd.SetRow(mrTopPos.Row() + nRowEnd);
+                            aRef.InitRange(aRefRange);
+                            formula::FormulaTokenRef xTok(new ScMatrixRangeToken(pMat, aRef));
+                            aCode2.AddToken(*xTok);
+                        }
+                        else
+                        {
+                            ScMatrixToken aTok(pMat);
+                            aCode2.AddToken(aTok);
+                        }
+                    }
+                    break;
+                    default:
+                        aCode2.AddToken(*p);
+                } // end of switch statement
+            } // end of formula token for loop
+
+            ScFormulaCell* pDest = mrDoc.GetFormulaCell(maBatchTopPos);
+            if (!pDest)
+                return;
+
+            ScCompiler aComp(&mrDoc, maBatchTopPos, aCode2);
+            aComp.CompileTokenArray();
+            ScInterpreter aInterpreter(pDest, &mrDoc, mrDoc.GetNonThreadedContext(), maBatchTopPos, aCode2);
+            aInterpreter.Interpret();
+            mrResults[i] = aInterpreter.GetResultToken();
+        } // Row iteration for loop end
+    } // operator () end
+
+private:
+    ScTokenArray& mrCode;
+    ScAddress maBatchTopPos;
+    const ScAddress& mrTopPos;
+    ScDocument& mrDoc;
+    std::vector<formula::FormulaConstTokenRef>& mrResults;
+    SCROW mnIdx;
+    SCROW mnLastIdx;
+};
+
 bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddress& rTopPos,
                                                 ScFormulaCellGroupRef& xGroup,
                                                 ScTokenArray& rCode)
 {
-    typedef std::unordered_map<const formula::FormulaToken*, formula::FormulaTokenRef> CachedTokensType;
-
     // Decompose the group into individual cells and calculate them individually.
 
     // The caller must ensure that the top position is the start position of
     // the group.
 
     ScAddress aTmpPos = rTopPos;
-    std::vector<formula::FormulaConstTokenRef> aResults;
-    aResults.reserve(xGroup->mnLength);
-    CachedTokensType aCachedTokens;
-
-    double fNan;
-    rtl::math::setNan(&fNan);
+    std::vector<formula::FormulaConstTokenRef> aResults(xGroup->mnLength);
 
-    for (SCROW i = 0; i < xGroup->mnLength; ++i, aTmpPos.IncRow())
+    class Executor : public comphelper::ThreadTask
     {
-        ScTokenArray aCode2;
-        formula::FormulaTokenArrayPlainIterator aIter(rCode);
-        for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+    public:
+        Executor(std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
+                 ScTokenArray& rCode2,
+                 ScAddress aBatchTopPos,
+                 const ScAddress& rTopPos2,
+                 ScDocument& rDoc2,
+                 std::vector<formula::FormulaConstTokenRef>& rRes,
+                 SCROW nIndex,
+                 SCROW nLastIndex) :
+            comphelper::ThreadTask(rTag),
+            maSWIFunc(rCode2, aBatchTopPos, rTopPos2, rDoc2, rRes, nIndex, nLastIndex)
         {
-            CachedTokensType::iterator it = aCachedTokens.find(p);
-            if (it != aCachedTokens.end())
-            {
-                // This token is cached. Use the cached one.
-                aCode2.AddToken(*it->second);
-                continue;
-            }
+        }
+        virtual void doWork() override
+        {
+            maSWIFunc();
+        }
 
-            switch (p->GetType())
-            {
-                case formula::svSingleVectorRef:
-                {
-                    const formula::SingleVectorRefToken* p2 = static_cast<const formula::SingleVectorRefToken*>(p);
-                    const formula::VectorRefArray& rArray = p2->GetArray();
+    private:
+        SoftwareInterpreterFunc maSWIFunc;
+    };
 
-                    rtl_uString* pStr = nullptr;
-                    double fVal = fNan;
-                    if (static_cast<size_t>(i) < p2->GetArrayLength())
-                    {
-                        if (rArray.mpStringArray)
-                            // See if the cell is of string type.
-                            pStr = rArray.mpStringArray[i];
+    static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
 
-                        if (!pStr && rArray.mpNumericArray)
-                            fVal = rArray.mpNumericArray[i];
-                    }
+    bool bUseThreading = !bThreadingProhibited && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get();
 
-                    if (pStr)
-                    {
-                        // This is a string cell.
-                        svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
-                        aCode2.AddString(rPool.intern(OUString(pStr)));
-                    }
-                    else if (rtl::math::isNan(fVal))
-                        // Value of NaN represents an empty cell.
-                        aCode2.AddToken(ScEmptyCellToken(false, false));
-                    else
-                        // Numeric cell.
-                        aCode2.AddDouble(fVal);
-                }
-                break;
-                case formula::svDoubleVectorRef:
-                {
-                    const formula::DoubleVectorRefToken* p2 = static_cast<const formula::DoubleVectorRefToken*>(p);
-                    size_t nRowStart = p2->IsStartFixed() ? 0 : i;
-                    size_t nRowEnd = p2->GetRefRowSize() - 1;
-                    if (!p2->IsEndFixed())
-                        nRowEnd += i;
+    if (bUseThreading)
+    {
+        comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
+        sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
+
+        SCROW nLen = xGroup->mnLength;
+        SCROW nBatchSize = nLen / nThreadCount;
+        if (nLen < nThreadCount)
+        {
+            nBatchSize = 1;
+            nThreadCount = nLen;
+        }
+        SCROW nRemaining = nLen - nBatchSize * nThreadCount;
 
-                    assert(nRowStart <= nRowEnd);
-                    ScMatrixRef pMat(new ScVectorRefMatrix(p2, nRowStart, nRowEnd - nRowStart + 1));
+        SAL_INFO("sc.swipret.threaded", "Running " << nThreadCount << " threads");
 
-                    if (p2->IsStartFixed() && p2->IsEndFixed())
-                    {
-                        // Cached the converted token for absolute range reference.
-                        ScComplexRefData aRef;
-                        ScRange aRefRange = rTopPos;
-                        aRefRange.aEnd.SetRow(rTopPos.Row() + nRowEnd);
-                        aRef.InitRange(aRefRange);
-                        formula::FormulaTokenRef xTok(new ScMatrixRangeToken(pMat, aRef));
-                        aCachedTokens.emplace(p, xTok);
-                        aCode2.AddToken(*xTok);
-                    }
-                    else
-                    {
-                        ScMatrixToken aTok(pMat);
-                        aCode2.AddToken(aTok);
-                    }
-                }
-                break;
-                default:
-                    aCode2.AddToken(*p);
-            }
+        SCROW nLeft = nLen;
+        SCROW nStart = 0;
+        std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
+        while (nLeft > 0)
+        {
+            SCROW nCount = std::min(nLeft, nBatchSize) + (nRemaining ? 1 : 0);
+            if ( nRemaining )
+                --nRemaining;
+            SCROW nLast = nStart + nCount - 1;
+            rThreadPool.pushTask(new Executor(aTag, rCode, aTmpPos, rTopPos, rDoc, aResults, nStart, nLast));
+            aTmpPos.IncRow(nCount);
+            nLeft -= nCount;
+            nStart = nLast + 1;
         }
+        SAL_INFO("sc.swipret.threaded", "Joining threads");
+        rThreadPool.waitUntilDone(aTag);
+        SAL_INFO("sc.swipret.threaded", "Done");
+    }
+    else
+    {
+        SoftwareInterpreterFunc aSWIFunc(rCode, aTmpPos, rTopPos, rDoc, aResults, 0, xGroup->mnLength - 1);
+        aSWIFunc();
+    }
 
-        ScFormulaCell* pDest = rDoc.GetFormulaCell(aTmpPos);
-        if (!pDest)
+    for (SCROW i = 0; i < xGroup->mnLength; ++i)
+        if (!aResults[i].get())
             return false;
 
-        ScCompiler aComp(&rDoc, aTmpPos, aCode2);
-        aComp.CompileTokenArray();
-        ScInterpreter aInterpreter(pDest, &rDoc, rDoc.GetNonThreadedContext(), aTmpPos, aCode2);
-        aInterpreter.Interpret();
-        aResults.push_back(aInterpreter.GetResultToken());
-    } // for loop end (xGroup->mnLength)
-
     if (!aResults.empty())
         rDoc.SetFormulaResults(rTopPos, &aResults[0], aResults.size());
 
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 5349e2d6dff3..2ded45291544 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -1336,7 +1336,7 @@ bool ScTokenArray::AddFormulaToken(
     return bError;
 }
 
-void ScTokenArray::CheckToken( const FormulaToken& r )
+void ScTokenArray::CheckForThreading( OpCode eOp  )
 {
     static const std::set<OpCode> aThreadedCalcBlackList({
         ocIndirect,
@@ -1345,23 +1345,32 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
         ocTableOp
     });
 
-    if (IsFormulaVectorDisabled())
-        // It's already disabled.  No more checking needed.
-        return;
+    // We only call this if it was already disabled
+    assert(IsFormulaVectorDisabled());
 
     static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
 
-    OpCode eOp = r.GetOpCode();
-
     if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
     {
         if (aThreadedCalcBlackList.count(eOp))
         {
-            meVectorState = FormulaVectorDisabledNotInSubSet;
             SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables threaded calculation of formula group");
         }
-        return;
+        else
+        {
+            SAL_INFO("sc.core.formulagroup", "but enabling for threading instead");
+            meVectorState = FormulaVectorEnabledForThreading;
+        }
     }
+}
+
+void ScTokenArray::CheckToken( const FormulaToken& r )
+{
+    if (IsFormulaVectorDisabled())
+        // It's already disabled.  No more checking needed.
+        return;
+
+    OpCode eOp = r.GetOpCode();
 
     if (SC_OPCODE_START_FUNCTION <= eOp && eOp < SC_OPCODE_STOP_FUNCTION)
     {
@@ -1370,6 +1379,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
         {
             SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables vectorisation for formula group");
             meVectorState = FormulaVectorDisabledNotInSubSet;
+            CheckForThreading(eOp);
             return;
         }
 
@@ -1381,6 +1391,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
         {
             SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables S/W interpreter for formula group");
             meVectorState = FormulaVectorDisabledNotInSoftwareSubset;
+            CheckForThreading(eOp);
             return;
         }
 
@@ -1608,6 +1619,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
                 // We don't support vectorization on these.
                 SAL_INFO("sc.opencl", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType()) << " disables vectorisation for formula group");
                 meVectorState = FormulaVectorDisabledByStackVariable;
+                CheckForThreading(eOp);
             break;
             default:
                 ;
@@ -1619,6 +1631,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
     {
         SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables vectorisation for formula group");
         meVectorState = FormulaVectorDisabledNotInSubSet;
+        CheckForThreading(eOp);
     }
     // only when openCL interpreter is not enabled - the assumption is that
     // the S/W interpreter blacklist is more strict
@@ -1629,6 +1642,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
     {
         SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables S/W interpreter for formula group");
         meVectorState = FormulaVectorDisabledNotInSoftwareSubset;
+        CheckForThreading(eOp);
     }
 }
 
@@ -1756,6 +1770,7 @@ bool ScTokenArray::IsFormulaVectorDisabled() const
         case FormulaVectorDisabledNotInSoftwareSubset:
         case FormulaVectorDisabledByStackVariable:
         case FormulaVectorDisabledNotInSubSet:
+        case FormulaVectorEnabledForThreading:
             return true;
         default:
             ;
commit 718e8606714300d7a587b60f17aeb893abcac8f0
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Oct 16 18:40:51 2017 +0300

    Add OFFSET to blacklist for threaded calculation
    
    Change-Id: Ia1aaf40aa4e8e6f41ca190272365528bf37bf130

diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index b7c73ef7ea3d..5349e2d6dff3 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -1341,6 +1341,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
     static const std::set<OpCode> aThreadedCalcBlackList({
         ocIndirect,
         ocMacro,
+        ocOffset,
         ocTableOp
     });
 
commit bfe5b5cb584eae3cecc9826ed16664518f323353
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Oct 16 18:31:07 2017 +0300

    Add INDIRECT to blacklist for threaded calculation
    
    Change-Id: I9a2066c396802551c3eda2c8db32b6d1a4171dfd

diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index a24f41949022..b7c73ef7ea3d 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -1339,6 +1339,7 @@ bool ScTokenArray::AddFormulaToken(
 void ScTokenArray::CheckToken( const FormulaToken& r )
 {
     static const std::set<OpCode> aThreadedCalcBlackList({
+        ocIndirect,
         ocMacro,
         ocTableOp
     });
commit b9485ab084e10970ae1da2435aac821444c1eb69
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Oct 16 18:28:24 2017 +0300

    Check whether ScTokenArray::CheckToken() has disabled threading of the group
    
    Otherwise the aThreadedCalcBlackList check in CheckToken() has no
    effect, we would still attempt the threaded code path.
    
    Change-Id: I08dc2dd174459615ab8a11dbb819e39fc5437d10

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 44e046c26bcf..36d4997ba157 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -4345,7 +4345,7 @@ bool ScFormulaCell::InterpretFormulaGroup()
         return false;
     }
 
-    if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
+    if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() && pCode->GetVectorState() != FormulaVectorDisabledNotInSubSet && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
     {
         // iterate over code in the formula ...
         // ensure all input is pre-calculated -
commit bd244800b92572f652edd490ac5f3c5ff429d702
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Oct 4 22:55:19 2017 +0300

    Avoid unused private field warning in the NDEBUG case
    
    Change-Id: I5e37b9a8325af35a15c01409f9eaa2f92459cc28

diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 73f450dc8163..a53258a2fb70 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -6747,6 +6747,7 @@ ScMutationGuard::ScMutationGuard(ScDocument* pDocument, ScMutationGuardFlags nFl
     mpDocument(pDocument),
     mnFlags(nFlags)
 {
+    (void) mpDocument;
     for (unsigned b = 0; b < static_cast<std::size_t>(ScMutationGuardFlags::N); b++)
     {
         if (static_cast<std::size_t>(mnFlags) & (1 << b))
commit 9284ffd0a3711d36ad334ec37b976f036a043839
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Oct 4 16:55:59 2017 +0300

    Move ScDocument::GetNonThreadedContext() inline
    
    Did not have any impact on performance, though.
    
    Change-Id: I7e769b4a74e0ff9e0aabfb7e291fc4b987441954

diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index f37e8e7d856e..8b138378dda3 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -560,7 +560,11 @@ public:
 
     SC_DLLPUBLIC void  InitDrawLayer( SfxObjectShell* pDocShell = nullptr );
 
-    SC_DLLPUBLIC ScInterpreterContext     GetNonThreadedContext() const;
+    ScInterpreterContext GetNonThreadedContext() const
+    {
+        // GetFormatTable() asserts that we are not in a threaded calculation
+        return ScInterpreterContext(*this, GetFormatTable());
+    }
 
     SC_DLLPUBLIC sfx2::LinkManager*       GetLinkManager();
     SC_DLLPUBLIC const sfx2::LinkManager* GetLinkManager() const;
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 0df71931c355..73f450dc8163 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -6769,13 +6769,6 @@ ScMutationGuard::~ScMutationGuard()
 #endif
 }
 
-ScInterpreterContext ScDocument::GetNonThreadedContext() const
-{
-    // GetFormatTable() asserts that we are not in a threaded calculation
-    ScInterpreterContext aResult(*this, GetFormatTable());
-    return aResult;
-}
-
 thread_local ScDocumentThreadSpecific ScDocument::maThreadSpecific;
 
 ScRecursionHelper& ScDocument::GetRecursionHelper()
commit 95a0fce7271e65f2d0d6af1cbd8ee96ccfb6e6cd
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Oct 4 13:11:10 2017 +0300

    Display the threaded calculation state in Help:About
    
    Change-Id: I299e555392bb4b09325ad2c92f843b1e12ee4d31

diff --git a/cui/source/dialogs/about.cxx b/cui/source/dialogs/about.cxx
index a7d219407249..c9ba680c8609 100644
--- a/cui/source/dialogs/about.cxx
+++ b/cui/source/dialogs/about.cxx
@@ -53,6 +53,7 @@
 #include <opencl/openclwrapper.hxx>
 #endif
 #include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Calc.hxx>
 
 using namespace ::com::sun::star::uno;
 using namespace ::com::sun::star::beans;
@@ -334,19 +335,31 @@ OUString AboutDialog::GetVersionString()
         sVersion += m_aLocaleStr.replaceAll("$LOCALE", aLocaleStr);
     }
 
-#if HAVE_FEATURE_OPENCL
     OUString aCalcMode = "Calc: "; // Calc calculation mode
+
+#if HAVE_FEATURE_OPENCL
     bool bSWInterp = officecfg::Office::Common::Misc::UseSwInterpreter::get();
     bool bOpenCL = openclwrapper::GPUEnv::isOpenCLEnabled();
     if (bOpenCL)
         aCalcMode += "CL";
     else if (bSWInterp)
         aCalcMode += "group";
-    else
-        aCalcMode += "single";
-    sVersion += "; " + aCalcMode;
+#else
+    const bool bOpenCL = false;
 #endif
 
+    static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
+    bool bThreadedCalc = officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get();
+
+    if (!bThreadingProhibited && !bOpenCL && bThreadedCalc)
+    {
+        if (!aCalcMode.endsWith(" "))
+            aCalcMode += " ";
+        aCalcMode += "threaded";
+    }
+
+    sVersion += "; " + aCalcMode;
+
     return sVersion;
 }
 
commit c5e976e2ad5dda092fb47fbf6e9ee823bc03782a
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Oct 4 12:40:20 2017 +0300

    Make threaded calculation the default (when OpenCL is not used)
    
    Introduce a configuration setting to turn it off. For now, can also be
    turned off with the environment variable SC_NO_THREADED_CALCULATION,
    but that is probably not something we want to keep or guarantee
    staility of. (LO looks at way too many environment variables already.)
    
    Change-Id: I469cde259eda72cc2d630814a25f707f1210b0ab

diff --git a/officecfg/registry/schema/org/openoffice/Office/Calc.xcs b/officecfg/registry/schema/org/openoffice/Office/Calc.xcs
index b1feca1825c6..72ac33b99413 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Calc.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Calc.xcs
@@ -1416,6 +1416,12 @@
         <info>
           <desc>Contains settings for how to calculate formulae.</desc>
         </info>
+	<prop oor:name="UseThreadedCalculationForFormulaGroups" oor:type="xs:boolean" oor:nillable="false">
+          <info>
+            <desc>Whether to use threaded calculation of forumula groups when applicable.</desc>
+          </info>
+          <value>true</value>
+	</prop>
 	<!-- Note: The default values below probably must correspond
 	to those assigned in setOpenCLConfigToDefault() in
 	sc/source/core/tool/calcconfig.cxx
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 2b87393c7d5a..44e046c26bcf 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -54,6 +54,7 @@
 #include <svl/intitem.hxx>
 #include <o3tl/make_unique.hxx>
 #include <rtl/strbuf.hxx>
+#include <officecfg/Office/Calc.hxx>
 #include <formulagroup.hxx>
 #include <listenercontext.hxx>
 #include <types.hxx>
@@ -4326,11 +4327,10 @@ bool ScFormulaCell::InterpretFormulaGroup()
         return false;
     }
 
-    static const bool bThreadingRequested = std::getenv("CPU_THREADED_CALCULATION");
+    static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
 
     // To temporarilu use threading for sc unit tests regardless of the size of the formula group,
-    // add the condition !std::getenv("LO_TESTNAME") below (with &&), and run with
-    // CPU_THREADED_CALCULATION=yes
+    // add the condition !std::getenv("LO_TESTNAME") below (with &&)
     if (GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize)
     {
         mxGroup->meCalcState = sc::GroupCalcDisabled;
@@ -4345,7 +4345,7 @@ bool ScFormulaCell::InterpretFormulaGroup()
         return false;
     }
 
-    if (!ScCalcConfig::isOpenCLEnabled() && bThreadingRequested)
+    if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
     {
         // iterate over code in the formula ...
         // ensure all input is pre-calculated -
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 0a586073c7c6..a24f41949022 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -25,6 +25,7 @@
 #include <tools/mempool.hxx>
 #include <osl/diagnose.h>
 #include <sfx2/docfile.hxx>
+#include <officecfg/Office/Calc.hxx>
 
 #include <token.hxx>
 #include <tokenarray.hxx>
@@ -1346,11 +1347,11 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
         // It's already disabled.  No more checking needed.
         return;
 
-    static const bool bThreadingRequested = std::getenv("CPU_THREADED_CALCULATION");
+    static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
 
     OpCode eOp = r.GetOpCode();
 
-    if (!ScCalcConfig::isOpenCLEnabled() && bThreadingRequested)
+    if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
     {
         if (aThreadedCalcBlackList.count(eOp))
         {
commit 26a9157a4b28e7cf80f2f16bc314b4b4b4a23d8c
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Oct 4 10:55:13 2017 +0300

    Need more ScInterpreterContexts
    
    Change-Id: I1dd679156661bb5cb025ca6cb46d19783524d5a4

diff --git a/sc/source/core/tool/interpr6.cxx b/sc/source/core/tool/interpr6.cxx
index 0f513c231327..00f399fab47c 100644
--- a/sc/source/core/tool/interpr6.cxx
+++ b/sc/source/core/tool/interpr6.cxx
@@ -846,7 +846,7 @@ void ScInterpreter::IterateParameters( ScIterFunc eFunc, bool bTextAsZero )
                         nFuncFmtIndex = aAction.getNumberFormat();
                     }
 
-                    nFuncFmtType = pDok->GetFormatTable()->GetType( nFuncFmtIndex );
+                    nFuncFmtType = mrContext.GetFormatTable()->GetType( nFuncFmtIndex );
                 }
                 else
                 {
commit 39ede542924672c97749147141c924a03dbfa25f
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Oct 4 08:48:05 2017 +0300

    -Werror,-Wunused-parameter
    
    Change-Id: If10c6a58f5b6f196f3644f6c592dd6d1dc0d860c

diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 62f4baa471c3..0df71931c355 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -6794,13 +6794,13 @@ ScRecursionHelper& ScDocument::GetRecursionHelper()
     }
 }
 
-void ScDocumentThreadSpecific::SetupFromNonThreadedData(const ScDocumentThreadSpecific& rNonThreadedData)
+void ScDocumentThreadSpecific::SetupFromNonThreadedData(const ScDocumentThreadSpecific& /*rNonThreadedData*/)
 {
     // What about the recursion helper?
     // Copy the lookup cache?
 }
 
-void ScDocumentThreadSpecific::MergeBackIntoNonThreadedData(ScDocumentThreadSpecific& rNonThreadedData)
+void ScDocumentThreadSpecific::MergeBackIntoNonThreadedData(ScDocumentThreadSpecific& /*rNonThreadedData*/)
 {
     // What about recursion helper and lookup cache?
 }
commit 034e06f3a8da0e766fc5e7bde88c9fe7b5a11937
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Oct 4 08:47:02 2017 +0300

    -Werror,-Wsign-compare
    
    Change-Id: Ide03e0ae1fe97e1a09a767908a981a1e803a3474

diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index f15107bc3fe7..62f4baa471c3 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -6747,7 +6747,7 @@ ScMutationGuard::ScMutationGuard(ScDocument* pDocument, ScMutationGuardFlags nFl
     mpDocument(pDocument),
     mnFlags(nFlags)
 {
-    for (auto b = 0; b < static_cast<std::size_t>(ScMutationGuardFlags::N); b++)
+    for (unsigned b = 0; b < static_cast<std::size_t>(ScMutationGuardFlags::N); b++)
     {
         if (static_cast<std::size_t>(mnFlags) & (1 << b))
         {
@@ -6759,7 +6759,7 @@ ScMutationGuard::ScMutationGuard(ScDocument* pDocument, ScMutationGuardFlags nFl
 ScMutationGuard::~ScMutationGuard()
 {
 #ifndef NDEBUG
-    for (auto b = 0; b < static_cast<std::size_t>(ScMutationGuardFlags::N); b++)
+    for (unsigned b = 0; b < static_cast<std::size_t>(ScMutationGuardFlags::N); b++)
     {
         if (static_cast<std::size_t>(mnFlags) & (1 << b))
         {
commit ada660b44bb44913d880cebc775016aa3ee75fd9
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Oct 4 00:12:31 2017 +0300

    Introduce ScInterpreterContext
    
    When calculating a formula group in multiple threads in parallel, we
    need to use separate SvNumberFormatters in the threads. Possibly later
    also other things that need to be thread-local can be handled through
    the ScInterpreterContext.
    
    Why handle some thread-local things through the
    ScDocument::maNonThreaded and ScDocument::maThreadSpecific mechanism,
    and others through this ScInterpreterContext? Good question.
    
    Share SvNumberFormatter across worker threads
    and use mutex to protect SvNumberFormatter::IsNumberFormat()
    
    Change-Id: I372e5fbd9a19785f55f0faf4a4bedc5fc1ef3e03

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 67bc6c348c5e..44bee716bf18 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -106,6 +106,7 @@ class ScDocumentImport;
 class ScHint;
 enum class ScMF;
 struct ScFilterEntries;
+struct ScInterpreterContext;
 
 struct ScNeededSizeOptions
 {
@@ -447,7 +448,7 @@ public:
     const ScPatternAttr*    GetPattern( SCROW nRow ) const;
     const ScPatternAttr*    GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow ) const;
 
-    sal_uInt32 GetNumberFormat( SCROW nRow ) const;
+    sal_uInt32 GetNumberFormat( const ScInterpreterContext& rContext, SCROW nRow ) const;
     sal_uInt32  GetNumberFormat( SCROW nStartRow, SCROW nEndRow ) const;
 
     void        MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, bool bDeep ) const;
@@ -582,7 +583,7 @@ public:
     void SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen );
     void SetFormulaResults( SCROW nRow, const formula::FormulaConstTokenRef* pResults, size_t nLen );
 
-    void CalculateInThread( SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
+    void CalculateInThread( const ScInterpreterContext& rContext, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal );
     void HandleStuffAfterParallelCalculation( SCROW nRow, size_t nLen );
 
     void SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat );
diff --git a/sc/inc/dociter.hxx b/sc/inc/dociter.hxx
index 544e64ebc9c1..904f471c3c4f 100644
--- a/sc/inc/dociter.hxx
+++ b/sc/inc/dociter.hxx
@@ -45,6 +45,7 @@ struct ScQueryParam;
 struct ScDBQueryParamInternal;
 struct ScDBQueryParamMatrix;
 class ScFormulaCell;
+struct ScInterpreterContext;
 
 class ScValueIterator            // walk through all values in an area
 {
@@ -84,7 +85,7 @@ public:
         ScDocument* pDocument, const ScRange& rRange, SubtotalFlags nSubTotalFlags = SubtotalFlags::NONE,
         bool bTextAsZero = false );
 
-    void GetCurNumFmtInfo( short& nType, sal_uLong& nIndex );
+    void GetCurNumFmtInfo( const ScInterpreterContext& rContext, short& nType, sal_uLong& nIndex );
 
     /// Does NOT reset rValue if no value found!
     bool GetFirst( double& rValue, FormulaError& rErr );
@@ -125,7 +126,7 @@ private:
     {
         typedef std::pair<sc::CellStoreType::const_iterator,size_t> PositionType;
     public:
-        DataAccessInternal(ScDBQueryParamInternal* pParam, ScDocument* pDoc);
+        DataAccessInternal(ScDBQueryParamInternal* pParam, ScDocument* pDoc, const ScInterpreterContext& rContext);
         virtual ~DataAccessInternal() override;
         virtual bool getCurrent(Value& rValue) override;
         virtual bool getFirst(Value& rValue) override;
@@ -139,6 +140,7 @@ private:
         PositionType maCurPos;
         ScDBQueryParamInternal* mpParam;
         ScDocument*         mpDoc;
+        const ScInterpreterContext& mrContext;
         const ScAttrArray*  pAttrArray;
         sal_uLong               nNumFormat;     // for CalcAsShown
         sal_uLong               nNumFmtIndex;
@@ -171,7 +173,7 @@ private:
     ::std::unique_ptr<DataAccess>         mpData;
 
 public:
-                    ScDBQueryDataIterator(ScDocument* pDocument, ScDBQueryParamBase* pParam);
+                    ScDBQueryDataIterator(ScDocument* pDocument, const ScInterpreterContext& rContext, ScDBQueryParamBase* pParam);
     /// Does NOT reset rValue if no value found!
     bool            GetFirst(Value& rValue);
     /// Does NOT reset rValue if no value found!
@@ -266,6 +268,7 @@ class ScQueryCellIterator           // walk through all non-empty cells in an ar
 
     std::unique_ptr<ScQueryParam> mpParam;
     ScDocument*     pDoc;
+    const ScInterpreterContext& mrContext;
     SCTAB           nTab;
     SCCOL           nCol;
     SCROW           nRow;
@@ -292,7 +295,7 @@ class ScQueryCellIterator           // walk through all non-empty cells in an ar
     bool BinarySearch();
 
 public:
-                    ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable,
+                    ScQueryCellIterator(ScDocument* pDocument, const ScInterpreterContext& rContext, SCTAB nTable,
                                         const ScQueryParam& aParam, bool bMod);
                                         // when !bMod, the QueryParam has to be filled
                                         // (bIsString)
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index eef7c4c8df07..f37e8e7d856e 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -26,6 +26,7 @@
 #include <com/sun/star/uno/Reference.hxx>
 #include <vcl/vclptr.hxx>
 #include "scdllapi.h"
+#include "interpretercontext.hxx"
 #include "rangelst.hxx"
 #include "rangenam.hxx"
 #include "tabopparams.hxx"
@@ -559,6 +560,8 @@ public:
 
     SC_DLLPUBLIC void  InitDrawLayer( SfxObjectShell* pDocShell = nullptr );
 
+    SC_DLLPUBLIC ScInterpreterContext     GetNonThreadedContext() const;
+
     SC_DLLPUBLIC sfx2::LinkManager*       GetLinkManager();
     SC_DLLPUBLIC const sfx2::LinkManager* GetLinkManager() const;
 
@@ -1111,10 +1114,10 @@ public:
     SC_DLLPUBLIC void                         GetNumberFormat( SCCOL nCol, SCROW nRow, SCTAB nTab,
                                                                sal_uInt32& rFormat ) const;
     sal_uInt32                                GetNumberFormat( const ScRange& rRange ) const;
-    SC_DLLPUBLIC sal_uInt32                   GetNumberFormat( const ScAddress& ) const;
+    SC_DLLPUBLIC sal_uInt32                   GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& ) const;
     void                                      SetNumberFormat( const ScAddress& rPos, sal_uInt32 nNumberFormat );
 
-    void                                      GetNumberFormatInfo( short& nType, sal_uLong& nIndex, const ScAddress& rPos ) const;
+    void                                      GetNumberFormatInfo( const ScInterpreterContext& rContext, short& nType, sal_uLong& nIndex, const ScAddress& rPos ) const;
     SC_DLLPUBLIC const ScFormulaCell*         GetFormulaCell( const ScAddress& rPos ) const;
     SC_DLLPUBLIC ScFormulaCell*               GetFormulaCell( const ScAddress& rPos );
     SC_DLLPUBLIC void                         GetFormula( SCCOL nCol, SCROW nRow, SCTAB nTab, OUString& rFormula ) const;
@@ -2049,7 +2052,7 @@ public:
     void SC_DLLPUBLIC SetFormulaResults( const ScAddress& rTopPos, const double* pResults, size_t nLen );
     void SC_DLLPUBLIC SetFormulaResults( const ScAddress& rTopPos, const formula::FormulaConstTokenRef* pResults, size_t nLen );
 
-    ScDocumentThreadSpecific CalculateInColumnInThread( const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
+    ScDocumentThreadSpecific CalculateInColumnInThread( const ScInterpreterContext& rContext, const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
     void HandleStuffAfterParallelCalculation( const ScAddress& rTopPos, size_t nLen );
 
     /**
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index 40fe21598e28..0185e2629010 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -30,7 +30,7 @@
 #include <svl/listener.hxx>
 
 #include "types.hxx"
-
+#include "interpretercontext.hxx"
 #include "formularesult.hxx"
 
 namespace sc {
@@ -150,7 +150,7 @@ public:
                         SCITP_FROM_ITERATION,
                         SCITP_CLOSE_ITERATION_CIRCLE
                     };
-    void            InterpretTail( ScInterpretTailParameter );
+                    void InterpretTail( const ScInterpreterContext&, ScInterpretTailParameter );
 
     void            HandleStuffAfterParallelCalculation();
 
diff --git a/sc/inc/interpretercontext.hxx b/sc/inc/interpretercontext.hxx
new file mode 100644
index 000000000000..cbf05349ca5f
--- /dev/null
+++ b/sc/inc/interpretercontext.hxx
@@ -0,0 +1,39 @@
+/* -*- 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_SC_INC_INTERPRETERCONTEXT_HXX
+#define INCLUDED_SC_INC_INTERPRETERCONTEXT_HXX
+
+class ScDocument;
+class SvNumberFormatter;
+
+struct ScInterpreterContext
+{
+    const ScDocument& mrDoc;
+    SvNumberFormatter* mpFormatter;
+
+    ScInterpreterContext(const ScDocument& rDoc, SvNumberFormatter* pFormatter) :
+        mrDoc(rDoc),
+        mpFormatter(pFormatter)
+    {
+    }
+
+    ~ScInterpreterContext()
+    {
+    }
+
+    SvNumberFormatter* GetFormatTable() const
+    {
+        return mpFormatter;
+    }
+};
+
+#endif // INCLUDED_SC_INC_INTERPRETERCONTEXT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 9b04a4d019a4..1f77ae4d2e39 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -116,6 +116,7 @@ class ScRangeName;
 class ScDBData;
 class ScDocumentImport;
 class ScHint;
+struct ScInterpreterContext;
 
 class ScColumnsRange final
 {
@@ -672,7 +673,7 @@ public:
     const ScPatternAttr*    GetPattern( SCCOL nCol, SCROW nRow ) const;
     const ScPatternAttr*    GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const;
 
-    sal_uInt32 GetNumberFormat( const ScAddress& rPos ) const;
+    sal_uInt32 GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const;
     sal_uInt32 GetNumberFormat( SCCOL nCol, SCROW nRow ) const;
     sal_uInt32 GetNumberFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const;
 
@@ -997,7 +998,7 @@ public:
     void SetFormulaResults( SCCOL nCol, SCROW nRow, const double* pResults, size_t nLen );
     void SetFormulaResults( SCCOL nCol, SCROW nRow, const formula::FormulaConstTokenRef* pResults, size_t nLen );
 
-    void CalculateInColumnInThread( SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
+    void CalculateInColumnInThread( const ScInterpreterContext& rContext, SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal);
     void HandleStuffAfterParallelCalculation( SCCOL nCol, SCROW nRow, size_t nLen);
 
     /**
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index ea93b02156f1..5d021d41aec4 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -418,9 +418,9 @@ sal_uInt32 ScColumn::GetNumberFormat( SCROW nStartRow, SCROW nEndRow ) const
     return nFormat;
 }
 
-sal_uInt32 ScColumn::GetNumberFormat( SCROW nRow ) const
+sal_uInt32 ScColumn::GetNumberFormat( const ScInterpreterContext& rContext, SCROW nRow ) const
 {
-    return pAttrArray->GetPattern( nRow )->GetNumberFormat( pDocument->GetFormatTable() );
+    return pAttrArray->GetPattern( nRow )->GetNumberFormat( rContext.GetFormatTable() );
 }
 
 SCROW ScColumn::ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark, ScEditDataArray* pDataArray, bool* const pIsChanged )
@@ -1151,7 +1151,7 @@ void ScColumn::CopyStaticToDocument(
     // Dont' forget to copy the number formats over.  Charts may reference them.
     for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
     {
-        sal_uInt32 nNumFmt = GetNumberFormat(nRow);
+        sal_uInt32 nNumFmt = GetNumberFormat(pDocument->GetNonThreadedContext(), nRow);
         SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt);
         if (itNum != rMap.end())
             nNumFmt = itNum->second;
@@ -2890,7 +2890,7 @@ public:
 
     void operator() (size_t nRow, ScFormulaCell* pCell)
     {
-        sal_uInt32 nFormat = mrCol.GetNumberFormat(nRow);
+        sal_uInt32 nFormat = mrCol.GetNumberFormat(mrCol.GetDoc().GetNonThreadedContext(), nRow);
         if( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
             // Non-default number format is set.
             pCell->SetNeedNumberFormat(false);
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index aaa897889603..daeda62bd881 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -2877,8 +2877,10 @@ void ScColumn::SetFormulaResults( SCROW nRow, const formula::FormulaConstTokenRe
     }
 }
 
-void ScColumn::CalculateInThread( SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
+void ScColumn::CalculateInThread( const ScInterpreterContext& rContext, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
 {
+    assert(pDocument->mbThreadedGroupCalcInProgress);
+
     sc::CellStoreType::position_type aPos = maCells.position(nRow);
     sc::CellStoreType::iterator it = aPos.first;
     if (it->type != sc::element_type_formula)
@@ -2901,8 +2903,7 @@ void ScColumn::CalculateInThread( SCROW nRow, size_t nLen, unsigned nThisThread,
         ScFormulaCell& rCell = **itCell;
         // Here we don't call IncInterpretLevel() and DecInterpretLevel() as this call site is
         // always in a threaded calculation.
-        assert(pDocument->mbThreadedGroupCalcInProgress);
-        rCell.InterpretTail(ScFormulaCell::SCITP_NORMAL);
+        rCell.InterpretTail(rContext, ScFormulaCell::SCITP_NORMAL);
     }
 }
 
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 646210a8e679..9a5a57205436 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1700,7 +1700,7 @@ bool ScColumn::ParseString(
     if (!aParam.mpNumFormatter)
         aParam.mpNumFormatter = pDocument->GetFormatTable();
 
-    nIndex = nOldIndex = GetNumberFormat( nRow );
+    nIndex = nOldIndex = GetNumberFormat( pDocument->GetNonThreadedContext(), nRow );
     if ( rString.getLength() > 1
             && aParam.mpNumFormatter->GetType(nIndex) != css::util::NumberFormat::TEXT )
         cFirstChar = rString[0];
@@ -1922,7 +1922,7 @@ void ScColumn::SetFormula( SCROW nRow, const ScTokenArray& rArray, formula::Form
 
     sc::CellStoreType::iterator it = GetPositionToInsert(nRow);
     ScFormulaCell* pCell = new ScFormulaCell(pDocument, aPos, rArray, eGram);
-    sal_uInt32 nCellFormat = GetNumberFormat(nRow);
+    sal_uInt32 nCellFormat = GetNumberFormat(pDocument->GetNonThreadedContext(), nRow);
     if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
         pCell->SetNeedNumberFormat(true);
     it = maCells.set(it, nRow, pCell);
@@ -1939,7 +1939,7 @@ void ScColumn::SetFormula( SCROW nRow, const OUString& rFormula, formula::Formul
 
     sc::CellStoreType::iterator it = GetPositionToInsert(nRow);
     ScFormulaCell* pCell = new ScFormulaCell(pDocument, aPos, rFormula, eGram);
-    sal_uInt32 nCellFormat = GetNumberFormat(nRow);
+    sal_uInt32 nCellFormat = GetNumberFormat(pDocument->GetNonThreadedContext(), nRow);
     if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
         pCell->SetNeedNumberFormat(true);
     it = maCells.set(it, nRow, pCell);
@@ -1954,7 +1954,7 @@ ScFormulaCell* ScColumn::SetFormulaCell(
     SCROW nRow, ScFormulaCell* pCell, sc::StartListeningType eListenType )
 {
     sc::CellStoreType::iterator it = GetPositionToInsert(nRow);
-    sal_uInt32 nCellFormat = GetNumberFormat(nRow);
+    sal_uInt32 nCellFormat = GetNumberFormat(pDocument->GetNonThreadedContext(), nRow);
     if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
         pCell->SetNeedNumberFormat(true);
     it = maCells.set(it, nRow, pCell);
@@ -1971,7 +1971,7 @@ void ScColumn::SetFormulaCell(
     sc::StartListeningType eListenType )
 {
     rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow);
-    sal_uInt32 nCellFormat = GetNumberFormat(nRow);
+    sal_uInt32 nCellFormat = GetNumberFormat(pDocument->GetNonThreadedContext(), nRow);
     if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
         pCell->SetNeedNumberFormat(true);
     rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, pCell);
@@ -2002,7 +2002,7 @@ bool ScColumn::SetFormulaCells( SCROW nRow, std::vector<ScFormulaCell*>& rCells
         for (size_t i = 0, n = rCells.size(); i < n; ++i)
         {
             SCROW nThisRow = nRow + i;
-            sal_uInt32 nFmt = GetNumberFormat(nThisRow);
+            sal_uInt32 nFmt = GetNumberFormat(pDocument->GetNonThreadedContext(), nThisRow);
             if ((nFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
                 rCells[i]->SetNeedNumberFormat(true);
         }
@@ -2055,7 +2055,7 @@ class FilterEntriesHandler
     {
         SvNumberFormatter* pFormatter = mrColumn.GetDoc().GetFormatTable();
         OUString aStr;
-        sal_uLong nFormat = mrColumn.GetNumberFormat(nRow);
+        sal_uLong nFormat = mrColumn.GetNumberFormat(mrColumn.GetDoc().GetNonThreadedContext(), nRow);
         ScCellFormat::GetInputString(rCell, nFormat, aStr, *pFormatter, &mrColumn.GetDoc());
 
         if (rCell.hasString())
@@ -2543,7 +2543,7 @@ void ScColumn::GetString( SCROW nRow, OUString& rString ) const
     if (aCell.meType == CELLTYPE_FORMULA)
         aCell.mpFormula->MaybeInterpret();
 
-    sal_uLong nFormat = GetNumberFormat(nRow);
+    sal_uLong nFormat = GetNumberFormat(pDocument->GetNonThreadedContext(), nRow);
     Color* pColor = nullptr;
     ScCellFormat::GetString(aCell, nFormat, rString, &pColor, *(pDocument->GetFormatTable()), pDocument);
 }
@@ -2564,7 +2564,7 @@ double* ScColumn::GetValueCell( SCROW nRow )
 void ScColumn::GetInputString( SCROW nRow, OUString& rString ) const
 {
     ScRefCellValue aCell = GetCellValue(nRow);
-    sal_uLong nFormat = GetNumberFormat(nRow);
+    sal_uLong nFormat = GetNumberFormat(pDocument->GetNonThreadedContext(), nRow);
     ScCellFormat::GetInputString(aCell, nFormat, rString, *(pDocument->GetFormatTable()), pDocument);
 }
 
diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx
index 358774153027..fa7f9dfb8c05 100644
--- a/sc/source/core/data/dociter.cxx
+++ b/sc/source/core/data/dociter.cxx
@@ -262,14 +262,14 @@ bool ScValueIterator::GetThis(double& rValue, FormulaError& rErr)
     }
 }
 
-void ScValueIterator::GetCurNumFmtInfo( short& nType, sal_uLong& nIndex )
+void ScValueIterator::GetCurNumFmtInfo( const ScInterpreterContext& rContext, short& nType, sal_uLong& nIndex )
 {
     if (!bNumValid && mnTab < pDoc->GetTableCount())
     {
         SCROW nCurRow = GetRow();
         const ScColumn* pCol = &(pDoc->maTabs[mnTab])->aCol[mnCol];
-        nNumFmtIndex = pCol->GetNumberFormat(nCurRow);
-        nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex );
+        nNumFmtIndex = pCol->GetNumberFormat(rContext, nCurRow);
+        nNumFmtType = rContext.GetFormatTable()->GetType( nNumFmtIndex );
         bNumValid = true;
     }
 
@@ -334,11 +334,12 @@ bool ScDBQueryDataIterator::IsQueryValid(
     return rDoc.maTabs[nTab]->ValidQuery(nRow, rParam, pCell);
 }
 
-ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(ScDBQueryParamInternal* pParam, ScDocument* pDoc)
+ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(ScDBQueryParamInternal* pParam, ScDocument* pDoc, const ScInterpreterContext& rContext)
     : DataAccess()
     , mpCells(nullptr)
     , mpParam(pParam)
     , mpDoc(pDoc)
+    , mrContext(rContext)
     , pAttrArray(nullptr)
     , nNumFormat(0) // Initialized in GetNumberFormat
     , nNumFmtIndex(0)
@@ -432,7 +433,7 @@ bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value& rValue)
                         rValue.mfValue = aCell.mpFormula->GetValue();
                         rValue.mbIsNumber = true;
                         mpDoc->GetNumberFormatInfo(
-                            nNumFmtType, nNumFmtIndex, ScAddress(nCol, nRow, nTab));
+                            mrContext, nNumFmtType, nNumFmtIndex, ScAddress(nCol, nRow, nTab));
                         rValue.mnError = aCell.mpFormula->GetErrCode();
                         return true; // Found it!
                     }
@@ -744,7 +745,7 @@ ScDBQueryDataIterator::Value::Value() :
     ::rtl::math::setNan(&mfValue);
 }
 
-ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument* pDocument, ScDBQueryParamBase* pParam) :
+ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument* pDocument, const ScInterpreterContext& rContext, ScDBQueryParamBase* pParam) :
     mpParam (pParam)
 {
     switch (mpParam->GetType())
@@ -752,7 +753,7 @@ ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument* pDocument, ScDBQueryPar
         case ScDBQueryParamBase::INTERNAL:
         {
             ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pParam);
-            mpData.reset(new DataAccessInternal(p, pDocument));
+            mpData.reset(new DataAccessInternal(p, pDocument, rContext));
         }
         break;
         case ScDBQueryParamBase::MATRIX:
@@ -1046,10 +1047,11 @@ bool ScCellIterator::next()
     return getCurrent();
 }
 
-ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable,
+ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, const ScInterpreterContext& rContext, SCTAB nTable,
              const ScQueryParam& rParam, bool bMod ) :
     mpParam(new ScQueryParam(rParam)),
     pDoc( pDocument ),
+    mrContext( rContext ),
     nTab( nTable),
     nStopOnMismatch( nStopOnMismatchDisabled ),
     nTestEqualCondition( nTestEqualConditionDisabled ),
@@ -1681,7 +1683,7 @@ bool ScQueryCellIterator::BinarySearch()
         if (aPos.first->type == sc::element_type_string || aPos.first->type == sc::element_type_edittext)
         {
             aCell = sc::toRefCell(aPos.first, aPos.second);
-            sal_uLong nFormat = pCol->GetNumberFormat(nRow);
+            sal_uLong nFormat = pCol->GetNumberFormat(mrContext, nRow);
             OUString aCellStr;
             ScCellFormat::GetInputString(aCell, nFormat, aCellStr, rFormatter, pDoc);
             sal_Int32 nTmp = pCollator->compareString(aCellStr, rEntry.GetQueryItem().maString.getString());
@@ -1715,7 +1717,7 @@ bool ScQueryCellIterator::BinarySearch()
     aCell = aCellData.first;
     if (aCell.hasString())
     {
-        sal_uLong nFormat = pCol->GetNumberFormat(aCellData.second);
+        sal_uLong nFormat = pCol->GetNumberFormat(mrContext, aCellData.second);
         OUString aStr;
         ScCellFormat::GetInputString(aCell, nFormat, aStr, rFormatter, pDoc);
         aLastInRangeString = aStr;
@@ -1814,7 +1816,7 @@ bool ScQueryCellIterator::BinarySearch()
         else if (bStr && bByString)
         {
             OUString aCellStr;
-            sal_uLong nFormat = pCol->GetNumberFormat(aCellData.second);
+            sal_uLong nFormat = pCol->GetNumberFormat(mrContext, aCellData.second);
             ScCellFormat::GetInputString(aCell, nFormat, aCellStr, rFormatter, pDoc);
 
             nRes = pCollator->compareString(aCellStr, rEntry.GetQueryItem().maString.getString());
diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx
index 283c082199b2..b15ee591a7e2 100644
--- a/sc/source/core/data/documen2.cxx
+++ b/sc/source/core/data/documen2.cxx
@@ -502,6 +502,7 @@ void ScDocument::InitClipPtrs( ScDocument* pSourceDoc )
 
 SvNumberFormatter* ScDocument::GetFormatTable() const
 {
+    assert(!mbThreadedGroupCalcInProgress);
     return mxPoolHelper->GetFormTable();
 }
 
diff --git a/sc/source/core/data/documen8.cxx b/sc/source/core/data/documen8.cxx
index b29de4004c90..8bf1de3fb191 100644
--- a/sc/source/core/data/documen8.cxx
+++ b/sc/source/core/data/documen8.cxx
@@ -427,7 +427,7 @@ void ScDocument::SetFormulaResults(
     pTab->SetFormulaResults(rTopPos.Col(), rTopPos.Row(), pResults, nLen);
 }
 
-ScDocumentThreadSpecific ScDocument::CalculateInColumnInThread( const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
+ScDocumentThreadSpecific ScDocument::CalculateInColumnInThread( const ScInterpreterContext& rContext, const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal)
 {
     ScTable* pTab = FetchTable(rTopPos.Tab());
     if (!pTab)
@@ -436,7 +436,7 @@ ScDocumentThreadSpecific ScDocument::CalculateInColumnInThread( const ScAddress&
     assert(mbThreadedGroupCalcInProgress);
 
     maThreadSpecific.SetupFromNonThreadedData(maNonThreaded);
-    pTab->CalculateInColumnInThread(rTopPos.Col(), rTopPos.Row(), nLen, nThisThread, nThreadsTotal);
+    pTab->CalculateInColumnInThread(rContext, rTopPos.Col(), rTopPos.Row(), nLen, nThisThread, nThreadsTotal);
 
     assert(mbThreadedGroupCalcInProgress);
 
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 185115ac8ad7..f15107bc3fe7 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -3702,13 +3702,13 @@ sal_uInt32 ScDocument::GetNumberFormat( const ScRange& rRange ) const
     return nFormat;
 }
 
-sal_uInt32 ScDocument::GetNumberFormat( const ScAddress& rPos ) const
+sal_uInt32 ScDocument::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const
 {
     SCTAB nTab = rPos.Tab();
     if (!TableExists(nTab))
         return 0;
 
-    return maTabs[nTab]->GetNumberFormat( rPos );
+    return maTabs[nTab]->GetNumberFormat( rContext, rPos );
 }
 
 void ScDocument::SetNumberFormat( const ScAddress& rPos, sal_uInt32 nNumberFormat )
@@ -3720,14 +3720,14 @@ void ScDocument::SetNumberFormat( const ScAddress& rPos, sal_uInt32 nNumberForma
     maTabs[nTab]->SetNumberFormat(rPos.Col(), rPos.Row(), nNumberFormat);
 }
 
-void ScDocument::GetNumberFormatInfo( short& nType, sal_uLong& nIndex,
+void ScDocument::GetNumberFormatInfo( const ScInterpreterContext& rContext, short& nType, sal_uLong& nIndex,
             const ScAddress& rPos ) const
 {
     SCTAB nTab = rPos.Tab();
     if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
     {
-        nIndex = maTabs[nTab]->GetNumberFormat( rPos );
-        nType = GetFormatTable()->GetType( nIndex );
+        nIndex = maTabs[nTab]->GetNumberFormat( rContext, rPos );
+        nType = rContext.GetFormatTable()->GetType( nIndex );
     }
     else
     {
@@ -6769,6 +6769,13 @@ ScMutationGuard::~ScMutationGuard()
 #endif
 }
 
+ScInterpreterContext ScDocument::GetNonThreadedContext() const
+{
+    // GetFormatTable() asserts that we are not in a threaded calculation
+    ScInterpreterContext aResult(*this, GetFormatTable());
+    return aResult;
+}
+
 thread_local ScDocumentThreadSpecific ScDocument::maThreadSpecific;
 
 ScRecursionHelper& ScDocument::GetRecursionHelper()
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index bc6fd157e34b..2b87393c7d5a 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -1528,10 +1528,10 @@ void ScFormulaCell::Interpret()
         bool bGroupInterpreted = InterpretFormulaGroup();
         aDC.leaveGroup();
         if (!bGroupInterpreted)
-            InterpretTail( SCITP_NORMAL);
+            InterpretTail( pDocument->GetNonThreadedContext(), SCITP_NORMAL);
 #else
         if (!InterpretFormulaGroup())
-            InterpretTail( SCITP_NORMAL);
+            InterpretTail( pDocument->GetNonThreadedContext(), SCITP_NORMAL);
 #endif
         pDocument->DecInterpretLevel();
     }
@@ -1595,8 +1595,8 @@ void ScFormulaCell::Interpret()
                     bResumeIteration = false;
                     // Close circle once.

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list