Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
#include "TFile.h"
#include "TTree.h"
#include "TBranch.h"
#include "TClass.h"
#include "TThread.h"
#include "TVirtualStreamerInfo.h"

#include "TList.h"
#include "TMap.h"
#include "TObjString.h"
#include "TH1F.h"

#include "FWCore/FWLite/interface/FWLiteEnabler.h"

#include <memory>
#include <cassert>
#include <iostream>
#include <thread>
#include <atomic>

std::atomic<bool> waitToStart{true};

void printHelp(const char* iName, int iDefaultNThreads) {
  std::cout << iName << " [number of threads] [filename] [gDebug value]\n\n"
            << "[number of threads] number of threads to use in test\n"
            << "[filename] name of CMSSW file to read\n"
            << "[gDebug value] value of gDebug to pass to ROOT (gDebug=1 is useful)\n"
            << "If no arguments are given " << iDefaultNThreads
            << " threads will be used and a dummy file will be created." << std::endl;
}

const std::string kDefaultFileName("read_test_dummy.root");

std::tuple<int, std::string, int> parseOptions(int argc, char** argv) {
  constexpr int kDefaultNThreads = 4;
  int kDefaultgDebug = gDebug;
  int nThreads = kDefaultNThreads;
  int newGDebug = kDefaultgDebug;
  std::string fileName(kDefaultFileName);
  if (argc >= 2) {
    if (strcmp("-h", argv[1]) == 0) {
      printHelp(argv[0], kDefaultNThreads);
      exit(0);
    }

    nThreads = atoi(argv[1]);
  }
  if (argc >= 3) {
    fileName = argv[2];
  }
  if (argc == 4) {
    newGDebug = atoi(argv[3]);
  }

  if (argc > 4) {
    printHelp(argv[0], kDefaultNThreads);
    exit(1);
  }
  return std::make_tuple(nThreads, fileName, newGDebug);
}

void createDummyFile() {
  auto theList = new TList();

  for (unsigned int i = 0; i < 10; ++i) {
    theList->Add(new TList());
    theList->Add(new TMap());
    theList->Add(new TObjString());
    theList->Add(new TH1F());
    theList->Add(new TH1D());
  }

  TFile f(kDefaultFileName.c_str(), "RECREATE", "test");

  auto listTree = new TTree("Events", "TheList");
  listTree->Branch("theList", "TList", &theList);

  for (unsigned int i = 0; i < 100; ++i) {
    listTree->Fill();
  }
  f.Write();
  f.Close();
}

int main(int argc, char** argv) {
  auto options = parseOptions(argc, argv);

  const int kNThreads = std::get<0>(options);

  auto const kFileName = std::get<1>(options);

  gDebug = std::get<2>(options);

  FWLiteEnabler::enable();

  //Tell Root we want to be multi-threaded
  TThread::Initialize();
  //When threading, also have to keep ROOT from logging all TObjects into a list
  TObject::SetObjectStat(false);
  //Have to avoid having Streamers modify themselves after they have been used
  TVirtualStreamerInfo::Optimize(false);

  if (kFileName == kDefaultFileName) {
    createDummyFile();
  }

  std::vector<std::shared_ptr<std::thread>> threads;
  threads.reserve(kNThreads);

  for (int i = 0; i < kNThreads; ++i) {
    threads.push_back(std::make_shared<std::thread>(std::thread([&kFileName]() {
      static thread_local TThread s_thread_guard;
      while (waitToStart)
        ;
      std::unique_ptr<TFile> f{TFile::Open(kFileName.c_str())};
      assert(f.get());

      TTree* eventTree = dynamic_cast<TTree*>(f.get()->Get("Events"));
      assert(eventTree);

      for (Long64_t i = 0, iEnd = eventTree->GetEntries(); i != iEnd; ++i) {
        eventTree->GetEntry(i, 1);
      }

      f.get()->Close();
    })));
  }
  waitToStart = false;
  for (auto& t : threads) {
    t->join();
  }

  return 0;
}