/* @(#)root/multiproc:$Id$ */
// Author: Enrico Guiraud July 2015

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_TProcessExecutor
#define ROOT_TProcessExecutor

#include "MPCode.h"
#include "MPSendRecv.h"
#include "PoolUtils.h"
#include "TChain.h"
#include "TChainElement.h"
#include "TError.h"
#include "TFileCollection.h"
#include "TFileInfo.h"
#include "THashList.h"
#include "TMPClient.h"
#include "ROOT/TExecutor.hxx"
#include "TPoolProcessor.h"
#include "TPoolWorker.h"
#include "TSelector.h"
#include "TTreeReader.h"
#include <algorithm> //std::generate
#include <numeric> //std::iota
#include <string>
#include <type_traits> //std::result_of, std::enable_if
#include <functional> //std::reference_wrapper
#include <vector>

namespace ROOT {

class TProcessExecutor : public TExecutor<TProcessExecutor>, private TMPClient {
public:
   explicit TProcessExecutor(unsigned nWorkers = 0); //default number of workers is the number of processors
   ~TProcessExecutor() = default;
   //it doesn't make sense for a TProcessExecutor to be copied
   TProcessExecutor(const TProcessExecutor &) = delete;
   TProcessExecutor &operator=(const TProcessExecutor &) = delete;

   // Map
   template<class F, class Cond = noReferenceCond<F>>
   auto Map(F func, unsigned nTimes) -> std::vector<typename std::result_of<F()>::type>;
   /// \cond
   template<class F, class INTEGER, class Cond = noReferenceCond<F, INTEGER>>
   auto Map(F func, ROOT::TSeq<INTEGER> args) -> std::vector<typename std::result_of<F(INTEGER)>::type>;
   template<class F, class T, class Cond = noReferenceCond<F, T>>
   auto Map(F func, std::vector<T> &args) -> std::vector<typename std::result_of<F(T)>::type>;
   /// \endcond
   using TExecutor<TProcessExecutor>::Map;

   // ProcTree
   // these versions requires that procFunc returns a ptr to TObject or inheriting classes and takes a TTreeReader& (both enforced at compile-time)
   template<class F> auto ProcTree(const std::vector<std::string>& fileNames, F procFunc, const std::string& treeName = "", ULong64_t nToProcess = 0) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type;
   template<class F> auto ProcTree(const std::string& fileName, F procFunc, const std::string& treeName = "", ULong64_t nToProcess = 0) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type;
   template<class F> auto ProcTree(TFileCollection& files, F procFunc, const std::string& treeName = "", ULong64_t nToProcess = 0) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type;
   template<class F> auto ProcTree(TChain& files, F procFunc, const std::string& treeName = "", ULong64_t nToProcess = 0) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type;
   template<class F> auto ProcTree(TTree& tree, F procFunc, ULong64_t nToProcess = 0) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type;
   // these versions require a TSelector
   TList* ProcTree(const std::vector<std::string>& fileNames, TSelector& selector, const std::string& treeName = "", ULong64_t nToProcess = 0);
   TList* ProcTree(const std::string &fileName, TSelector& selector, const std::string& treeName = "", ULong64_t nToProcess = 0);
   TList* ProcTree(TFileCollection& files, TSelector& selector, const std::string& treeName = "", ULong64_t nToProcess = 0);
   TList* ProcTree(TChain& files, TSelector& selector, const std::string& treeName = "", ULong64_t nToProcess = 0);
   TList* ProcTree(TTree& tree, TSelector& selector, ULong64_t nToProcess = 0);

   void SetNWorkers(unsigned n) { TMPClient::SetNWorkers(n); }
   unsigned GetNWorkers() const { return TMPClient::GetNWorkers(); }

   template<class T, class R> T Reduce(const std::vector<T> &objs, R redfunc);

private:
   template<class T> void Collect(std::vector<T> &reslist);
   template<class T> void HandlePoolCode(MPCodeBufPair &msg, TSocket *sender, std::vector<T> &reslist);

   void FixLists(std::vector<TObject*> &lists);
   void Reset();
   void ReplyToFuncResult(TSocket *s);
   void ReplyToIdle(TSocket *s);

   unsigned fNProcessed; ///< number of arguments already passed to the workers
   unsigned fNToProcess; ///< total number of arguments to pass to the workers

   /// A collection of the types of tasks that TProcessExecutor can execute.
   /// It is used to interpret in the right way and properly reply to the
   /// messages received (see, for example, TProcessExecutor::HandleInput)
   enum class ETask : unsigned char {
      kNoTask,   ///< no task is being executed
      kMap,          ///< a Map method with no arguments is being executed
      kMapWithArg,   ///< a Map method with arguments is being executed
      kProcByRange,   ///< a ProcTree method is being executed and each worker will process a certain range of each file
      kProcByFile,    ///< a ProcTree method is being executed and each worker will process a different file
   };

   ETask fTaskType = ETask::kNoTask; ///< the kind of task that is being executed, if any
};


/************ TEMPLATE METHODS IMPLEMENTATION ******************/

//////////////////////////////////////////////////////////////////////////
/// Execute func (with no arguments) nTimes in parallel.
/// A vector containg executions' results is returned.
/// Functions that take more than zero arguments can be executed (with
/// fixed arguments) by wrapping them in a lambda or with std::bind.
template<class F, class Cond>
auto TProcessExecutor::Map(F func, unsigned nTimes) -> std::vector<typename std::result_of<F()>::type>
{
   using retType = decltype(func());
   //prepare environment
   Reset();
   fTaskType = ETask::kMap;

   //fork max(nTimes, fNWorkers) times
   unsigned oldNWorkers = GetNWorkers();
   if (nTimes < oldNWorkers)
      SetNWorkers(nTimes);
   TPoolWorker<F> worker(func);
   bool ok = Fork(worker);
   SetNWorkers(oldNWorkers);
   if (!ok)
   {
      Error("TProcessExecutor::Map", "[E][C] Could not fork. Aborting operation.");
      return std::vector<retType>();
   }

   //give out tasks
   fNToProcess = nTimes;
   std::vector<retType> reslist;
   reslist.reserve(fNToProcess);
   fNProcessed = Broadcast(PoolCode::kExecFunc, fNToProcess);

   //collect results, give out other tasks if needed
   Collect(reslist);

   //clean-up and return
   ReapWorkers();
   fTaskType = ETask::kNoTask;
   return reslist;
}

// tell doxygen to ignore this (\endcond closes the statement)
/// \cond

// actual implementation of the Map method. all other calls with arguments eventually
// call this one
template<class F, class T, class Cond>
auto TProcessExecutor::Map(F func, std::vector<T> &args) -> std::vector<typename std::result_of<F(T)>::type>
{
   //check whether func is callable
   using retType = decltype(func(args.front()));
   //prepare environment
   Reset();
   fTaskType = ETask::kMapWithArg;

   //fork max(args.size(), fNWorkers) times
   //N.B. from this point onwards, args is filled with undefined (but valid) values, since TPoolWorker moved its content away
   unsigned oldNWorkers = GetNWorkers();
   if (args.size() < oldNWorkers)
      SetNWorkers(args.size());
   TPoolWorker<F, T> worker(func, args);
   bool ok = Fork(worker);
   SetNWorkers(oldNWorkers);
   if (!ok)
   {
      Error("TProcessExecutor::Map", "[E][C] Could not fork. Aborting operation.");
      return std::vector<retType>();
   }

   //give out tasks
   fNToProcess = args.size();
   std::vector<retType> reslist;
   reslist.reserve(fNToProcess);
   std::vector<unsigned> range(fNToProcess);
   std::iota(range.begin(), range.end(), 0);
   fNProcessed = Broadcast(PoolCode::kExecFuncWithArg, range);

   //collect results, give out other tasks if needed
   Collect(reslist);

   //clean-up and return
   ReapWorkers();
   fTaskType = ETask::kNoTask;
   return reslist;
}

template<class F, class INTEGER, class Cond>
auto TProcessExecutor::Map(F func, ROOT::TSeq<INTEGER> args) -> std::vector<typename std::result_of<F(INTEGER)>::type>
{
   std::vector<INTEGER> vargs(args.size());
   std::copy(args.begin(), args.end(), vargs.begin());
   const auto &reslist = Map(func, vargs);
   return reslist;
}
// tell doxygen to stop ignoring code
/// \endcond

template<class T, class R>
T TProcessExecutor::Reduce(const std::vector<T> &objs, R redfunc)
{
   // check we can apply reduce to objs
   static_assert(std::is_same<decltype(redfunc(objs)), T>::value, "redfunc does not have the correct signature");
   return redfunc(objs);
}

template<class F>
auto TProcessExecutor::ProcTree(const std::vector<std::string>& fileNames, F procFunc, const std::string& treeName, ULong64_t nToProcess) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type
{
   using retType = typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type;
   static_assert(std::is_constructible<TObject*, retType>::value, "procFunc must return a pointer to a class inheriting from TObject, and must take a reference to TTreeReader as the only argument");

   //prepare environment
   Reset();
   unsigned nWorkers = GetNWorkers();

   //fork
   TPoolProcessor<F> worker(procFunc, fileNames, treeName, nWorkers, nToProcess);
   bool ok = Fork(worker);
   if(!ok) {
      Error("TProcessExecutor::ProcTree", "[E][C] Could not fork. Aborting operation.");
      return nullptr;
   }

   if(fileNames.size() < nWorkers) {
      //TTree entry granularity. For each file, we divide entries equally between workers
      fTaskType = ETask::kProcByRange;
      //Tell workers to start processing entries
      fNToProcess = nWorkers*fileNames.size(); //this is the total number of ranges that will be processed by all workers cumulatively
      std::vector<unsigned> args(nWorkers);
      std::iota(args.begin(), args.end(), 0);
      fNProcessed = Broadcast(PoolCode::kProcRange, args);
      if(fNProcessed < nWorkers)
         Error("TProcessExecutor::ProcTree", "[E][C] There was an error while sending tasks to workers. Some entries might not be processed.");
   } else {
      //file granularity. each worker processes one whole file as a single task
      fTaskType = ETask::kProcByFile;
      fNToProcess = fileNames.size();
      std::vector<unsigned> args(nWorkers);
      std::iota(args.begin(), args.end(), 0);
      fNProcessed = Broadcast(PoolCode::kProcFile, args);
      if(fNProcessed < nWorkers)
         Error("TProcessExecutor::ProcTree", "[E][C] There was an error while sending tasks to workers. Some entries might not be processed.");
   }

   //collect results, distribute new tasks
   std::vector<TObject*> reslist;
   Collect(reslist);

   //merge
   PoolUtils::ReduceObjects<TObject *> redfunc;
   auto res = redfunc(reslist);

   //clean-up and return
   ReapWorkers();
   fTaskType = ETask::kNoTask;
   return static_cast<retType>(res);
}


template<class F>
auto TProcessExecutor::ProcTree(const std::string& fileName, F procFunc, const std::string& treeName, ULong64_t nToProcess) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type
{
   std::vector<std::string> singleFileName(1, fileName);
   return ProcTree(singleFileName, procFunc, treeName, nToProcess);
}


template<class F>
auto TProcessExecutor::ProcTree(TFileCollection& files, F procFunc, const std::string& treeName, ULong64_t nToProcess) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type
{
   std::vector<std::string> fileNames(files.GetNFiles());
   unsigned count = 0;
   for(auto f : *static_cast<THashList*>(files.GetList()))
      fileNames[count++] = static_cast<TFileInfo*>(f)->GetCurrentUrl()->GetUrl();

   return ProcTree(fileNames, procFunc, treeName, nToProcess);
}


template<class F>
auto TProcessExecutor::ProcTree(TChain& files, F procFunc, const std::string& treeName, ULong64_t nToProcess) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type
{
   TObjArray* filelist = files.GetListOfFiles();
   std::vector<std::string> fileNames(filelist->GetEntries());
   unsigned count = 0;
   for(auto f : *filelist)
      fileNames[count++] = f->GetTitle();

   return ProcTree(fileNames, procFunc, treeName, nToProcess);
}


template<class F>
auto TProcessExecutor::ProcTree(TTree& tree, F procFunc, ULong64_t nToProcess) -> typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type
{
   using retType = typename std::result_of<F(std::reference_wrapper<TTreeReader>)>::type;
   static_assert(std::is_constructible<TObject*, retType>::value, "procFunc must return a pointer to a class inheriting from TObject, and must take a reference to TTreeReader as the only argument");

   //prepare environment
   Reset();
   unsigned nWorkers = GetNWorkers();

   //fork
   TPoolProcessor<F> worker(procFunc, &tree, nWorkers, nToProcess);
   bool ok = Fork(worker);
   if(!ok) {
      Error("TProcessExecutor::ProcTree", "[E][C] Could not fork. Aborting operation.");
      return nullptr;
   }

   //divide entries equally between workers
   fTaskType = ETask::kProcByRange;

   //tell workers to start processing entries
   fNToProcess = nWorkers; //this is the total number of ranges that will be processed by all workers cumulatively
   std::vector<unsigned> args(nWorkers);
   std::iota(args.begin(), args.end(), 0);
   fNProcessed = Broadcast(PoolCode::kProcTree, args);
   if(fNProcessed < nWorkers)
      Error("TProcessExecutor::ProcTree", "[E][C] There was an error while sending tasks to workers. Some entries might not be processed.");

   //collect results, distribute new tasks
   std::vector<TObject*> reslist;
   Collect(reslist);

   //merge
   PoolUtils::ReduceObjects<TObject *> redfunc;
   auto res = redfunc(reslist);

   //clean-up and return
   ReapWorkers();
   fTaskType = ETask::kNoTask;
   return static_cast<retType>(res);
}

//////////////////////////////////////////////////////////////////////////
/// Handle message and reply to the worker
template<class T>
void TProcessExecutor::HandlePoolCode(MPCodeBufPair &msg, TSocket *s, std::vector<T> &reslist)
{
   unsigned code = msg.first;
   if (code == PoolCode::kFuncResult) {
      reslist.push_back(std::move(ReadBuffer<T>(msg.second.get())));
      ReplyToFuncResult(s);
   } else if (code == PoolCode::kIdling) {
      ReplyToIdle(s);
   } else if(code == PoolCode::kProcResult) {
      if(msg.second != nullptr)
         reslist.push_back(std::move(ReadBuffer<T>(msg.second.get())));
      MPSend(s, MPCode::kShutdownOrder);
   } else if(code == PoolCode::kProcError) {
      const char *str = ReadBuffer<const char*>(msg.second.get());
      Error("TProcessExecutor::HandlePoolCode", "[E][C] a worker encountered an error: %s\n"
                                         "Continuing execution ignoring these entries.", str);
      ReplyToIdle(s);
      delete [] str;
   } else {
      // UNKNOWN CODE
      Error("TProcessExecutor::HandlePoolCode", "[W][C] unknown code received from server. code=%d", code);
   }
}

//////////////////////////////////////////////////////////////////////////
/// Listen for messages sent by the workers and call the appropriate handler function.
/// TProcessExecutor::HandlePoolCode is called on messages with a code < 1000 and
/// TMPClient::HandleMPCode is called on messages with a code >= 1000.
template<class T>
void TProcessExecutor::Collect(std::vector<T> &reslist)
{
   TMonitor &mon = GetMonitor();
   mon.ActivateAll();
   while (mon.GetActive() > 0) {
      TSocket *s = mon.Select();
      MPCodeBufPair msg = MPRecv(s);
      if (msg.first == MPCode::kRecvError) {
         Error("TProcessExecutor::Collect", "[E][C] Lost connection to a worker");
         Remove(s);
      } else if (msg.first < 1000)
         HandlePoolCode(msg, s, reslist);
      else
         HandleMPCode(msg, s);
   }
}

} // ROOT namespace

#endif
 TProcessExecutor.hxx:1
 TProcessExecutor.hxx:2
 TProcessExecutor.hxx:3
 TProcessExecutor.hxx:4
 TProcessExecutor.hxx:5
 TProcessExecutor.hxx:6
 TProcessExecutor.hxx:7
 TProcessExecutor.hxx:8
 TProcessExecutor.hxx:9
 TProcessExecutor.hxx:10
 TProcessExecutor.hxx:11
 TProcessExecutor.hxx:12
 TProcessExecutor.hxx:13
 TProcessExecutor.hxx:14
 TProcessExecutor.hxx:15
 TProcessExecutor.hxx:16
 TProcessExecutor.hxx:17
 TProcessExecutor.hxx:18
 TProcessExecutor.hxx:19
 TProcessExecutor.hxx:20
 TProcessExecutor.hxx:21
 TProcessExecutor.hxx:22
 TProcessExecutor.hxx:23
 TProcessExecutor.hxx:24
 TProcessExecutor.hxx:25
 TProcessExecutor.hxx:26
 TProcessExecutor.hxx:27
 TProcessExecutor.hxx:28
 TProcessExecutor.hxx:29
 TProcessExecutor.hxx:30
 TProcessExecutor.hxx:31
 TProcessExecutor.hxx:32
 TProcessExecutor.hxx:33
 TProcessExecutor.hxx:34
 TProcessExecutor.hxx:35
 TProcessExecutor.hxx:36
 TProcessExecutor.hxx:37
 TProcessExecutor.hxx:38
 TProcessExecutor.hxx:39
 TProcessExecutor.hxx:40
 TProcessExecutor.hxx:41
 TProcessExecutor.hxx:42
 TProcessExecutor.hxx:43
 TProcessExecutor.hxx:44
 TProcessExecutor.hxx:45
 TProcessExecutor.hxx:46
 TProcessExecutor.hxx:47
 TProcessExecutor.hxx:48
 TProcessExecutor.hxx:49
 TProcessExecutor.hxx:50
 TProcessExecutor.hxx:51
 TProcessExecutor.hxx:52
 TProcessExecutor.hxx:53
 TProcessExecutor.hxx:54
 TProcessExecutor.hxx:55
 TProcessExecutor.hxx:56
 TProcessExecutor.hxx:57
 TProcessExecutor.hxx:58
 TProcessExecutor.hxx:59
 TProcessExecutor.hxx:60
 TProcessExecutor.hxx:61
 TProcessExecutor.hxx:62
 TProcessExecutor.hxx:63
 TProcessExecutor.hxx:64
 TProcessExecutor.hxx:65
 TProcessExecutor.hxx:66
 TProcessExecutor.hxx:67
 TProcessExecutor.hxx:68
 TProcessExecutor.hxx:69
 TProcessExecutor.hxx:70
 TProcessExecutor.hxx:71
 TProcessExecutor.hxx:72
 TProcessExecutor.hxx:73
 TProcessExecutor.hxx:74
 TProcessExecutor.hxx:75
 TProcessExecutor.hxx:76
 TProcessExecutor.hxx:77
 TProcessExecutor.hxx:78
 TProcessExecutor.hxx:79
 TProcessExecutor.hxx:80
 TProcessExecutor.hxx:81
 TProcessExecutor.hxx:82
 TProcessExecutor.hxx:83
 TProcessExecutor.hxx:84
 TProcessExecutor.hxx:85
 TProcessExecutor.hxx:86
 TProcessExecutor.hxx:87
 TProcessExecutor.hxx:88
 TProcessExecutor.hxx:89
 TProcessExecutor.hxx:90
 TProcessExecutor.hxx:91
 TProcessExecutor.hxx:92
 TProcessExecutor.hxx:93
 TProcessExecutor.hxx:94
 TProcessExecutor.hxx:95
 TProcessExecutor.hxx:96
 TProcessExecutor.hxx:97
 TProcessExecutor.hxx:98
 TProcessExecutor.hxx:99
 TProcessExecutor.hxx:100
 TProcessExecutor.hxx:101
 TProcessExecutor.hxx:102
 TProcessExecutor.hxx:103
 TProcessExecutor.hxx:104
 TProcessExecutor.hxx:105
 TProcessExecutor.hxx:106
 TProcessExecutor.hxx:107
 TProcessExecutor.hxx:108
 TProcessExecutor.hxx:109
 TProcessExecutor.hxx:110
 TProcessExecutor.hxx:111
 TProcessExecutor.hxx:112
 TProcessExecutor.hxx:113
 TProcessExecutor.hxx:114
 TProcessExecutor.hxx:115
 TProcessExecutor.hxx:116
 TProcessExecutor.hxx:117
 TProcessExecutor.hxx:118
 TProcessExecutor.hxx:119
 TProcessExecutor.hxx:120
 TProcessExecutor.hxx:121
 TProcessExecutor.hxx:122
 TProcessExecutor.hxx:123
 TProcessExecutor.hxx:124
 TProcessExecutor.hxx:125
 TProcessExecutor.hxx:126
 TProcessExecutor.hxx:127
 TProcessExecutor.hxx:128
 TProcessExecutor.hxx:129
 TProcessExecutor.hxx:130
 TProcessExecutor.hxx:131
 TProcessExecutor.hxx:132
 TProcessExecutor.hxx:133
 TProcessExecutor.hxx:134
 TProcessExecutor.hxx:135
 TProcessExecutor.hxx:136
 TProcessExecutor.hxx:137
 TProcessExecutor.hxx:138
 TProcessExecutor.hxx:139
 TProcessExecutor.hxx:140
 TProcessExecutor.hxx:141
 TProcessExecutor.hxx:142
 TProcessExecutor.hxx:143
 TProcessExecutor.hxx:144
 TProcessExecutor.hxx:145
 TProcessExecutor.hxx:146
 TProcessExecutor.hxx:147
 TProcessExecutor.hxx:148
 TProcessExecutor.hxx:149
 TProcessExecutor.hxx:150
 TProcessExecutor.hxx:151
 TProcessExecutor.hxx:152
 TProcessExecutor.hxx:153
 TProcessExecutor.hxx:154
 TProcessExecutor.hxx:155
 TProcessExecutor.hxx:156
 TProcessExecutor.hxx:157
 TProcessExecutor.hxx:158
 TProcessExecutor.hxx:159
 TProcessExecutor.hxx:160
 TProcessExecutor.hxx:161
 TProcessExecutor.hxx:162
 TProcessExecutor.hxx:163
 TProcessExecutor.hxx:164
 TProcessExecutor.hxx:165
 TProcessExecutor.hxx:166
 TProcessExecutor.hxx:167
 TProcessExecutor.hxx:168
 TProcessExecutor.hxx:169
 TProcessExecutor.hxx:170
 TProcessExecutor.hxx:171
 TProcessExecutor.hxx:172
 TProcessExecutor.hxx:173
 TProcessExecutor.hxx:174
 TProcessExecutor.hxx:175
 TProcessExecutor.hxx:176
 TProcessExecutor.hxx:177
 TProcessExecutor.hxx:178
 TProcessExecutor.hxx:179
 TProcessExecutor.hxx:180
 TProcessExecutor.hxx:181
 TProcessExecutor.hxx:182
 TProcessExecutor.hxx:183
 TProcessExecutor.hxx:184
 TProcessExecutor.hxx:185
 TProcessExecutor.hxx:186
 TProcessExecutor.hxx:187
 TProcessExecutor.hxx:188
 TProcessExecutor.hxx:189
 TProcessExecutor.hxx:190
 TProcessExecutor.hxx:191
 TProcessExecutor.hxx:192
 TProcessExecutor.hxx:193
 TProcessExecutor.hxx:194
 TProcessExecutor.hxx:195
 TProcessExecutor.hxx:196
 TProcessExecutor.hxx:197
 TProcessExecutor.hxx:198
 TProcessExecutor.hxx:199
 TProcessExecutor.hxx:200
 TProcessExecutor.hxx:201
 TProcessExecutor.hxx:202
 TProcessExecutor.hxx:203
 TProcessExecutor.hxx:204
 TProcessExecutor.hxx:205
 TProcessExecutor.hxx:206
 TProcessExecutor.hxx:207
 TProcessExecutor.hxx:208
 TProcessExecutor.hxx:209
 TProcessExecutor.hxx:210
 TProcessExecutor.hxx:211
 TProcessExecutor.hxx:212
 TProcessExecutor.hxx:213
 TProcessExecutor.hxx:214
 TProcessExecutor.hxx:215
 TProcessExecutor.hxx:216
 TProcessExecutor.hxx:217
 TProcessExecutor.hxx:218
 TProcessExecutor.hxx:219
 TProcessExecutor.hxx:220
 TProcessExecutor.hxx:221
 TProcessExecutor.hxx:222
 TProcessExecutor.hxx:223
 TProcessExecutor.hxx:224
 TProcessExecutor.hxx:225
 TProcessExecutor.hxx:226
 TProcessExecutor.hxx:227
 TProcessExecutor.hxx:228
 TProcessExecutor.hxx:229
 TProcessExecutor.hxx:230
 TProcessExecutor.hxx:231
 TProcessExecutor.hxx:232
 TProcessExecutor.hxx:233
 TProcessExecutor.hxx:234
 TProcessExecutor.hxx:235
 TProcessExecutor.hxx:236
 TProcessExecutor.hxx:237
 TProcessExecutor.hxx:238
 TProcessExecutor.hxx:239
 TProcessExecutor.hxx:240
 TProcessExecutor.hxx:241
 TProcessExecutor.hxx:242
 TProcessExecutor.hxx:243
 TProcessExecutor.hxx:244
 TProcessExecutor.hxx:245
 TProcessExecutor.hxx:246
 TProcessExecutor.hxx:247
 TProcessExecutor.hxx:248
 TProcessExecutor.hxx:249
 TProcessExecutor.hxx:250
 TProcessExecutor.hxx:251
 TProcessExecutor.hxx:252
 TProcessExecutor.hxx:253
 TProcessExecutor.hxx:254
 TProcessExecutor.hxx:255
 TProcessExecutor.hxx:256
 TProcessExecutor.hxx:257
 TProcessExecutor.hxx:258
 TProcessExecutor.hxx:259
 TProcessExecutor.hxx:260
 TProcessExecutor.hxx:261
 TProcessExecutor.hxx:262
 TProcessExecutor.hxx:263
 TProcessExecutor.hxx:264
 TProcessExecutor.hxx:265
 TProcessExecutor.hxx:266
 TProcessExecutor.hxx:267
 TProcessExecutor.hxx:268
 TProcessExecutor.hxx:269
 TProcessExecutor.hxx:270
 TProcessExecutor.hxx:271
 TProcessExecutor.hxx:272
 TProcessExecutor.hxx:273
 TProcessExecutor.hxx:274
 TProcessExecutor.hxx:275
 TProcessExecutor.hxx:276
 TProcessExecutor.hxx:277
 TProcessExecutor.hxx:278
 TProcessExecutor.hxx:279
 TProcessExecutor.hxx:280
 TProcessExecutor.hxx:281
 TProcessExecutor.hxx:282
 TProcessExecutor.hxx:283
 TProcessExecutor.hxx:284
 TProcessExecutor.hxx:285
 TProcessExecutor.hxx:286
 TProcessExecutor.hxx:287
 TProcessExecutor.hxx:288
 TProcessExecutor.hxx:289
 TProcessExecutor.hxx:290
 TProcessExecutor.hxx:291
 TProcessExecutor.hxx:292
 TProcessExecutor.hxx:293
 TProcessExecutor.hxx:294
 TProcessExecutor.hxx:295
 TProcessExecutor.hxx:296
 TProcessExecutor.hxx:297
 TProcessExecutor.hxx:298
 TProcessExecutor.hxx:299
 TProcessExecutor.hxx:300
 TProcessExecutor.hxx:301
 TProcessExecutor.hxx:302
 TProcessExecutor.hxx:303
 TProcessExecutor.hxx:304
 TProcessExecutor.hxx:305
 TProcessExecutor.hxx:306
 TProcessExecutor.hxx:307
 TProcessExecutor.hxx:308
 TProcessExecutor.hxx:309
 TProcessExecutor.hxx:310
 TProcessExecutor.hxx:311
 TProcessExecutor.hxx:312
 TProcessExecutor.hxx:313
 TProcessExecutor.hxx:314
 TProcessExecutor.hxx:315
 TProcessExecutor.hxx:316
 TProcessExecutor.hxx:317
 TProcessExecutor.hxx:318
 TProcessExecutor.hxx:319
 TProcessExecutor.hxx:320
 TProcessExecutor.hxx:321
 TProcessExecutor.hxx:322
 TProcessExecutor.hxx:323
 TProcessExecutor.hxx:324
 TProcessExecutor.hxx:325
 TProcessExecutor.hxx:326
 TProcessExecutor.hxx:327
 TProcessExecutor.hxx:328
 TProcessExecutor.hxx:329
 TProcessExecutor.hxx:330
 TProcessExecutor.hxx:331
 TProcessExecutor.hxx:332
 TProcessExecutor.hxx:333
 TProcessExecutor.hxx:334
 TProcessExecutor.hxx:335
 TProcessExecutor.hxx:336
 TProcessExecutor.hxx:337
 TProcessExecutor.hxx:338
 TProcessExecutor.hxx:339
 TProcessExecutor.hxx:340
 TProcessExecutor.hxx:341
 TProcessExecutor.hxx:342
 TProcessExecutor.hxx:343
 TProcessExecutor.hxx:344
 TProcessExecutor.hxx:345
 TProcessExecutor.hxx:346
 TProcessExecutor.hxx:347
 TProcessExecutor.hxx:348
 TProcessExecutor.hxx:349
 TProcessExecutor.hxx:350
 TProcessExecutor.hxx:351
 TProcessExecutor.hxx:352
 TProcessExecutor.hxx:353
 TProcessExecutor.hxx:354
 TProcessExecutor.hxx:355
 TProcessExecutor.hxx:356
 TProcessExecutor.hxx:357
 TProcessExecutor.hxx:358
 TProcessExecutor.hxx:359
 TProcessExecutor.hxx:360
 TProcessExecutor.hxx:361
 TProcessExecutor.hxx:362
 TProcessExecutor.hxx:363
 TProcessExecutor.hxx:364
 TProcessExecutor.hxx:365
 TProcessExecutor.hxx:366
 TProcessExecutor.hxx:367
 TProcessExecutor.hxx:368
 TProcessExecutor.hxx:369
 TProcessExecutor.hxx:370
 TProcessExecutor.hxx:371
 TProcessExecutor.hxx:372
 TProcessExecutor.hxx:373
 TProcessExecutor.hxx:374
 TProcessExecutor.hxx:375
 TProcessExecutor.hxx:376
 TProcessExecutor.hxx:377
 TProcessExecutor.hxx:378
 TProcessExecutor.hxx:379
 TProcessExecutor.hxx:380
 TProcessExecutor.hxx:381
 TProcessExecutor.hxx:382
 TProcessExecutor.hxx:383
 TProcessExecutor.hxx:384
 TProcessExecutor.hxx:385
 TProcessExecutor.hxx:386
 TProcessExecutor.hxx:387
 TProcessExecutor.hxx:388
 TProcessExecutor.hxx:389
 TProcessExecutor.hxx:390
 TProcessExecutor.hxx:391
 TProcessExecutor.hxx:392