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

Revision 2164, 9.0 kB (checked in by jwoithe, 8 years ago)

A more robust solution to ticket #354. Rather than relying on an ad hoc delay, use a condition variable to ensure that PosixThread::ThreadHandler?() acquires m_lock before anything else. It is particularly important that PosixThread::Kill() and PosixThread::Stop() don't lock m_lock before PosixThread::ThreadHandler?(); otherwise a deadlock will result.

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 /*
25  * Copied from the jackd/jackdmp sources
26  * function names changed in order to avoid naming problems when using this in
27  * a jackd backend.
28  */
29
30 /* Original license:
31  * Copyright (C) 2001 Paul Davis
32  * Copyright (C) 2004-2006 Grame
33  *
34  * this program is free software; you can redistribute it and/or modify
35  * it under the terms of the GNU General Public License as published by
36  * the Free Software Foundation; either version 2 of the License, or
37  * (at your option) version 3 of the License.
38  *
39  * This program is distributed in the hope that it will be useful,
40  * but WITHOUT ANY WARRANTY; without even the implied warranty of
41  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42  * GNU General Public License for more details.
43  *
44  * You should have received a copy of the GNU General Public License
45  * along with this program; if not, write to the Free Software
46  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
47  *
48  */
49
50 #include "PosixThread.h"
51 #include <string.h> // for memset
52 #include <errno.h>
53 #include <assert.h>
54 #include <sys/prctl.h>
55
56 namespace Util
57 {
58
59 IMPL_DEBUG_MODULE( Thread, Thread, DEBUG_LEVEL_NORMAL );
60
61 void* PosixThread::ThreadHandler(void* arg)
62 {
63     PosixThread* obj = (PosixThread*)arg;
64     RunnableInterface* runnable = obj->fRunnable;
65     int err;
66
67     obj->m_lock.Lock();
68
69     // Signal that ThreadHandler has acquired its initial lock
70     pthread_mutex_lock(&obj->handler_active_lock);
71     obj->handler_active = 1;
72     pthread_cond_signal(&obj->handler_active_cond);
73     pthread_mutex_unlock(&obj->handler_active_lock);
74
75     if ((err = pthread_setcanceltype(obj->fCancellation, NULL)) != 0) {
76         debugError("pthread_setcanceltype err = %s\n", strerror(err));
77     }
78
79     // Call Init method
80     if (!runnable->Init()) {
81         debugError("Thread init fails: thread quits\n");
82         obj->m_lock.Unlock();
83         return 0;
84     }
85
86     std::string threadname = std::string("FW_") + obj->m_id;
87     prctl(PR_SET_NAME, threadname.c_str());
88
89     debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) ThreadHandler: start %p\n", obj->m_id.c_str(), obj);
90
91     // If Init succeed start the thread loop
92     bool res = true;
93
94     obj->m_lock.Unlock();
95     while (obj->fRunning && res) {
96         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "(%s) ThreadHandler: run %p\n", obj->m_id.c_str(), obj);
97         res = runnable->Execute();
98         pthread_testcancel();
99     }
100
101     debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) ThreadHandler: exit %p\n", obj->m_id.c_str(), obj);
102     return 0;
103 }
104
105 int PosixThread::Start()
106 {
107     int res;
108     fRunning = true;
109
110     if (fRealTime) {
111
112         debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Create RT thread %p with priority %d\n", m_id.c_str(), this, fPriority);
113
114         /* Get the client thread to run as an RT-FIFO
115            scheduled thread of appropriate priority.
116         */
117         pthread_attr_t attributes;
118         struct sched_param rt_param;
119         pthread_attr_init(&attributes);
120
121         if ((res = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED))) {
122             debugError("Cannot request explicit scheduling for RT thread  %d %s\n", res, strerror(res));
123             return -1;
124         }
125         if ((res = pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_JOINABLE))) {
126             debugError("Cannot request joinable thread creation for RT thread  %d %s\n", res, strerror(res));
127             return -1;
128         }
129         if ((res = pthread_attr_setscope(&attributes, PTHREAD_SCOPE_SYSTEM))) {
130             debugError("Cannot set scheduling scope for RT thread %d %s\n", res, strerror(res));
131             return -1;
132         }
133
134         if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_FIFO))) {
135
136         //if ((res = pthread_attr_setschedpolicy(&attributes, SCHED_RR))) {
137             debugError("Cannot set FIFO scheduling class for RT thread  %d %s\n", res, strerror(res));
138             return -1;
139         }
140
141         memset(&rt_param, 0, sizeof(rt_param));
142         if(fPriority <= 0) {
143             debugWarning("Clipping to minimum priority (%d -> 1)\n", fPriority);
144             rt_param.sched_priority = 1;
145         } else if(fPriority >= 99) {
146             debugWarning("Clipping to maximum priority (%d -> 98)\n", fPriority);
147             rt_param.sched_priority = 98;
148         } else {
149             rt_param.sched_priority = fPriority;
150         }
151
152         if ((res = pthread_attr_setschedparam(&attributes, &rt_param))) {
153             debugError("Cannot set scheduling priority for RT thread %d %s\n", res, strerror(res));
154             return -1;
155         }
156
157         m_lock.Lock();
158         res = pthread_create(&fThread, &attributes, ThreadHandler, this);
159         m_lock.Unlock();
160         if (res) {
161             debugError("Cannot create realtime thread (%d: %s)\n", res, strerror(res));
162             debugError(" priority: %d\n", fPriority);
163             return -1;
164         }
165     } else {
166         debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Create non RT thread %p\n", m_id.c_str(), this);
167
168         m_lock.Lock();
169         res = pthread_create(&fThread, 0, ThreadHandler, this);
170         m_lock.Unlock();
171         if (res) {
172             debugError("Cannot create thread %d %s\n", res, strerror(res));
173             return -1;
174         }
175     }
176
177     // Wait for ThreadHandler() to acquire the thread lock (m_lock) before
178     // continuing, thereby ensuring that ThreadHandler() acquires a lock on
179     // m_lock before anything else tries.
180     pthread_mutex_lock(&handler_active_lock);
181     while (handler_active == 0)
182         pthread_cond_wait(&handler_active_cond, &handler_active_lock);
183     pthread_mutex_unlock(&handler_active_lock);
184
185     return 0;
186 }
187
188 int PosixThread::Kill()
189 {
190     if (fThread) { // If thread has been started
191         debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Kill %p (thread: %p)\n", m_id.c_str(), this, (void *)fThread);
192         void* status;
193         pthread_cancel(fThread);
194         m_lock.Lock();
195         pthread_join(fThread, &status);
196         m_lock.Unlock();
197         debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Killed %p (thread: %p)\n", m_id.c_str(), this, (void *)fThread);
198         return 0;
199     } else {
200         return -1;
201     }
202 }
203
204 int PosixThread::Stop()
205 {
206     if (fThread) { // If thread has been started
207         debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Stop %p (thread: %p)\n", m_id.c_str(), this, (void *)fThread);
208         void* status;
209         fRunning = false; // Request for the thread to stop
210         m_lock.Lock();
211         pthread_join(fThread, &status);
212         fThread = 0;
213         m_lock.Unlock();
214         debugOutput( DEBUG_LEVEL_VERBOSE, "(%s) Stopped %p (thread: %p)\n", m_id.c_str(), this, (void *)fThread);
215         return 0;
216     } else {
217         return -1;
218     }
219 }
220
221 int PosixThread::AcquireRealTime()
222 {
223     struct sched_param rtparam;
224     int res;
225     debugOutput( DEBUG_LEVEL_VERBOSE, "(%s, %p) Aquire realtime, prio %d\n", m_id.c_str(), this, fPriority);
226
227     if (!fThread)
228         return -1;
229
230     memset(&rtparam, 0, sizeof(rtparam));
231     if(fPriority <= 0) {
232         debugWarning("Clipping to minimum priority (%d -> 1)\n", fPriority);
233         rtparam.sched_priority = 1;
234     } else if(fPriority >= 99) {
235         debugWarning("Clipping to maximum priority (%d -> 98)\n", fPriority);
236         rtparam.sched_priority = 98;
237     } else {
238         rtparam.sched_priority = fPriority;
239     }
240
241     //if ((res = pthread_setschedparam(fThread, SCHED_FIFO, &rtparam)) != 0) {
242
243     if ((res = pthread_setschedparam(fThread, SCHED_FIFO, &rtparam)) != 0) {
244         debugError("Cannot use real-time scheduling (FIFO/%d) "
245                    "(%d: %s)", rtparam.sched_priority, res,
246                    strerror(res));
247         return -1;
248     }
249     return 0;
250 }
251
252 int PosixThread::AcquireRealTime(int priority)
253 {
254     fPriority = priority;
255     return AcquireRealTime();
256 }
257
258 int PosixThread::DropRealTime()
259 {
260     struct sched_param rtparam;
261     int res;
262     debugOutput( DEBUG_LEVEL_VERBOSE, "(%s, %p) Drop realtime\n", m_id.c_str(), this);
263
264     if (!fThread)
265         return -1;
266
267     memset(&rtparam, 0, sizeof(rtparam));
268     rtparam.sched_priority = 0;
269
270     if ((res = pthread_setschedparam(fThread, SCHED_OTHER, &rtparam)) != 0) {
271         debugError("Cannot switch to normal scheduling priority(%s)\n", strerror(res));
272         return -1;
273     }
274     return 0;
275 }
276
277 pthread_t PosixThread::GetThreadID()
278 {
279     return fThread;
280 }
281
282 } // end of namespace
283
Note: See TracBrowser for help on using the browser.