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
#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 <memory>
#include <cassert>
#include <iostream>
#include <thread>
#include <atomic>
#include <sstream>

std::atomic<bool> waitToStart{true};
std::atomic<unsigned int> countdownToWrite{0};

void printHelp(const char* iName, int iDefaultNThreads) {
  std::cout << iName << " [number of threads] [gDebug value]\n\n"
            << "[number of threads] number of threads to use in test\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" << std::endl;
}

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

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

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

TList* createList() {
  auto returnValue = new TList();

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

  return returnValue;
}

int main(int argc, char** argv) {
  auto values = parseOptionsForNumberOfThreadsAndgDebug(argc, argv);

  const int kNThreads = values.first;
  countdownToWrite = kNThreads;

  gDebug = values.second;

  //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);

  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([i]() {
      static thread_local TThread s_thread_guard;

      std::stringstream nameStream;
      nameStream << "write_thread_" << i << ".root";

      auto theList = createList();

      while (waitToStart)
        ;
      TFile f(nameStream.str().c_str(), "RECREATE", "test");

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

      --countdownToWrite;
      while (countdownToWrite != 0)
        ;

      for (unsigned int i = 0; i < 100; ++i) {
        listTree->Fill();
      }
      f.Write();
      f.Close();
    })));
  }
  waitToStart = false;
  for (auto& t : threads) {
    t->join();
  }

  return 0;
}