use static functions where possible for some local functions.
[blender.git] / source / blender / blenlib / intern / threads.c
1 /**
2  *
3  * $Id$
4  *
5  * ***** BEGIN GPL LICENSE BLOCK *****
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version. 
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * The Original Code is Copyright (C) 2006 Blender Foundation
22  * All rights reserved.
23  *
24  * The Original Code is: all of this file.
25  *
26  * Contributor(s): none yet.
27  *
28  * ***** END GPL LICENSE BLOCK *****
29  */
30
31 #include <math.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <pthread.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "DNA_listBase.h"
39
40 #include "BLI_blenlib.h"
41 #include "BLI_threads.h"
42
43 #include "PIL_time.h"
44
45 /* for checking system threads - BLI_system_thread_count */
46 #ifdef WIN32
47 #include "windows.h"
48 #elif defined(__APPLE__)
49 #include <sys/types.h>
50 #include <sys/sysctl.h>
51 #else
52 #include <unistd.h> 
53 #endif
54
55 /* ********** basic thread control API ************ 
56
57 Many thread cases have an X amount of jobs, and only an Y amount of
58 threads are useful (typically amount of cpus)
59
60 This code can be used to start a maximum amount of 'thread slots', which
61 then can be filled in a loop with an idle timer. 
62
63 A sample loop can look like this (pseudo c);
64
65         ListBase lb;
66         int maxthreads= 2;
67         int cont= 1;
68
69         BLI_init_threads(&lb, do_something_func, maxthreads);
70
71         while(cont) {
72                 if(BLI_available_threads(&lb) && !(escape loop event)) {
73                         // get new job (data pointer)
74                         // tag job 'processed 
75                         BLI_insert_thread(&lb, job);
76                 }
77                 else PIL_sleep_ms(50);
78                 
79                 // find if a job is ready, this the do_something_func() should write in job somewhere
80                 cont= 0;
81                 for(go over all jobs)
82                         if(job is ready) {
83                                 if(job was not removed) {
84                                         BLI_remove_thread(&lb, job);
85                                 }
86                         }
87                         else cont= 1;
88                 }
89                 // conditions to exit loop 
90                 if(if escape loop event) {
91                         if(BLI_available_threadslots(&lb)==maxthreads)
92                                 break;
93                 }
94         }
95
96         BLI_end_threads(&lb);
97
98  ************************************************ */
99 static pthread_mutex_t _malloc_lock = PTHREAD_MUTEX_INITIALIZER;
100 static pthread_mutex_t _image_lock = PTHREAD_MUTEX_INITIALIZER;
101 static pthread_mutex_t _custom1_lock = PTHREAD_MUTEX_INITIALIZER;
102 static int thread_levels= 0;    /* threads can be invoked inside threads */
103
104 /* just a max for security reasons */
105 #define RE_MAX_THREAD   8
106
107 typedef struct ThreadSlot {
108         struct ThreadSlot *next, *prev;
109         void *(*do_thread)(void *);
110         void *callerdata;
111         pthread_t pthread;
112         int avail;
113 } ThreadSlot;
114
115 void BLI_lock_malloc_thread(void)
116 {
117         pthread_mutex_lock(&_malloc_lock);
118 }
119
120 void BLI_unlock_malloc_thread(void)
121 {
122         pthread_mutex_unlock(&_malloc_lock);
123 }
124
125 /* tot = 0 only initializes malloc mutex in a safe way (see sequence.c)
126    problem otherwise: scene render will kill of the mutex!
127 */
128
129 void BLI_init_threads(ListBase *threadbase, void *(*do_thread)(void *), int tot)
130 {
131         int a;
132         
133         if(threadbase != NULL && tot > 0) {
134                 threadbase->first= threadbase->last= NULL;
135         
136                 if(tot>RE_MAX_THREAD) tot= RE_MAX_THREAD;
137                 else if(tot<1) tot= 1;
138         
139                 for(a=0; a<tot; a++) {
140                         ThreadSlot *tslot= MEM_callocN(sizeof(ThreadSlot), "threadslot");
141                         BLI_addtail(threadbase, tslot);
142                         tslot->do_thread= do_thread;
143                         tslot->avail= 1;
144                 }
145                 
146                 MEM_set_lock_callback(BLI_lock_malloc_thread, BLI_unlock_malloc_thread);
147                 thread_levels++;
148         }
149 }
150
151 /* amount of available threads */
152 int BLI_available_threads(ListBase *threadbase)
153 {
154         ThreadSlot *tslot;
155         int counter=0;
156         
157         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
158                 if(tslot->avail)
159                         counter++;
160         }
161         return counter;
162 }
163
164 /* returns thread number, for sample patterns or threadsafe tables */
165 int BLI_available_thread_index(ListBase *threadbase)
166 {
167         ThreadSlot *tslot;
168         int counter=0;
169         
170         for(tslot= threadbase->first; tslot; tslot= tslot->next, counter++) {
171                 if(tslot->avail)
172                         return counter;
173         }
174         return 0;
175 }
176
177
178 void BLI_insert_thread(ListBase *threadbase, void *callerdata)
179 {
180         ThreadSlot *tslot;
181         
182         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
183                 if(tslot->avail) {
184                         tslot->avail= 0;
185                         tslot->callerdata= callerdata;
186                         pthread_create(&tslot->pthread, NULL, tslot->do_thread, tslot->callerdata);
187                         return;
188                 }
189         }
190         printf("ERROR: could not insert thread slot\n");
191 }
192
193 void BLI_remove_thread(ListBase *threadbase, void *callerdata)
194 {
195         ThreadSlot *tslot;
196         
197         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
198                 if(tslot->callerdata==callerdata) {
199                         tslot->callerdata= NULL;
200                         pthread_join(tslot->pthread, NULL);
201                         tslot->avail= 1;
202                 }
203         }
204 }
205
206 void BLI_remove_thread_index(ListBase *threadbase, int index)
207 {
208         ThreadSlot *tslot;
209         int counter=0;
210         
211         for(tslot = threadbase->first; tslot; tslot = tslot->next, counter++) {
212                 if (counter == index && tslot->avail == 0) {
213                         tslot->callerdata = NULL;
214                         pthread_join(tslot->pthread, NULL);
215                         tslot->avail = 1;
216                         break;
217                 }
218         }
219 }
220
221 void BLI_remove_threads(ListBase *threadbase)
222 {
223         ThreadSlot *tslot;
224         
225         for(tslot = threadbase->first; tslot; tslot = tslot->next) {
226                 if (tslot->avail == 0) {
227                         tslot->callerdata = NULL;
228                         pthread_join(tslot->pthread, NULL);
229                         tslot->avail = 1;
230                 }
231         }
232 }
233
234 void BLI_end_threads(ListBase *threadbase)
235 {
236         ThreadSlot *tslot;
237         
238         /* only needed if there's actually some stuff to end
239          * this way we don't end up decrementing thread_levels on an empty threadbase 
240          * */
241         if (threadbase && threadbase->first != NULL) {
242                 for(tslot= threadbase->first; tslot; tslot= tslot->next) {
243                         if(tslot->avail==0) {
244                                 pthread_join(tslot->pthread, NULL);
245                         }
246                 }
247                 BLI_freelistN(threadbase);
248
249                 thread_levels--;
250                 if(thread_levels==0)
251                         MEM_set_lock_callback(NULL, NULL);
252         }
253 }
254
255 void BLI_lock_thread(int type)
256 {
257         if (type==LOCK_IMAGE)
258                 pthread_mutex_lock(&_image_lock);
259         else if (type==LOCK_CUSTOM1)
260                 pthread_mutex_lock(&_custom1_lock);
261 }
262
263 void BLI_unlock_thread(int type)
264 {
265         if (type==LOCK_IMAGE)
266                 pthread_mutex_unlock(&_image_lock);
267         else if(type==LOCK_CUSTOM1)
268                 pthread_mutex_unlock(&_custom1_lock);
269 }
270
271 /* how many threads are native on this system? */
272 int BLI_system_thread_count( void )
273 {
274         int t;
275 #ifdef WIN32
276         SYSTEM_INFO info;
277         GetSystemInfo(&info);
278         t = (int) info.dwNumberOfProcessors;
279 #else 
280 #       ifdef __APPLE__
281         int mib[2];
282         size_t len;
283         
284         mib[0] = CTL_HW;
285         mib[1] = HW_NCPU;
286         len = sizeof(t);
287         sysctl(mib, 2, &t, &len, NULL, 0);
288 #       elif defined(__sgi)
289         t = sysconf(_SC_NPROC_ONLN);
290 #       else
291         t = (int)sysconf(_SC_NPROCESSORS_ONLN);
292 #       endif
293 #endif
294         
295         if (t>RE_MAX_THREAD)
296                 return RE_MAX_THREAD;
297         if (t<1)
298                 return 1;
299         
300         return t;
301 }
302
303 /* ************************************************ */
304
305 typedef struct ThreadedWorker {
306         ListBase threadbase;
307         void *(*work_fnct)(void *);
308         char     busy[RE_MAX_THREAD];
309         int              total;
310         int              sleep_time;
311 } ThreadedWorker;
312
313 typedef struct WorkParam {
314         ThreadedWorker *worker;
315         void *param;
316         int       index;
317 } WorkParam;
318
319 static void *exec_work_fnct(void *v_param)
320 {
321         WorkParam *p = (WorkParam*)v_param;
322         void *value;
323         
324         value = p->worker->work_fnct(p->param);
325         
326         p->worker->busy[p->index] = 0;
327         MEM_freeN(p);
328         
329         return value;
330 }
331
332 ThreadedWorker *BLI_create_worker(void *(*do_thread)(void *), int tot, int sleep_time)
333 {
334         ThreadedWorker *worker;
335         
336         worker = MEM_callocN(sizeof(ThreadedWorker), "threadedworker");
337         
338         if (tot > RE_MAX_THREAD)
339         {
340                 tot = RE_MAX_THREAD;
341         }
342         else if (tot < 1)
343         {
344                 tot= 1;
345         }
346         
347         worker->total = tot;
348         worker->work_fnct = do_thread;
349         
350         BLI_init_threads(&worker->threadbase, exec_work_fnct, tot);
351         
352         return worker;
353 }
354
355 void BLI_end_worker(ThreadedWorker *worker)
356 {
357         BLI_remove_threads(&worker->threadbase);
358 }
359
360 void BLI_destroy_worker(ThreadedWorker *worker)
361 {
362         BLI_end_worker(worker);
363         BLI_freelistN(&worker->threadbase);
364         MEM_freeN(worker);
365 }
366
367 void BLI_insert_work(ThreadedWorker *worker, void *param)
368 {
369         WorkParam *p = MEM_callocN(sizeof(WorkParam), "workparam");
370         int index;
371         
372         if (BLI_available_threads(&worker->threadbase) == 0)
373         {
374                 index = worker->total;
375                 while(index == worker->total)
376                 {
377                         PIL_sleep_ms(worker->sleep_time);
378                         
379                         for (index = 0; index < worker->total; index++)
380                         {
381                                 if (worker->busy[index] == 0)
382                                 {
383                                         BLI_remove_thread_index(&worker->threadbase, index);
384                                         break;
385                                 }
386                         }
387                 }
388         }
389         else
390         {
391                 index = BLI_available_thread_index(&worker->threadbase);
392         }
393         
394         worker->busy[index] = 1;
395         
396         p->param = param;
397         p->index = index;
398         p->worker = worker;
399         
400         BLI_insert_thread(&worker->threadbase, p);
401 }
402
403 /* eof */