merge runk 16887:16950
[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 "BLI_blenlib.h"
39 #include "BLI_threads.h"
40
41 #include "PIL_time.h"
42
43 /* for checking system threads - BLI_system_thread_count */
44 #ifdef WIN32
45 #include "Windows.h"
46 #elif defined(__APPLE__)
47 #include <sys/types.h>
48 #include <sys/sysctl.h>
49 #else
50 #include <unistd.h> 
51 #endif
52
53 /* ********** basic thread control API ************ 
54
55 Many thread cases have an X amount of jobs, and only an Y amount of
56 threads are useful (typically amount of cpus)
57
58 This code can be used to start a maximum amount of 'thread slots', which
59 then can be filled in a loop with an idle timer. 
60
61 A sample loop can look like this (pseudo c);
62
63         ListBase lb;
64         int maxthreads= 2;
65         int cont= 1;
66
67         BLI_init_threads(&lb, do_something_func, maxthreads);
68
69         while(cont) {
70                 if(BLI_available_threads(&lb) && !(escape loop event)) {
71                         // get new job (data pointer)
72                         // tag job 'processed 
73                         BLI_insert_thread(&lb, job);
74                 }
75                 else PIL_sleep_ms(50);
76                 
77                 // find if a job is ready, this the do_something_func() should write in job somewhere
78                 cont= 0;
79                 for(go over all jobs)
80                         if(job is ready) {
81                                 if(job was not removed) {
82                                         BLI_remove_thread(&lb, job);
83                                 }
84                         }
85                         else cont= 1;
86                 }
87                 // conditions to exit loop 
88                 if(if escape loop event) {
89                         if(BLI_available_threadslots(&lb)==maxthreads)
90                                 break;
91                 }
92         }
93
94         BLI_end_threads(&lb);
95
96  ************************************************ */
97 static pthread_mutex_t _malloc_lock = PTHREAD_MUTEX_INITIALIZER;
98 static pthread_mutex_t _image_lock = PTHREAD_MUTEX_INITIALIZER;
99 static pthread_mutex_t _custom1_lock = PTHREAD_MUTEX_INITIALIZER;
100 static int thread_levels= 0;    /* threads can be invoked inside threads */
101
102 /* just a max for security reasons */
103 #define RE_MAX_THREAD   8
104
105 typedef struct ThreadSlot {
106         struct ThreadSlot *next, *prev;
107         void *(*do_thread)(void *);
108         void *callerdata;
109         pthread_t pthread;
110         int avail;
111 } ThreadSlot;
112
113 static void BLI_lock_malloc_thread(void)
114 {
115         pthread_mutex_lock(&_malloc_lock);
116 }
117
118 static void BLI_unlock_malloc_thread(void)
119 {
120         pthread_mutex_unlock(&_malloc_lock);
121 }
122
123 /* tot = 0 only initializes malloc mutex in a safe way (see sequence.c)
124    problem otherwise: scene render will kill of the mutex!
125 */
126
127 void BLI_init_threads(ListBase *threadbase, void *(*do_thread)(void *), int tot)
128 {
129         int a;
130         
131         if(threadbase != NULL && tot > 0) {
132                 threadbase->first= threadbase->last= NULL;
133         
134                 if(tot>RE_MAX_THREAD) tot= RE_MAX_THREAD;
135                 else if(tot<1) tot= 1;
136         
137                 for(a=0; a<tot; a++) {
138                         ThreadSlot *tslot= MEM_callocN(sizeof(ThreadSlot), "threadslot");
139                         BLI_addtail(threadbase, tslot);
140                         tslot->do_thread= do_thread;
141                         tslot->avail= 1;
142                 }
143         }
144
145         MEM_set_lock_callback(BLI_lock_malloc_thread, BLI_unlock_malloc_thread);
146         thread_levels++;
147 }
148
149 /* amount of available threads */
150 int BLI_available_threads(ListBase *threadbase)
151 {
152         ThreadSlot *tslot;
153         int counter=0;
154         
155         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
156                 if(tslot->avail)
157                         counter++;
158         }
159         return counter;
160 }
161
162 /* returns thread number, for sample patterns or threadsafe tables */
163 int BLI_available_thread_index(ListBase *threadbase)
164 {
165         ThreadSlot *tslot;
166         int counter=0;
167         
168         for(tslot= threadbase->first; tslot; tslot= tslot->next, counter++) {
169                 if(tslot->avail)
170                         return counter;
171         }
172         return 0;
173 }
174
175
176 void BLI_insert_thread(ListBase *threadbase, void *callerdata)
177 {
178         ThreadSlot *tslot;
179         
180         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
181                 if(tslot->avail) {
182                         tslot->avail= 0;
183                         tslot->callerdata= callerdata;
184                         pthread_create(&tslot->pthread, NULL, tslot->do_thread, tslot->callerdata);
185                         return;
186                 }
187         }
188         printf("ERROR: could not insert thread slot\n");
189 }
190
191 void BLI_remove_thread(ListBase *threadbase, void *callerdata)
192 {
193         ThreadSlot *tslot;
194         
195         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
196                 if(tslot->callerdata==callerdata) {
197                         tslot->callerdata= NULL;
198                         pthread_join(tslot->pthread, NULL);
199                         tslot->avail= 1;
200                 }
201         }
202 }
203
204 void BLI_remove_thread_index(ListBase *threadbase, int index)
205 {
206         ThreadSlot *tslot;
207         int counter=0;
208         
209         for(tslot = threadbase->first; tslot; tslot = tslot->next, counter++) {
210                 if (counter == index && tslot->avail == 0) {
211                         tslot->callerdata = NULL;
212                         pthread_join(tslot->pthread, NULL);
213                         tslot->avail = 1;
214                         break;
215                 }
216         }
217 }
218
219 void BLI_remove_threads(ListBase *threadbase)
220 {
221         ThreadSlot *tslot;
222         
223         for(tslot = threadbase->first; tslot; tslot = tslot->next) {
224                 if (tslot->avail == 0) {
225                         tslot->callerdata = NULL;
226                         pthread_join(tslot->pthread, NULL);
227                         tslot->avail = 1;
228                 }
229         }
230 }
231
232 void BLI_end_threads(ListBase *threadbase)
233 {
234         ThreadSlot *tslot;
235         
236         if (threadbase) {
237                 for(tslot= threadbase->first; tslot; tslot= tslot->next) {
238                         if(tslot->avail==0) {
239                                 pthread_join(tslot->pthread, NULL);
240                         }
241                 }
242                 BLI_freelistN(threadbase);
243         }
244         
245         thread_levels--;
246         if(thread_levels==0)
247                 MEM_set_lock_callback(NULL, NULL);
248 }
249
250 void BLI_lock_thread(int type)
251 {
252         if (type==LOCK_IMAGE)
253                 pthread_mutex_lock(&_image_lock);
254         else if (type==LOCK_CUSTOM1)
255                 pthread_mutex_lock(&_custom1_lock);
256 }
257
258 void BLI_unlock_thread(int type)
259 {
260         if (type==LOCK_IMAGE)
261                 pthread_mutex_unlock(&_image_lock);
262         else if(type==LOCK_CUSTOM1)
263                 pthread_mutex_unlock(&_custom1_lock);
264 }
265
266 /* how many threads are native on this system? */
267 int BLI_system_thread_count( void )
268 {
269         int t;
270 #ifdef WIN32
271         SYSTEM_INFO info;
272         GetSystemInfo(&info);
273         t = (int) info.dwNumberOfProcessors;
274 #else 
275 #       ifdef __APPLE__
276         int mib[2];
277         size_t len;
278         
279         mib[0] = CTL_HW;
280         mib[1] = HW_NCPU;
281         len = sizeof(t);
282         sysctl(mib, 2, &t, &len, NULL, 0);
283 #       elif defined(__sgi)
284         t = sysconf(_SC_NPROC_ONLN);
285 #       else
286         t = (int)sysconf(_SC_NPROCESSORS_ONLN);
287 #       endif
288 #endif
289         
290         if (t>RE_MAX_THREAD)
291                 return RE_MAX_THREAD;
292         if (t<1)
293                 return 1;
294         
295         return t;
296 }
297
298 /* ************************************************ */
299
300 typedef struct ThreadedWorker {
301         ListBase threadbase;
302         void *(*work_fnct)(void *);
303         char     busy[RE_MAX_THREAD];
304         int              total;
305         int              sleep_time;
306 } ThreadedWorker;
307
308 typedef struct WorkParam {
309         ThreadedWorker *worker;
310         void *param;
311         int       index;
312 } WorkParam;
313
314 void *exec_work_fnct(void *v_param)
315 {
316         WorkParam *p = (WorkParam*)v_param;
317         void *value;
318         
319         value = p->worker->work_fnct(p->param);
320         
321         p->worker->busy[p->index] = 0;
322         MEM_freeN(p);
323         
324         return value;
325 }
326
327 ThreadedWorker *BLI_create_worker(void *(*do_thread)(void *), int tot, int sleep_time)
328 {
329         ThreadedWorker *worker;
330         
331         worker = MEM_callocN(sizeof(ThreadedWorker), "threadedworker");
332         
333         if (tot > RE_MAX_THREAD)
334         {
335                 tot = RE_MAX_THREAD;
336         }
337         else if (tot < 1)
338         {
339                 tot= 1;
340         }
341         
342         worker->total = tot;
343         worker->work_fnct = do_thread;
344         
345         BLI_init_threads(&worker->threadbase, exec_work_fnct, tot);
346         
347         return worker;
348 }
349
350 void BLI_end_worker(ThreadedWorker *worker)
351 {
352         BLI_remove_threads(&worker->threadbase);
353 }
354
355 void BLI_destroy_worker(ThreadedWorker *worker)
356 {
357         BLI_end_worker(worker);
358         BLI_freelistN(&worker->threadbase);
359         MEM_freeN(worker);
360 }
361
362 void BLI_insert_work(ThreadedWorker *worker, void *param)
363 {
364         WorkParam *p = MEM_callocN(sizeof(WorkParam), "workparam");
365         int index;
366         
367         if (BLI_available_threads(&worker->threadbase) == 0)
368         {
369                 index = worker->total;
370                 while(index == worker->total)
371                 {
372                         PIL_sleep_ms(worker->sleep_time);
373                         
374                         for (index = 0; index < worker->total; index++)
375                         {
376                                 if (worker->busy[index] == 0)
377                                 {
378                                         BLI_remove_thread_index(&worker->threadbase, index);
379                                         break;
380                                 }
381                         }
382                 }
383         }
384         else
385         {
386                 index = BLI_available_thread_index(&worker->threadbase);
387         }
388         
389         worker->busy[index] = 1;
390         
391         p->param = param;
392         p->index = index;
393         p->worker = worker;
394         
395         BLI_insert_thread(&worker->threadbase, p);
396 }
397
398 /* eof */