From ec035930754cd19244486963581ed4f04459d4c8 Mon Sep 17 00:00:00 2001
From: Philippe Canal <pcanal@fnal.gov>
Date: Thu, 11 Feb 2021 16:22:20 -0600
Subject: [PATCH] TBufferMergerFile: Call FlushBasket in unlocked thread.

Add new enum for TObject::Write: kOnlyPrepStep  = BIT(3)         ///< Just prepare the objects do not write them (i.e. call TTree::FlushBaskets)
---
 core/base/inc/TObject.h         | 11 ++++++++++-
 core/base/src/TObject.cxx       |  3 +++
 io/io/src/TBufferMergerFile.cxx |  4 ++++
 io/io/src/TDirectoryFile.cxx    |  3 ++-
 tree/tree/src/TTree.cxx         |  2 ++
 5 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/core/base/inc/TObject.h b/core/base/inc/TObject.h
index 58c09609f69..f3646aa4f4c 100644
--- a/core/base/inc/TObject.h
+++ b/core/base/inc/TObject.h
@@ -86,7 +86,16 @@ public:
    enum {
       kSingleKey     = BIT(0),        ///< write collection with single key
       kOverwrite     = BIT(1),        ///< overwrite existing object with same name
-      kWriteDelete   = BIT(2)         ///< write object, then delete previous key with same name
+      kWriteDelete   = BIT(2),        ///< write object, then delete previous key with same name
+
+      ///< Used to request that the class specific implementation of `TObject::Write` 
+      ///< just prepare the objects to be ready to be written but do not actually write
+      ///< them into the TBuffer. This is just for example by TBufferMerger to request
+      ///< that the TTree inside the file calls `TTree::FlushBaskets` (outside of the merging lock)
+      ///< and TBufferMerger will later ask for the write (inside the merging lock).
+      ///< To take advantage of this feature the class needs to overload `TObject::Write`
+      ///< and use this enum value accordingly.  (See `TTree::Write` and `TObject::Write`)
+      kOnlyPrepStep  = BIT(3)         
    };
 
    TObject();
diff --git a/core/base/src/TObject.cxx b/core/base/src/TObject.cxx
index 8d5cf8ba115..e9e54301d11 100644
--- a/core/base/src/TObject.cxx
+++ b/core/base/src/TObject.cxx
@@ -771,6 +771,9 @@ void TObject::UseCurrentStyle()
 
 Int_t TObject::Write(const char *name, Int_t option, Int_t bufsize) const
 {
+   if (R__unlikely(option & kOnlyPrepStep))
+      return 0;
+
    TString opt = "";
    if (option & kSingleKey)   opt += "SingleKey";
    if (option & kOverwrite)   opt += "OverWrite";
diff --git a/io/io/src/TBufferMergerFile.cxx b/io/io/src/TBufferMergerFile.cxx
index 249882bfc67..634fa8a5064 100644
--- a/io/io/src/TBufferMergerFile.cxx
+++ b/io/io/src/TBufferMergerFile.cxx
@@ -29,6 +29,10 @@ TBufferMergerFile::~TBufferMergerFile()
 
 Int_t TBufferMergerFile::Write(const char *name, Int_t opt, Int_t bufsize)
 {
+   // Make sure the compression of the basket is done in the unlocked thread and
+   // not in the locked section.
+   TMemFile::Write(name, opt | TObject::kOnlyPrepStep, bufsize);
+
    // Instead of Writing the TTree, doing a memcpy, Pushing to the queue
    // then Reading and then deleting, let's see if we can just merge using
    // the live TTree.
diff --git a/io/io/src/TDirectoryFile.cxx b/io/io/src/TDirectoryFile.cxx
index 15ae3e635c1..b4dc18fff3e 100644
--- a/io/io/src/TDirectoryFile.cxx
+++ b/io/io/src/TDirectoryFile.cxx
@@ -1785,7 +1785,8 @@ Int_t TDirectoryFile::Write(const char *, Int_t opt, Int_t bufsize)
    while ((obj=next())) {
       nbytes += obj->Write(0,opt,bufsize);
    }
-   SaveSelf(kTRUE);   // force save itself
+   if (R__likely(!(opt & kOnlyPrepStep)))
+      SaveSelf(kTRUE);   // force save itself
 
    return nbytes;
 }
diff --git a/tree/tree/src/TTree.cxx b/tree/tree/src/TTree.cxx
index 31240e96517..99837711e20 100644
--- a/tree/tree/src/TTree.cxx
+++ b/tree/tree/src/TTree.cxx
@@ -9576,6 +9576,8 @@ void TTree::UseCurrentStyle()
 Int_t TTree::Write(const char *name, Int_t option, Int_t bufsize) const
 {
    FlushBasketsImpl();
+   if (R__unlikely(option & kOnlyPrepStep))
+      return 0;
    return TObject::Write(name, option, bufsize);
 }
 
-- 
GitLab