include/svl/poolitem.hxx     |    2 
 svl/source/items/itemset.cxx |  151 ++++++++++++++++++++++++++++++++++++-------
 2 files changed, 129 insertions(+), 24 deletions(-)

New commits:
commit 64503f9187252c7ebf85832cca65c0642013bc38
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Tue Jan 23 20:56:17 2024 +0100
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Wed Jan 24 14:27:56 2024 +0100

    ITEM: Add some default global sharing
    
    As addition to tdf#158605 I have added a
    global default mechanism to support global
    sharing of Item instances. It's automated
    and complements the existing one, see one
    of the last commits. All in all there are
    now the following possibilities to support
    this for individual Item derivations:
    
    (1) Do nothing: In that case, if the Item
    is shareable, the new mechanism will kick
    in: It will start sharing the Item globally,
    but not immediately: After a defined amount
    of allowed non-shared ocurrences (look for
    NUMBER_OF_UNSHARED_INSTANCES) an instance
    of the default ItemInstanceManager, a
    DefaultItemInstanceManager, will be incarnated
    and used. NOTE: Mixing shared/unshared
    instances is not a problem (we might even
    implement a kind of 're-hash' when this
    kicks in, but is not really needded).
    
    (2) Overload getItemInstanceManager for
    SfxPoolItem in a class derived from
    SfxPoolItem and...
    
    (2a) Return a static incarnation of
    DefaultItemInstanceManager to immediately
    start global sharing of that Item derivation.
    
    (2b) Implement and return your own
    implementation and static incarnation of
    ItemInstanceManager to do something better/
    faster that the default implementation can#
    do. Example: SvxFontItem, uses hashing.
    
    The NUMBER_OF_UNSHARED_INSTANCES is currently
    at (50) which gives a decent relationship
    bewteen no global sharing (speed) and memory
    needs. Not sharing is faster (that access to
    'find' an equal item is spared which might be
    costly), sharing again needs less memory.
    
    There are two supported ENVVARs to use:
    
    (a) SVL_DISABLE_ITEM_INSTANCE_MANAGER:
    This disables the mechanism of global Item
    sharing completely. This can be used to test/
    check speed/memory needs compared with using
    it, but also may come in handy to check if
    evtl. errors/resgrressions have to do with
    it.
    
    (b) SVL_SHARE_ITEMS_GLOBALLY_INSTANTLY:
    This internally forces the
    NUMBER_OF_UNSHARED_INSTANCES to be ignored
    and start sharing ALL Item derivations
    instantly.
    
    Change-Id: I40d9c5f228f0bcbf239f2ce0a02d423054240570
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162478
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/include/svl/poolitem.hxx b/include/svl/poolitem.hxx
index a3af1762d679..43d8c101dd05 100644
--- a/include/svl/poolitem.hxx
+++ b/include/svl/poolitem.hxx
@@ -118,6 +118,7 @@ class SVL_DLLPUBLIC SfxPoolItem
     friend class SfxItemPool;
     friend class SfxItemDisruptor_Impl;
     friend class SfxItemSet;
+    friend class InstanceManagerHelper;
 
     // allow ItemSetTooling to access
     friend SfxPoolItem const* implCreateItemEntry(SfxItemPool&, SfxPoolItem 
const*, bool);
@@ -316,6 +317,7 @@ class SVL_DLLPUBLIC DefaultItemInstanceManager : public 
ItemInstanceManager
     std::unordered_set<const SfxPoolItem*>  maRegistered;
 
 public:
+    virtual ~DefaultItemInstanceManager() = default;
     virtual const SfxPoolItem* find(const SfxPoolItem&) const override;
     virtual void add(const SfxPoolItem&) override;
     virtual void remove(const SfxPoolItem&) override;
diff --git a/svl/source/items/itemset.cxx b/svl/source/items/itemset.cxx
index df46011dc2d9..907daa23180f 100644
--- a/svl/source/items/itemset.cxx
+++ b/svl/source/items/itemset.cxx
@@ -34,10 +34,11 @@
 #include <svl/setitem.hxx>
 #include <svl/whiter.hxx>
 #include <svl/voiditem.hxx>
-
 #include <items_helper.hxx>
 
 static bool 
g_bDisableItemInstanceManager(getenv("SVL_DISABLE_ITEM_INSTANCE_MANAGER"));
+static bool g_bShareImmediately(getenv("SVL_SHARE_ITEMS_GLOBALLY_INSTANTLY"));
+#define NUMBER_OF_UNSHARED_INSTANCES  (50)
 
 #ifdef DBG_UTIL
 static size_t nAllocatedSfxItemSetCount(0);
@@ -323,6 +324,116 @@ SfxItemSet::SfxItemSet(SfxItemPool& pool, 
WhichRangesContainer wids)
     assert(svl::detail::validRanges2(m_pWhichRanges));
 }
 
+class InstanceManagerHelper
+{
+    typedef std::unordered_map<std::size_t, std::pair<sal_uInt16, 
DefaultItemInstanceManager*>> managerTypeMap;
+    managerTypeMap  maManagerPerType;
+
+public:
+    InstanceManagerHelper() {}
+    ~InstanceManagerHelper()
+    {
+        for (auto& rCandidate : maManagerPerType)
+            if (nullptr != rCandidate.second.second)
+                delete rCandidate.second.second;
+    }
+
+    ItemInstanceManager* getOrCreateItemInstanceManager(const SfxPoolItem& 
rItem)
+    {
+        // deactivated?
+        if (g_bDisableItemInstanceManager)
+            return nullptr;
+
+        // Item cannot be shared?
+        if (!rItem.isShareable())
+            return nullptr;
+
+        // prefer getting an ItemInstanceManager directly from
+        // the Item: These are the extra implemented (and thus
+        // hopefully fast) incarnations
+        ItemInstanceManager* pManager(rItem.getItemInstanceManager());
+        if (nullptr != pManager)
+            return pManager;
+
+        // get hash and check memory for existing entry
+        const std::size_t aHash(typeid(rItem).hash_code());
+        managerTypeMap::iterator aHit(maManagerPerType.find(aHash));
+
+        // no instance yet, create one to start usage-counting
+        if (aHit == maManagerPerType.end())
+        {
+            if (g_bShareImmediately)
+            {
+                // create and immediately start sharing
+                DefaultItemInstanceManager* pNew(new 
DefaultItemInstanceManager);
+                maManagerPerType.insert({aHash, std::make_pair(0, pNew)});
+                return pNew;
+            }
+
+            // start countown from NUMBER_OF_UNSHARED_INSTANCES until zero is 
reached
+            maManagerPerType.insert({aHash, 
std::make_pair(NUMBER_OF_UNSHARED_INSTANCES, nullptr)});
+            return nullptr;
+        }
+
+        // if there is already a ItemInstanceManager incarnated, return it
+        if (nullptr != aHit->second.second)
+            return aHit->second.second;
+
+        if (aHit->second.first > 0)
+        {
+            // still not the neeed number of hits, countdown & return nullptr
+            aHit->second.first--;
+            return nullptr;
+        }
+
+        // here there is not yet a ItemInstanceManager incarnated. Do
+        // so and return it
+        assert(nullptr == aHit->second.second);
+        DefaultItemInstanceManager* pNew(new DefaultItemInstanceManager);
+        aHit->second.second = pNew;
+
+        return pNew;
+    }
+
+    ItemInstanceManager* getExistingItemInstanceManager(const SfxPoolItem& 
rItem)
+    {
+        // deactivated?
+        if (g_bDisableItemInstanceManager)
+            return nullptr;
+
+        // Item cannot be shared?
+        if (!rItem.isShareable())
+            return nullptr;
+
+        // prefer getting an ItemInstanceManager directly from
+        // the Item: These are the extra implemented (and thus
+        // hopefully fast) incarnations
+        ItemInstanceManager* pManager(rItem.getItemInstanceManager());
+        if (nullptr != pManager)
+            return pManager;
+
+        // get hash and check memory for existing entry
+        const std::size_t aHash(typeid(rItem).hash_code());
+        managerTypeMap::iterator aHit(maManagerPerType.find(aHash));
+
+        if (aHit == maManagerPerType.end())
+            // no instance yet, return nullptr
+            return nullptr;
+
+        // if there is already a ItemInstanceManager incarnated, return it
+        if (nullptr != aHit->second.second)
+            return aHit->second.second;
+
+        // count-up neeed number of hits again if item is released
+        if (aHit->second.first < NUMBER_OF_UNSHARED_INSTANCES)
+            aHit->second.first++;
+
+        return nullptr;
+    }
+};
+
+static InstanceManagerHelper aInstanceManagerHelper;
+
 SfxPoolItem const* implCreateItemEntry(SfxItemPool& rPool, SfxPoolItem const* 
pSource, bool bPassingOwnership)
 {
     if (nullptr == pSource)
@@ -450,18 +561,15 @@ SfxPoolItem const* implCreateItemEntry(SfxItemPool& 
rPool, SfxPoolItem const* pS
         return pSource;
     }
 
-    // check if we can globally share the Item using the
-    // ItemInstanceManager (only for shareable Items)
-    while (!g_bDisableItemInstanceManager && pSource->isShareable())
-    {
-        ItemInstanceManager* pManager(pSource->getItemInstanceManager());
-        if (nullptr == pManager)
-            // not supported by this Item, done
-            break;
+    // try to get an ItemInstanceManager for global Item instance sharing
+    ItemInstanceManager* 
pManager(aInstanceManagerHelper.getOrCreateItemInstanceManager(*pSource));
 
+    // check if we can globally share the Item using an ItemInstanceManager
+    while (nullptr != pManager)
+    {
         const SfxPoolItem* pAlternative(pManager->find(*pSource));
         if(nullptr == pAlternative)
-            // none found, done
+            // no already globally shared one found, done
             break;
 
         // need to delete evtl. handed over ownership change Item
@@ -501,12 +609,8 @@ SfxPoolItem const* implCreateItemEntry(SfxItemPool& rPool, 
SfxPoolItem const* pS
 
     // check if we should register this Item for the global
     // ItemInstanceManager mechanism (only for shareable Items)
-    if (!g_bDisableItemInstanceManager && pSource->isShareable())
-    {
-        ItemInstanceManager* pManager(pSource->getItemInstanceManager());
-        if (nullptr != pManager)
-            pManager->add(*pSource);
-    }
+    if (nullptr != pManager)
+        pManager->add(*pSource);
 
     return pSource;
 }
@@ -546,14 +650,13 @@ void implCleanupItemEntry(SfxPoolItem const* pSource)
         // default items (static and dynamic) are owned by the pool, do not 
delete
         return;
 
-    // check if we should remove this Item from the global
-    // ItemInstanceManager mechanism (only for shareable Items)
-    if (!g_bDisableItemInstanceManager && pSource->isShareable())
-    {
-        ItemInstanceManager* pManager(pSource->getItemInstanceManager());
-        if (nullptr != pManager)
-            pManager->remove(*pSource);
-    }
+    // try to get an ItemInstanceManager for global Item instance sharing
+    ItemInstanceManager* 
pManager(aInstanceManagerHelper.getExistingItemInstanceManager(*pSource));
+
+    // check if we should/can remove this Item from the global
+    // ItemInstanceManager mechanism
+    if (nullptr != pManager)
+        pManager->remove(*pSource);
 
     // decrease RefCnt before deleting (destructor asserts for it and that's
     // good to find other errors)

Reply via email to