formula/source/core/api/token.cxx   |    4 +--
 include/formula/token.hxx           |   41 ++++++++++++++++++++++++++++++++----
 sc/source/core/data/formulacell.cxx |   14 ++++++++++++
 sc/source/core/inc/interpre.hxx     |    3 ++
 sc/source/core/tool/interpr4.cxx    |    9 ++++++-
 5 files changed, 63 insertions(+), 8 deletions(-)

New commits:
commit 82d3b20a9404c076ea6b1aa5cb1901c006569ae0
Author:     Caolán McNamara <caolan.mcnam...@collabora.com>
AuthorDate: Sun Mar 24 21:31:38 2024 +0000
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Mon Apr 15 16:15:27 2024 +0200

    Related: tdf#160056 elide ref counting for initial tokens
    
    The initial tokens provided to interpreting will be restored
    to their original ref count when interpreting is complete. So we
    could elide expensive ref counting for them for the duration
    of the the threaded group calculation.
    
    Possibly "temp" tokens that are created just during interpreting could
    additionally use thread-unsafe ref counts presuming they only appear in
    a thread specific context.
    
    Change-Id: I1f5b0198e83027781be15812680079f28b6a4e27
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165259
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    (cherry picked from commit 791a2489eecc3e59d7a31a4bb9e513a576db08ee)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165943
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/formula/source/core/api/token.cxx 
b/formula/source/core/api/token.cxx
index 74a212937bb0..b384f091db67 100644
--- a/formula/source/core/api/token.cxx
+++ b/formula/source/core/api/token.cxx
@@ -55,12 +55,12 @@ static bool lcl_IsReference( OpCode eOp, StackVar eType )
 // --- class FormulaToken 
--------------------------------------------------------
 
 FormulaToken::FormulaToken( StackVar eTypeP, OpCode e ) :
-    eOp(e), eType( eTypeP ), mnRefCnt(0)
+    eOp(e), eType( eTypeP ), eRefCntPolicy(RefCntPolicy::ThreadSafe), 
mnRefCnt(0)
 {
 }
 
 FormulaToken::FormulaToken( const FormulaToken& r ) :
-    eOp(r.eOp), eType( r.eType ), mnRefCnt(0)
+    eOp(r.eOp), eType( r.eType ), eRefCntPolicy(RefCntPolicy::ThreadSafe), 
mnRefCnt(0)
 {
 }
 
diff --git a/include/formula/token.hxx b/include/formula/token.hxx
index f534b2c5c801..43f2574ff95b 100644
--- a/include/formula/token.hxx
+++ b/include/formula/token.hxx
@@ -120,10 +120,18 @@ inline std::string StackVarEnumToString(StackVar const e)
     return os.str();
 }
 
+enum class RefCntPolicy : sal_uInt8
+{
+    ThreadSafe, // refcounting via thread-safe oslInterlockedCount
+    UnsafeRef,  // refcounting done with no locking/guarding against 
concurrent access
+    None        // no ref counting done
+};
+
 class FORMULA_DLLPUBLIC FormulaToken
 {
     OpCode                      eOp;
-    const StackVar              eType;          // type of data
+    const StackVar              eType;           // type of data
+    RefCntPolicy                eRefCntPolicy;   // style of reference counting
     mutable oslInterlockedCount mnRefCnt;        // reference count
 
     FormulaToken&            operator=( const FormulaToken& ) = delete;
@@ -145,15 +153,40 @@ public:
 
     void IncRef() const
     {
-        osl_atomic_increment(&mnRefCnt);
+        switch (eRefCntPolicy)
+        {
+            case RefCntPolicy::ThreadSafe:
+            default:
+                osl_atomic_increment(&mnRefCnt);
+                break;
+            case RefCntPolicy::UnsafeRef:
+                ++mnRefCnt;
+                break;
+            case RefCntPolicy::None:
+                break;
+        }
     }
 
     void DecRef() const
     {
-        if (!osl_atomic_decrement(&mnRefCnt))
-            const_cast<FormulaToken*>(this)->Delete();
+        switch (eRefCntPolicy)
+        {
+            case RefCntPolicy::ThreadSafe:
+            default:
+                if (!osl_atomic_decrement(&mnRefCnt))
+                    const_cast<FormulaToken*>(this)->Delete();
+                break;
+            case RefCntPolicy::UnsafeRef:
+                if (!--mnRefCnt)
+                    const_cast<FormulaToken*>(this)->Delete();
+                break;
+            case RefCntPolicy::None:
+                break;
+        }
     }
 
+    void SetRefCntPolicy(RefCntPolicy ePolicy) { eRefCntPolicy = ePolicy; }
+
     oslInterlockedCount GetRef() const       { return mnRefCnt; }
     OpCode              GetOpCode() const    { return eOp; }
 
diff --git a/sc/source/core/data/formulacell.cxx 
b/sc/source/core/data/formulacell.cxx
index 2f0cf6c6bec5..74f88d1c008b 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -5011,6 +5011,13 @@ bool 
ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope
 
             ScMutationDisable aGuard(rDocument, ScMutationGuardFlags::CORE);
 
+            // Here we turn off ref-counting for the contents of pCode on the 
basis
+            // that pCode is not modified by interpreting and when 
interpreting is
+            // complete all token refcounts will be back to their initial ref 
count
+            FormulaToken** pArray = pCode->GetArray();
+            for (sal_uInt16 i = 0, n = pCode->GetLen(); i < n; ++i)
+                pArray[i]->SetRefCntPolicy(RefCntPolicy::None);
+
             // Start nThreadCount new threads
             std::shared_ptr<comphelper::ThreadTaskTag> aTag = 
comphelper::ThreadPool::createThreadTaskTag();
             ScThreadedInterpreterContextGetterGuard 
aContextGetterGuard(nThreadCount, rDocument, pNonThreadedFormatter);
@@ -5032,6 +5039,13 @@ bool 
ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope
             // if they don't get joined from elsewhere before (via 
ThreadPool::waitUntilDone).
             rThreadPool.waitUntilDone(aTag, false);
 
+            // Drop any caches that reference Tokens before restoring ref 
counting policy
+            for (int i = 0; i < nThreadCount; ++i)
+                aInterpreters[i]->DropTokenCaches();
+
+            for (sal_uInt16 i = 0, n = pCode->GetLen(); i < n; ++i)
+                pArray[i]->SetRefCntPolicy(RefCntPolicy::ThreadSafe);
+
             rDocument.SetThreadedGroupCalcInProgress(false);
 
             for (int i = 0; i < nThreadCount; ++i)
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
index e0d7508fd581..6a74c4974112 100644
--- a/sc/source/core/inc/interpre.hxx
+++ b/sc/source/core/inc/interpre.hxx
@@ -1092,6 +1092,9 @@ public:
     // Resets the interpreter object, allowing reuse of interpreter object for 
each cell
     // in the group.
     void Init( ScFormulaCell* pCell, const ScAddress& rPos, ScTokenArray& 
rTokArray );
+    // Used only for threaded formula-groups.
+    // Drops any caches that contain Tokens
+    void DropTokenCaches();
 
     formula::StackVar Interpret();
 
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index c5a4ccfa9074..8764a7674a85 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -3888,9 +3888,8 @@ void ScInterpreter::Init( ScFormulaCell* pCell, const 
ScAddress& rPos, ScTokenAr
     aCode.ReInit(rTokArray);
     aPos = rPos;
     pArr = &rTokArray;
-    xResult = nullptr;
     pJumpMatrix = nullptr;
-    maTokenMatrixMap.clear();
+    DropTokenCaches();
     pMyFormulaCell = pCell;
     pCur = nullptr;
     nGlobalError = FormulaError::NONE;
@@ -3907,6 +3906,12 @@ void ScInterpreter::Init( ScFormulaCell* pCell, const 
ScAddress& rPos, ScTokenAr
     cPar = 0;
 }
 
+void ScInterpreter::DropTokenCaches()
+{
+    xResult = nullptr;
+    maTokenMatrixMap.clear();
+}
+
 ScCalcConfig& ScInterpreter::GetOrCreateGlobalConfig()
 {
     if (!mpGlobalConfig)

Reply via email to