root/trunk/libffado/src/libutil/Watchdog.cpp

Revision 1027, 8.6 kB (checked in by ppalmers, 16 years ago)

simplify system time source class. should give a performance increase due to less function calls.

Line 
1 /*
2  * Copyright (C) 2005-2008 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 2 of the License, or
12  * (at your option) version 3 of the License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "Watchdog.h"
25 #include "SystemTimeSource.h"
26 #include "PosixThread.h"
27
28 #include "config.h"
29
30 // needed for clock_nanosleep
31 #ifndef _GNU_SOURCE
32     #define _GNU_SOURCE
33 #endif
34
35 #include <time.h>
36
37 namespace Util {
38
39 IMPL_DEBUG_MODULE( Watchdog, Watchdog, DEBUG_LEVEL_NORMAL );
40
41 // --- liveness check Thread --- //
42 Watchdog::WatchdogCheckTask::WatchdogCheckTask(Watchdog& parent, unsigned int interval_usecs)
43     : m_parent( parent )
44     , m_interval( interval_usecs )
45     , m_debugModule( parent.m_debugModule )
46 {
47 }
48
49 bool
50 Watchdog::WatchdogCheckTask::Init()
51 {
52     #ifdef DEBUG
53     m_last_loop_entry = 0;
54     m_successive_short_loops = 0;
55     #endif
56     return true;
57 }
58
59 bool
60 Watchdog::WatchdogCheckTask::Execute()
61 {
62     SystemTimeSource::SleepUsecRelative(m_interval);
63     if(m_parent.getHartbeat()) {
64         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
65                     "(%p) watchdog %p still alive\n", this, &m_parent);
66         m_parent.clearHartbeat();
67     } else {
68         debugWarning("(%p) watchdog %p died\n", this, &m_parent);
69         // set all watched threads to non-rt scheduling
70         m_parent.rescheduleThreads();
71     }
72
73     #ifdef DEBUG
74     uint64_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs();
75     int diff = now - m_last_loop_entry;
76     if(diff < 100) {
77         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
78                            "(%p) short loop detected (%d usec), cnt: %d\n",
79                            this, diff, m_successive_short_loops);
80         m_successive_short_loops++;
81         if(m_successive_short_loops > 100) {
82             debugError("Shutting down runaway thread\n");
83             return false;
84         }
85     } else {
86         // reset the counter
87         m_successive_short_loops = 0;
88     }
89     m_last_loop_entry = now;
90     #endif
91
92     return true;
93 }
94
95 // --- hartbeat Thread --- //
96
97 Watchdog::WatchdogHartbeatTask::WatchdogHartbeatTask(Watchdog& parent, unsigned int interval_usecs)
98     : m_parent( parent )
99     , m_interval( interval_usecs )
100     , m_debugModule( parent.m_debugModule )
101 {
102 }
103
104 bool
105 Watchdog::WatchdogHartbeatTask::Init()
106 {
107     #ifdef DEBUG
108     m_last_loop_entry = 0;
109     m_successive_short_loops = 0;
110     #endif
111     return true;
112 }
113
114 bool
115 Watchdog::WatchdogHartbeatTask::Execute()
116 {
117     SystemTimeSource::SleepUsecRelative(m_interval);
118     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
119                 "(%p) watchdog %p hartbeat\n", this, &m_parent);
120     m_parent.setHartbeat();
121
122     #ifdef DEBUG
123     uint64_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs();
124     int diff = now - m_last_loop_entry;
125     if(diff < 100) {
126         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
127                            "(%p) short loop detected (%d usec), cnt: %d\n",
128                            this, diff, m_successive_short_loops);
129         m_successive_short_loops++;
130         if(m_successive_short_loops > 100) {
131             debugError("Shutting down runaway thread\n");
132             return false;
133         }
134     } else {
135         // reset the counter
136         m_successive_short_loops = 0;
137     }
138     m_last_loop_entry = now;
139     #endif
140
141     return true;
142 }
143
144 // the actual watchdog class
145 Watchdog::Watchdog()
146 : m_hartbeat( true )
147 , m_check_interval( WATCHDOG_DEFAULT_CHECK_INTERVAL_USECS )
148 , m_realtime( WATCHDOG_DEFAULT_RUN_REALTIME )
149 , m_priority( WATCHDOG_DEFAULT_PRIORITY )
150 , m_CheckThread( NULL )
151 , m_HartbeatThread( NULL )
152 , m_CheckTask( NULL )
153 , m_HartbeatTask( NULL )
154 {
155 }
156
157 Watchdog::Watchdog(unsigned int interval_usec, bool realtime, unsigned int priority)
158 : m_hartbeat( true )
159 , m_check_interval( interval_usec )
160 , m_realtime( realtime )
161 , m_priority( priority )
162 , m_CheckThread( NULL )
163 , m_HartbeatThread( NULL )
164 , m_CheckTask( NULL )
165 , m_HartbeatTask( NULL )
166 {
167 }
168
169 Watchdog::~Watchdog()
170 {
171     if (m_CheckThread) {
172         //m_CheckThread->Stop();
173         m_CheckThread->Kill();
174         delete m_CheckThread;
175     }
176     if (m_HartbeatThread) {
177         //m_HartbeatThread->Stop();
178         m_HartbeatThread->Kill();
179         delete m_HartbeatThread;
180     }
181     if (m_CheckTask) {
182         delete m_CheckTask;
183     }
184     if (m_HartbeatTask) {
185         delete m_HartbeatTask;
186     }
187 }
188
189 void
190 Watchdog::setVerboseLevel(int i)
191 {
192     setDebugLevel(i);
193 }
194
195 bool
196 Watchdog::start()
197 {
198     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Starting watchdog...\n", this);
199     debugOutput( DEBUG_LEVEL_VERBOSE, "Create hartbeat task/thread for %p...\n", this);
200     m_HartbeatTask = new WatchdogHartbeatTask( *this, m_check_interval/2 );
201     if(!m_HartbeatTask) {
202         debugFatal("No hartbeat task\n");
203         return false;
204     }
205     m_HartbeatThread = new Util::PosixThread(m_HartbeatTask, false,
206                                              0, PTHREAD_CANCEL_ASYNCHRONOUS);
207     if(!m_HartbeatThread) {
208         debugFatal("No hartbeat thread\n");
209         return false;
210     }
211     debugOutput( DEBUG_LEVEL_VERBOSE,
212                  " hartbeat task: %p, thread %p...\n",
213                  m_HartbeatTask, m_HartbeatThread);
214
215     debugOutput( DEBUG_LEVEL_VERBOSE, "Create check task/thread for %p...\n", this);
216     m_CheckTask = new WatchdogCheckTask( *this, m_check_interval );
217     if(!m_CheckTask) {
218         debugFatal("No check task\n");
219         return false;
220     }
221     m_CheckThread = new Util::PosixThread(m_CheckTask, false,
222                                           0, PTHREAD_CANCEL_ASYNCHRONOUS);
223     if(!m_CheckThread) {
224         debugFatal("No check thread\n");
225         return false;
226     }
227     debugOutput( DEBUG_LEVEL_VERBOSE,
228                  " check task: %p, thread %p...\n",
229                  m_CheckTask, m_CheckThread);
230
231     // switch to realtime if necessary
232     if(m_realtime) {
233         if(!m_CheckThread->AcquireRealTime(m_priority)) {
234             debugWarning("(%p) Could not aquire realtime priotiry for watchdog thread.\n", this);
235         }
236     }
237
238     // start threads
239     if (m_HartbeatThread->Start() != 0) {
240         debugFatal("Could not start hartbeat thread\n");
241         return false;
242     }
243     if (m_CheckThread->Start() != 0) {
244         debugFatal("Could not start check thread\n");
245         return false;
246     }
247     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Watchdog running...\n", this);
248     return true;
249 }
250
251 bool
252 Watchdog::setThreadParameters(bool rt, int priority)
253 {
254     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority);
255     if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority
256     m_realtime = rt;
257     m_priority = priority;
258
259     if (m_CheckThread) {
260         if (m_realtime) {
261             m_CheckThread->AcquireRealTime(m_priority);
262         } else {
263             m_CheckThread->DropRealTime();
264         }
265     }
266     return true;
267 }
268
269 /**
270  * register a thread to the watchdog
271  * @param thread
272  * @return
273  */
274 bool
275 Watchdog::registerThread(Thread *thread)
276 {
277     assert(thread);
278     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Adding thread %p\n",
279         this, thread);
280
281     for ( ThreadVectorIterator it = m_Threads.begin();
282       it != m_Threads.end();
283       ++it )
284     {
285         if(*it == thread) {
286             debugError("Thread %p already registered with watchdog\n", thread);
287             return false;
288         }
289     }
290     m_Threads.push_back(thread);
291     return true;
292 }
293
294 bool
295 Watchdog::unregisterThread(Thread *thread)
296 {
297     assert(thread);
298     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) unregistering thread %p\n", this, thread);
299
300     for ( ThreadVectorIterator it = m_Threads.begin();
301       it != m_Threads.end();
302       ++it )
303     {
304         if(*it == thread) {
305             m_Threads.erase(it);
306             return true;
307         }
308     }
309     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) thread %p not found \n", this, thread);
310     return false; //not found
311 }
312
313 void Watchdog::rescheduleThreads()
314 {
315     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) rescheduling threads\n", this);
316
317     for ( ThreadVectorIterator it = m_Threads.begin();
318       it != m_Threads.end();
319       ++it )
320     {
321         (*it)->DropRealTime();
322     }
323 }
324
325 } // end of namespace Util
Note: See TracBrowser for help on using the browser.