DDS  ver. 3.6
MonitoringThread.h
Go to the documentation of this file.
1 // Copyright 2014 GSI, Inc. All rights reserved.
2 //
3 //
4 //
5 
6 #ifndef __DDS__MonitoringThread__
7 #define __DDS__MonitoringThread__
8 
9 // DDS
10 #include "Logger.h"
11 #include "Process.h"
12 // STL
13 #include <chrono>
14 #include <functional>
15 #include <mutex>
16 #include <thread>
17 // API
18 #include <csignal>
19 
20 namespace dds
21 {
23  {
24  typedef std::function<bool()> callbackFunction_t;
25  // the function to call, the interval (in sec) the function should be called at
26  typedef std::pair<callbackFunction_t, std::chrono::seconds> callbackValue_t;
27 
28  private:
30  {
31  }
33  {
34  }
35 
36  public:
39  {
41  return instance;
42  }
43 
48  void start(double _idleTime, const std::function<void(void)>& _idleCallback)
49  {
50  // Looping monitoring thread with a step of 1 sec up to *Unlimited* sec (size of int)
51  static const std::chrono::seconds INTERVAL_STEP(1);
52  static const std::chrono::seconds WAITING_TIME(20);
53 
54  updateIdle();
55 
56  std::thread t(
57  [this, &_idleCallback, _idleTime]()
58  {
59  try
60  {
61  std::chrono::seconds secInterval(0);
62  while (true)
63  {
64  // handle exceptions of the custom actions separately to prevent breaks of the
65  // monitoring thread
66  try
67  {
68  std::lock_guard<std::mutex> lock(m_registeredCallbackFunctionsMutex);
69  // Call registered callback functions
70  // We use Erase-remove idiom to execute callback and remove expired if needed.
71  m_registeredCallbackFunctions.erase(
72  remove_if(m_registeredCallbackFunctions.begin(),
73  m_registeredCallbackFunctions.end(),
74  [&](callbackValue_t& i)
75  {
76  // A callback function can return
77  // false, which
78  // means it wants to be unregistered
79  // (expire)
80  const int nCurInterval =
81  std::chrono::duration<int>(secInterval).count();
82  const int nInterval = std::chrono::duration<int>(i.second).count();
83  if (nCurInterval != 0 && nCurInterval >= nInterval &&
84  0 == (nCurInterval % nInterval))
85  {
87  << "MONITORING: calling callback at interval of "
88  << std::chrono::duration<int>(i.second).count();
89  return (!i.first());
90  }
91  return false;
92  }),
93  m_registeredCallbackFunctions.end());
94  }
95  catch (std::exception& _e)
96  {
97  LOG(dds::misc::error) << "MonitoringThread exception on custom actions: " << _e.what();
98  }
99  catch (...)
100  {
101  // Ignore any exception here to let the monitoring thread continue whatever it takes
102  }
103 
104  std::chrono::seconds idleTime;
105  // Check if process is idle.
106  {
107  std::lock_guard<std::mutex> lock(m_mutex);
108  std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now();
109  idleTime =
110  std::chrono::duration_cast<std::chrono::seconds>(currentTime - m_startIdleTime);
111  }
112 
113  if (idleTime.count() > _idleTime)
114  {
115  // First call idle callback
116  LOG(dds::misc::info) << "The process is idle for " << idleTime.count()
117  << " sec. Call idle callback and wait "
118  << std::chrono::duration<int>(WAITING_TIME).count() << "s";
119  // handle exceptions of the custom idle callback separately to prevent breaks of the
120  // monitoring thread
121  try
122  {
123  _idleCallback();
124  }
125  catch (std::exception& _e)
126  {
128  << "MonitoringThread exception on custom idle function: " << _e.what();
129  }
130  catch (...)
131  {
132  // Ignore any exception here to let the monitoring thread continue whatever it takes
133  }
134  std::this_thread::sleep_for(WAITING_TIME);
135 
136  // Call terminate
137  LOG(dds::misc::info) << "Sending SIGTERM to this process...";
138  std::raise(SIGTERM);
139  std::this_thread::sleep_for(WAITING_TIME);
140 
141  // Kill process
142  LOG(dds::misc::info) << "The process still exists. Killing the process...";
143  killProcess();
144  }
145 
146  std::this_thread::sleep_for(INTERVAL_STEP);
147  secInterval += INTERVAL_STEP;
148  }
149  }
150  catch (std::exception& _e)
151  {
152  LOG(dds::misc::error) << "MonitoringThread exception: " << _e.what();
153  }
154  });
155  t.detach();
156  }
157 
158  void updateIdle()
159  {
160  std::lock_guard<std::mutex> lock(m_mutex);
161  m_startIdleTime = std::chrono::steady_clock::now();
162  }
163 
164  void registerCallbackFunction(callbackFunction_t _handler, const std::chrono::seconds& _interval)
165  {
166  std::lock_guard<std::mutex> lock(m_registeredCallbackFunctionsMutex);
167  m_registeredCallbackFunctions.push_back(make_pair(_handler, _interval));
168  }
169 
170  private:
171  void killProcess()
172  {
173  pid_t pidToKill(::getpid());
174  if (pidToKill > 0 && dds::misc::IsProcessRunning(pidToKill))
175  {
176  LOG(dds::misc::log_stdout) << " self exiting (" << pidToKill << ")...";
177  // TODO: Maybe we need more validations of the process before
178  // sending a signal. We don't want to kill someone else.
179  kill(pidToKill, SIGTERM);
180 
181  // Waiting for the process to finish
182  size_t iter(0);
183  const size_t max_iter = 30;
184  while (iter <= max_iter)
185  {
186  if (!dds::misc::IsProcessRunning(pidToKill))
187  {
188  LOG(dds::misc::log_stdout) << std::endl;
189  break;
190  }
191  LOG(dds::misc::log_stdout) << ".";
192  sleep(1); // sleeping for 1 second
193  ++iter;
194  }
195  if (dds::misc::IsProcessRunning(pidToKill))
196  {
197  LOG(dds::misc::error) << "FAILED to close the process.";
198  LOG(dds::misc::warning) << "Sending unconditional terminate to (" << pidToKill << ")...";
199  kill(pidToKill, SIGKILL);
200  }
201  }
202  }
203 
204  private:
205  std::chrono::steady_clock::time_point m_startIdleTime;
206 
207  std::function<void(void)> m_idleCallback;
208  std::vector<callbackValue_t> m_registeredCallbackFunctions;
209 
210  std::mutex m_registeredCallbackFunctionsMutex;
211 
212  std::mutex m_mutex; // Mutex for updateIdle call
213  };
214 } // namespace dds
215 
216 #endif /* defined(__DDS__MonitoringThread__) */
void start(double _idleTime, const std::function< void(void)> &_idleCallback)
Main function user has to run to start monitoring thread.
Definition: MonitoringThread.h:48
Definition: def.h:148
static CMonitoringThread & instance()
Return singleton instance.
Definition: MonitoringThread.h:38
Definition: MonitoringThread.h:22
bool IsProcessRunning(pid_t _PID)
The function checks, whether the process which corresponds to the given _PID can be found.
Definition: Process.h:51
#define LOG(severity)
Definition: Logger.h:34
Definition: def.h:151
Miscellaneous functions and helpers are located here.
Definition: AgentConnectionManager.h:13
Definition: def.h:147
void registerCallbackFunction(callbackFunction_t _handler, const std::chrono::seconds &_interval)
Definition: MonitoringThread.h:164
void updateIdle()
Definition: MonitoringThread.h:158
Definition: def.h:146
Definition: def.h:149