BackgroundProcess.cpp
Go to the documentation of this file.
1 #include <NovaDAQUtilities/BackgroundProcess.h>
2 #include <NovaDAQUtilities/ProcessUtils.h>
3 #include <boost/filesystem.hpp>
4 #include <boost/lexical_cast.hpp>
5 #include <boost/algorithm/string.hpp>
6 
7 #include <iostream>
8 #include <fstream>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11 #include <signal.h>
12 
13 namespace BFS = boost::filesystem;
14 
15 namespace novadaq {
16 
17 // at the moment, we don't have any mutexes protecting data that
18 // might be accessed in different threads since all of that data
19 // are simple variables, but we should look into mutexes at some point.
20 
23  _description(description) {
24 
25  //std::cout << "BP constructor, cp1 " << getpid()
26  // << ":" << this << std::endl;
27  _childProcessId = -1;
28  _commandString = commandString;
29  _exitStatus = -99;
30 
32  new boost::thread(boost::bind(&novadaq::Runnable::run, this));
33  //std::cout << "BP constructor, cp2 " << getpid()
34  // << ":" << this << std::endl;
35 }
36 
38 
39  // stop and clean up our worker thread
40  //std::cout << "BP destructor, cp1 " << getpid()
41  // << ":" << this << std::endl;
42  this->stop();
43  //std::cout << "BP destructor, cp2 " << getpid()
44  // << ":" << this << std::endl;
45  this->waitForDone();
46  //std::cout << "BP destructor, cp3 " << getpid()
47  // << ":" << this << std::endl;
48  delete _workerThread;
49  //std::cout << "BP destructor, cp4 " << getpid()
50  // << ":" << this << std::endl;
51 }
52 
53 // blocks!
55  //std::cout << "BP waitForDone method, cp1 " << getpid()
56  // << ":" << this << std::endl;
57 
58  if (_hasCompleted) {return;}
59 
60  //std::cout << "BP waitForDone method, cp2 " << getpid()
61  // << ":" << this << std::endl;
62 
63  _workerThread->join();
64  //std::cout << "BP waitForDone method, cp3 " << getpid()
65  // << ":" << this << std::endl;
66 }
67 
68 /**
69  * Waits until the process is done or the specified timeout
70  * has expired. Returns true if the process finishes before the
71  * timeout or false if the timeout expires.
72  */
73 bool BackgroundProcess::timedWaitForDone(uint32_t microSeconds) {
74 
75  //std::cout << "BP timedWaitForDone method " << __LINE__ << std::endl;
76  if (isDone()) {return true;}
77 
78  uint32_t sleepInterval = microSeconds / 10;
79  if (sleepInterval < 10) {sleepInterval = 10;}
80  //std::cout << "BP timedWaitForDone method " << __LINE__ << " "
81  // << sleepInterval << std::endl;
82  uint32_t loopCount = microSeconds / sleepInterval;
83  if (loopCount < 1) {loopCount = 1;}
84  //std::cout << "BP timedWaitForDone method " << __LINE__ << " "
85  // << loopCount << std::endl;
86 
87  for (uint32_t idx = 0; idx < loopCount; ++idx) {
88  usleep(sleepInterval);
89  //std::cout << "BP timedWaitForDone method " << __LINE__ << " "
90  // << isDone() << std::endl;
91  if (isDone()) {return true;}
92  }
93 
94  //std::cout << "BP timedWaitForDone method " << __LINE__ << std::endl;
95  return false;
96 }
97 
99  return _stdoutOutput;
100 }
101 
103  return _stderrOutput;
104 }
105 
107  return _exitStatus;
108 }
109 
111  std::string tmpString;
112  std::string workString("*** BackgroundProcess result: *** \n");
113  workString.append(" Command: " + _commandString + "\n");
114  workString.append(" Description: " + _description + "\n");
115  workString.append(" Exit status = " +
116  boost::lexical_cast<std::string>(_exitStatus) + "\n");
117 
118  tmpString = " STDOUT: " + _stdoutOutput;
119  boost::replace_all(tmpString, "\n", "\n STDOUT: ");
120  workString.append(tmpString + "\n");
121 
122  tmpString = " STDERR: " + _stderrOutput;
123  boost::replace_all(tmpString, "\n", "\n STDERR: ");
124  workString.append(tmpString + "\n");
125 
126  return workString;
127 }
128 
130 
131  //std::cout << "BP run method, cp1 " << getpid()
132  // << ":" << this << std::endl;
133  _running = true;
134 
135  //while (! _stopRequested) {
136  // sleep(1);
137  // std::cout << "BBB " << this
138  // << " " << _stopRequested << std::endl;
139  //}
140 
141  int thisPID = getpid();
142  std::string stdoutFile("/tmp/bgprocess_" +
143  boost::lexical_cast<std::string>(thisPID) +
144  "_" +
145  boost::lexical_cast<std::string>(this) +
146  ".stdout");
147  std::string stderrFile("/tmp/bgprocess_" +
148  boost::lexical_cast<std::string>(thisPID) +
149  "_" +
150  boost::lexical_cast<std::string>(this) +
151  ".stderr");
152  std::string retcodeFile("/tmp/bgprocess_" +
153  boost::lexical_cast<std::string>(thisPID) +
154  "_" +
155  boost::lexical_cast<std::string>(this) +
156  ".status");
157 
158  _childProcessId = fork();
159 
160  // child
161  //std::cout << "BP run method, cp2 " << getpid()
162  // << ":" << this
163  // << " " << _childProcessId << std::endl;
164  if (_childProcessId == 0) {
165  //std::cout << "BP run method, cp3a " << getpid()
166  // << ":" << this << std::endl;
167 
168  std::string cmdStringWithRedirection("(" + _commandString + ")");
169  cmdStringWithRedirection.append(" 1>" + stdoutFile);
170  cmdStringWithRedirection.append(" 2>" + stderrFile);
171  //std::cout << "BP run method, cp3b " << getpid()
172  // << ":" << this << " "
173  // << cmdStringWithRedirection << std::endl;
174 
175  int retcode = system(cmdStringWithRedirection.c_str());
176  //std::cout << "BP run method, cp3c " << getpid()
177  // << ":" << this << " " << retcode << std::endl;
178 
179  std::ofstream statusStream(retcodeFile.c_str());
180  statusStream << retcode;
181  statusStream.close();
182  //std::cout << "BP run method, cp3d " << getpid()
183  // << ":" << this << " " << retcode << std::endl;
184 
185  // 29-Nov-2010, KAB: the original exit() call was taking a long
186  // time to complete in some cases. My theory is that this was
187  // due to the closing of connections and other cleanup that is
188  // part of the exit() operation. So, I've added the kill -9
189  // call in front of the exit() call. This should cause the
190  // child to exit without waiting (which is what we want, I believe;
191  // the only purpose of the child is to run the system() call above).
192  // The exit() call has been left in as a catch-all.
193  kill(getpid(), 9);
194  exit(retcode); // bogus
195  }
196 
197  // parent
198  else {
199  //std::cout << "BP run method, cp4a " << getpid()
200  // << ":" << this << " " << _childProcessId << std::endl;
201  int status;
202  waitpid(_childProcessId, &status, 0);
203  //std::cout << "BP run method, cp4b " << getpid()
204  // << ":" << this << " " << WEXITSTATUS(status) << std::endl;
205 
206  std::string tmpString;
207  std::ifstream stdoutStream(stdoutFile.c_str());
208  if (stdoutStream.fail()) {
209  _stdoutOutput = "Error opening file " + stdoutFile;
210  }
211  else {
212  std::getline(stdoutStream, tmpString);
213  while (! stdoutStream.eof() && ! stdoutStream.fail()) {
214  _stdoutOutput.append(tmpString + "\n");
215  std::getline(stdoutStream, tmpString);
216  }
217  stdoutStream.close();
218 
219  BFS::remove(stdoutFile);
220  }
221 
222  std::ifstream stderrStream(stderrFile.c_str());
223  if (stderrStream.fail()) {
224  _stderrOutput = "Error opening file " + stderrFile;
225  }
226  else {
227  std::getline(stderrStream, tmpString);
228  while (! stderrStream.eof() && ! stderrStream.fail()) {
229  _stderrOutput.append(tmpString + "\n");
230  std::getline(stderrStream, tmpString);
231  }
232  stderrStream.close();
233 
234  BFS::remove(stderrFile);
235  }
236 
237  std::ifstream statusStream(retcodeFile.c_str());
238  if (! statusStream.fail()) {
239  statusStream >> _exitStatus;
240  statusStream.close();
241  BFS::remove(retcodeFile);
242  }
243  }
244  //std::cout << "BP run method, cp5 " << getpid()
245  // << ":" << this
246  // << " " << _childProcessId << std::endl;
247 
248  _running = false;
249  _hasCompleted = true;
250 }
251 
253  //std::cout << "BP stop method, cp1 " << getpid()
254  // << ":" << this
255  // << " " << _childProcessId << std::endl;
256 
257  if (_hasCompleted) {return;}
258 
259  _stopRequested = true;
260 
261  if (_childProcessId > 0) {
262  std::vector<int> descendantPidList;
264  descendantPidList);
265  //std::cout << "BP stop method, cp2 " << getpid()
266  // << ":" << this << " killing pid "
267  // << _childProcessId << std::endl;
268  kill(_childProcessId, 9);
269  if (status == 0) {
271  idx < descendantPidList.size(); ++idx) {
272  //std::cout << "BP stop method, cp3 " << getpid()
273  // << ":" << this << ", killing pid "
274  // << descendantPidList[idx] << std::endl;
275  kill(descendantPidList[idx], 9);
276  }
277  }
278  _childProcessId = 0;
279  }
280  //std::cout << "BP stop method, cp4 " << getpid()
281  // << ":" << this << std::endl;
282 }
283 
284 }
system("rm -rf microbeam.root")
int status
Definition: fabricate.py:1613
virtual void run()=0
virtual bool isDone() const
Definition: Runnable.h:80
bool replace_all(std::string &in, std::string const &from, std::string const &to)
Replace all occurrences of from in string with to.
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic >::Index size_type
Definition: typedefs.hpp:11
static int getDescendantProcessIds(const int &parentPid, std::vector< int > &descendantPidList)
::xsd::cxx::tree::string< char, simple_type > string
Definition: Database.h:154
bool timedWaitForDone(uint32_t microSeconds)
BackgroundProcess(std::string commandString, std::string description="")
exit(0)