Getting a segfault in a Streamer. Can I flush a TBuffer?

So I’m working with this custom streamer here, for a class called TFCSParametrizationChain(apologies, it’s perhaps not very elegant, and there is a lot about it that’s opaque to me).

We had to change some pointers to fix some memory leaks, but in doing so I’ve introduced a segfault.
When creating a new simulation model we make and write many TFCSParametrizationChain objects to a single root file. The writing of more than one TFCSParameterizationChain to a single root file is causing me a seg fault, and I think it’s coming from the streamer.

I think what’s happening is each TFCSParametrizationChain probably doesn’t stay in scope for the whole writing process, so things held in unique_ptr are getting deleted. The m_writtenBases are passed to the TBuffer R__b using a left shift here, but their pointer is only valid for as long as their parent TFCSParametrizationChain is. However perhaps TBuffer for our root file assumes the pointers we want to write will be valid till we are finished with all of them, so when the m_writtenBases are cleared by their unique_ptrs they haven’t been written yet.

Just not clearing the m_writtenBases (on this line) isn’t enough to prevent a segfault. However, if I make the m_writtenBases into a vector of raw pointers, rather than unique_ptrs, so the m_writtenBases are never deleted and outlast the lifetime of TFCSParametrizationChain the segfault is resolved.

I don’t think I will be permitted a merge request that leaks memory like that (quite correctly), so either I need to figure out how to make m_writtenBases last for the lifetime of the buffer that’s passed to the streamer, R__b, or I need to get the buffer to “flush” before TFCSParametrizationChain goes out of scope.

Would you have any ideas on flushing a TBuffer? Or getting a TBuffer to take over the memory management of my pointers till it writes?


ROOT Version: 6.26/04
Platform: lxplus
Compiler: Built for linuxx8664gcc on Jun 07 2022, 16:01:16
From tags/v6-26-04@v6-26-04
With g++ (GCC) 11.2.0

You have a very unusual Streamer. It not only put some of the data in the TBuffer but also directly in the related TFile/TDirectory (i.e. in another TBuffer per se). TBuffer will never ever hold a reference to the data. It immediately transform the date from in-memory representation to platform independent binary representation. TFile/TDirectory can retain references to the objects but usually does only for TTree and TH1 (and derived).

The problem is likely the pattern:

    TFCSParametrizationChain::Chain_t &R__stl = m_chain;
    int R__n = int(R__stl.size());
    R__b << R__n;
    if (R__n) {
      TFCSParametrizationChain::Chain_t::iterator R__k;
      for (R__k = R__stl.begin(); R__k != R__stl.end(); ++R__k) {
        std::unique_ptr<TFCSParametrizationBase> R__t(*R__k);

where the last line seem to duplicate the ownership of the object from m_chain so both m_chain and the unique_ptr will delete the same object (i.e. seg fault).

Consider

    TFCSParametrizationChain::Chain_t &R__stl = m_chain;
    int R__n = int(R__stl.size());
    R__b << R__n;
    if (R__n) {
      TFCSParametrizationChain::Chain_t::iterator R__k;
      for (R__k = R__stl.begin(); R__k != R__stl.end(); ++R__k) {
        std::unique_ptr<TFCSParametrizationBase> R__t(*R__k);
        std::unique_ptr<TFCSParametrizationBase> new_R__t;
        if (dir && R__t != nullptr) {
          dir->WriteTObject(R__t.get());
          new_R__t = std::make_unique<TFCSParametrizationPlaceholder>(
              R__t->GetName(), TString("Placeholder for: ") + R__t->GetTitle());

        R__b << m_writtenBases.back().get();
      }
    }

Consider using this:

    TFCSParametrizationChain::Chain_t &R__stl = m_chain;
    int R__n = int(R__stl.size());
    R__b << R__n;
    if (R__n) {
      TFCSParametrizationChain::Chain_t::iterator R__k;
      for (R__k = R__stl.begin(); R__k != R__stl.end(); ++R__k) {
        TFCSParametrizationBase *R__t = *R__k; // Ownership stays with m_chain.
        if (dir && R__t != nullptr) {
          dir->WriteTObject(R__t);
          TFCSParametrizationPlaceholder tmp{ R__t->GetName(), TString("Placeholder for: ") + R__t->GetTitle() );
          R__b.WriteObject( &tmp, false /* tell R__b object with same address are actually different );
        }
      }
    }
1 Like

Wow, many thanks. This has really helpful, I didn’t write the first iteration of this streamer, so I didn’t understand the intent here either. You have shown me what this code was trying to do, and how to do it correctly :slight_smile:

It’s working great and I will get an MR in.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.