355d23d551561cb0dd236fa7b508597cad1556b3
[goodguy/history.git] / cinelerra-5.1 / guicast / thread.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22 #ifndef NO_GUICAST
23 #include "bcsignals.h"
24 #endif
25 #include <sys/wait.h>
26 #include <sched.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include "thread.h"
32
33 // track unjoined threads at termination
34 #ifndef NO_GUICAST
35 #include "arraylist.h"
36 #include "mutex.h"
37 #include <typeinfo>
38
39 class MLocker {
40         static Mutex the_lock;
41 public:
42         MLocker() { the_lock.lock(); }
43         ~MLocker() { the_lock.unlock(); }
44 };
45 Mutex MLocker::the_lock;
46
47 class the_dbg {
48 public:
49         pthread_t tid, owner;  const char *name;
50         the_dbg(pthread_t t, pthread_t o, const char *nm) { tid = t; owner = o; name = nm; }
51         ~the_dbg() {}
52 };
53
54
55 static class the_list : public ArrayList<the_dbg*> {
56 public:
57         static void dump_threads(FILE *fp);
58          the_list() {}
59         ~the_list() {
60                 MLocker mlkr;
61                 remove_all_objects();
62         }
63 } thread_list;
64
65 static void dbg_add(pthread_t tid, pthread_t owner, const char *nm)
66 {
67         MLocker mlkr;
68         int i = thread_list.size();
69         while( --i >= 0 && thread_list[i]->tid != tid );
70         if( i >= 0 ) {
71                 printf("dbg_add, dup %016lx %s %s\n",
72                         (unsigned long)tid, nm, thread_list[i]->name);
73                 return;
74         }
75         thread_list.append(new the_dbg(tid, owner, nm));
76 }
77
78 static void dbg_del(pthread_t tid)
79 {
80         MLocker mlkr;
81         int i = thread_list.size();
82         while( --i >= 0 && thread_list[i]->tid != tid );
83         if( i < 0 ) {
84                 printf("dbg_del, mis %016lx\n",(unsigned long)tid);
85                 return;
86         }
87         thread_list.remove_object_number(i);
88 }
89
90 static class the_chkr {
91 public:
92         the_chkr() {}
93         ~the_chkr() {
94                 int i = thread_list.size();
95                 if( !i ) return;
96                 printf("unjoined tids / owner %d\n", i);
97                 while( --i >= 0 ) printf("  %016lx / %016lx %s\n",
98                         (unsigned long)thread_list[i]->tid,
99                         (unsigned long)thread_list[i]->owner,
100                         thread_list[i]->name);
101         }
102 } the_chk;
103 #else
104 #define dbg_add(t, nm) do {} while(0)
105 #define dbg_del(t) do {} while(0)
106 #endif
107
108 Thread::Thread(int synchronous, int realtime, int autodelete)
109 {
110         this->synchronous = synchronous != 0;
111         this->realtime = realtime != 0;
112         this->autodelete = autodelete != 0;
113         tid = (pthread_t)-1;
114         finished = false;
115         cancelled = false;
116         cancel_enabled = false;
117 }
118
119 Thread::~Thread()
120 {
121 }
122
123 void* Thread::entrypoint(void *parameters)
124 {
125         Thread *thread = (Thread*)parameters;
126
127 // allow thread to be cancelled at any point during a region where it is enabled.
128         pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
129 // Disable cancellation by default.
130         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
131         thread->cancel_enabled = false;
132
133 // Set realtime here seince it doesn't work in start
134         if( thread->realtime && getuid() == 0 ) {
135                 struct sched_param param = { sched_priority : 1 };
136                 if(pthread_setschedparam(thread->tid, SCHED_RR, &param) < 0)
137                         perror("Thread::entrypoint pthread_attr_setschedpolicy");
138         }
139
140         thread->run();
141         thread->finished = true;
142         if( !thread->synchronous ) {
143                 if( !thread->cancelled ) dbg_del(thread->tid);
144                 if( thread->autodelete ) delete thread;
145                 else thread->tid = ((pthread_t)-1);
146         }
147         return NULL;
148 }
149
150 void Thread::start()
151 {
152         pthread_attr_t  attr;
153         struct sched_param param;
154
155         pthread_attr_init(&attr);
156
157 // previously run, and did not join, join to clean up zombie
158         if( synchronous && exists() )
159                 join();
160
161         finished = false;
162         cancelled = false;
163         owner = get_self();
164
165 // Inherit realtime from current thread the easy way.
166         if( !realtime )
167                 realtime = calculate_realtime();
168
169         if( !synchronous )
170                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
171
172         if( realtime && getuid() == 0 ) {
173                 if(pthread_attr_setschedpolicy(&attr, SCHED_RR) < 0)
174                         perror("Thread::start pthread_attr_setschedpolicy");
175                 param.sched_priority = 50;
176                 if(pthread_attr_setschedparam(&attr, &param) < 0)
177                         perror("Thread::start pthread_attr_setschedparam");
178         }
179         else {
180                 if(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED) < 0)
181                         perror("Thread::start pthread_attr_setinheritsched");
182         }
183
184         pthread_create(&tid, &attr, Thread::entrypoint, this);
185
186         dbg_add(tid, owner, typeid(*this).name());
187 }
188
189 int Thread::cancel()
190 {
191         if( exists() && !cancelled ) {
192                 LOCK_LOCKS("Thread::cancel");
193                 pthread_cancel(tid);
194                 cancelled = true;
195                 if( !synchronous ) dbg_del(tid);
196                 UNLOCK_LOCKS;
197         }
198         return 0;
199 }
200
201 int Thread::join()   // join this thread
202 {
203         if( !exists() ) return 0;
204         if( synchronous ) {
205 // NOTE: this does not do anything if the thread is not synchronous
206                 int ret = pthread_join(tid, 0);
207                 if( ret ) strerror(ret);
208                 CLEAR_LOCKS_TID(tid);
209                 dbg_del(tid);
210                 tid = ((pthread_t)-1);
211 // Don't execute anything after this.
212                 if( autodelete ) delete this;
213         }
214         else {
215 // kludge
216                 while( running() && !cancelled ) {
217                         int ret = pthread_kill(tid, 0);
218                         if( ret ) break;
219                         usleep(200000);
220                 }
221                 tid = ((pthread_t)-1);
222         }
223         return 0;
224 }
225
226 int Thread::enable_cancel()
227 {
228         if( !cancel_enabled ) {
229                 cancel_enabled = true;
230                 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
231         }
232         return 0;
233 }
234
235 int Thread::disable_cancel()
236 {
237         if( cancel_enabled ) {
238                 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
239                 cancel_enabled = false;
240         }
241         return 0;
242 }
243
244 int Thread::get_cancel_enabled()
245 {
246         return cancel_enabled;
247 }
248
249 void Thread::exit_thread()
250 {
251         finished = true;
252         pthread_exit(0);
253 }
254
255
256 int Thread::suspend_thread()
257 {
258         if( exists() )
259                 pthread_kill(tid, SIGSTOP);
260         return 0;
261 }
262
263 int Thread::continue_thread()
264 {
265         if( exists() )
266                 pthread_kill(tid, SIGCONT);
267         return 0;
268 }
269
270 int Thread::set_synchronous(int value)
271 {
272         this->synchronous = value != 0;
273         return 0;
274 }
275
276 int Thread::set_realtime(int value)
277 {
278         this->realtime = value != 0;
279         return 0;
280 }
281
282 int Thread::set_autodelete(int value)
283 {
284         this->autodelete = value != 0;
285         return 0;
286 }
287
288 int Thread::get_autodelete()
289 {
290         return autodelete ? 1 : 0;
291 }
292
293 int Thread::get_synchronous()
294 {
295         return synchronous ? 1 : 0;
296 }
297
298 bool Thread::calculate_realtime()
299 {
300 //printf("Thread::calculate_realtime %d %d\n", getpid(), sched_getscheduler(0));
301         return (sched_getscheduler(0) == SCHED_RR ||
302                 sched_getscheduler(0) == SCHED_FIFO);
303 }
304
305 int Thread::get_realtime()
306 {
307         return realtime ? 1 : 0;
308 }
309
310 unsigned long Thread::get_tid()
311 {
312         return tid;
313 }
314
315 void Thread::dump_threads(FILE *fp)
316 {
317         int i = thread_list.size();
318         while( --i >= 0 ) {
319                 fprintf(fp, "thread 0x%012lx, owner 0x%012lx, %s\n",
320                         (unsigned long)thread_list[i]->tid, (unsigned long)thread_list[i]->owner,
321                         thread_list[i]->name);
322         }
323 }
324