Undo revision 23130 which was a merge with 2.5, a messy one because I did something...
[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
147         MEM_set_lock_callback(BLI_lock_malloc_thread, BLI_unlock_malloc_thread);
148         thread_levels++;
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         if (threadbase) {
239                 for(tslot= threadbase->first; tslot; tslot= tslot->next) {
240                         if(tslot->avail==0) {
241                                 pthread_join(tslot->pthread, NULL);
242                         }
243                 }
244                 BLI_freelistN(threadbase);
245         }
246         
247         thread_levels--;
248         if(thread_levels==0)
249                 MEM_set_lock_callback(NULL, NULL);
250 }
251
252 void BLI_lock_thread(int type)
253 {
254         if (type==LOCK_IMAGE)
255                 pthread_mutex_lock(&_image_lock);
256         else if (type==LOCK_CUSTOM1)
257                 pthread_mutex_lock(&_custom1_lock);
258 }
259
260 void BLI_unlock_thread(int type)
261 {
262         if (type==LOCK_IMAGE)
263                 pthread_mutex_unlock(&_image_lock);
264         else if(type==LOCK_CUSTOM1)
265                 pthread_mutex_unlock(&_custom1_lock);
266 }
267
268 /* how many threads are native on this system? */
269 int BLI_system_thread_count( void )
270 {
271         int t;
272 #ifdef WIN32
273         SYSTEM_INFO info;
274         GetSystemInfo(&info);
275         t = (int) info.dwNumberOfProcessors;
276 #else 
277 #       ifdef __APPLE__
278         int mib[2];
279         size_t len;
280         
281         mib[0] = CTL_HW;
282         mib[1] = HW_NCPU;
283         len = sizeof(t);
284         sysctl(mib, 2, &t, &len, NULL, 0);
285 #       elif defined(__sgi)
286         t = sysconf(_SC_NPROC_ONLN);
287 #       else
288         t = (int)sysconf(_SC_NPROCESSORS_ONLN);
289 #       endif
290 #endif
291         
292         if (t>RE_MAX_THREAD)
293                 return RE_MAX_THREAD;
294         if (t<1)
295                 return 1;
296         
297         return t;
298 }
299
300 /* ************************************************ */
301
302 typedef struct ThreadedWorker {
303         ListBase threadbase;
304         void *(*work_fnct)(void *);
305         char     busy[RE_MAX_THREAD];
306         int              total;
307         int              sleep_time;
308 } ThreadedWorker;
309
310 typedef struct WorkParam {
311         ThreadedWorker *worker;
312         void *param;
313         int       index;
314 } WorkParam;
315
316 void *exec_work_fnct(void *v_param)
317 {
318         WorkParam *p = (WorkParam*)v_param;
319         void *value;
320         
321         value = p->worker->work_fnct(p->param);
322         
323         p->worker->busy[p->index] = 0;
324         MEM_freeN(p);
325         
326         return value;
327 }
328
329 ThreadedWorker *BLI_create_worker(void *(*do_thread)(void *), int tot, int sleep_time)
330 {
331         ThreadedWorker *worker;
332         
333         worker = MEM_callocN(sizeof(ThreadedWorker), "threadedworker");
334         
335         if (tot > RE_MAX_THREAD)
336         {
337                 tot = RE_MAX_THREAD;
338         }
339         else if (tot < 1)
340         {
341                 tot= 1;
342         }
343         
344         worker->total = tot;
345         worker->work_fnct = do_thread;
346         
347         BLI_init_threads(&worker->threadbase, exec_work_fnct, tot);
348         
349         return worker;
350 }
351
352 void BLI_end_worker(ThreadedWorker *worker)
353 {
354         BLI_remove_threads(&worker->threadbase);
355 }
356
357 void BLI_destroy_worker(ThreadedWorker *worker)
358 {
359         BLI_end_worker(worker);
360         BLI_freelistN(&worker->threadbase);
361         MEM_freeN(worker);
362 }
363
364 void BLI_insert_work(ThreadedWorker *worker, void *param)
365 {
366         WorkParam *p = MEM_callocN(sizeof(WorkParam), "workparam");
367         int index;
368         
369         if (BLI_available_threads(&worker->threadbase) == 0)
370         {
371                 index = worker->total;
372                 while(index == worker->total)
373                 {
374                         PIL_sleep_ms(worker->sleep_time);
375                         
376                         for (index = 0; index < worker->total; index++)
377                         {
378                                 if (worker->busy[index] == 0)
379                                 {
380                                         BLI_remove_thread_index(&worker->threadbase, index);
381                                         break;
382                                 }
383                         }
384                 }
385         }
386         else
387         {
388                 index = BLI_available_thread_index(&worker->threadbase);
389         }
390         
391         worker->busy[index] = 1;
392         
393         p->param = param;
394         p->index = index;
395         p->worker = worker;
396         
397         BLI_insert_thread(&worker->threadbase, p);
398 }
399
400 /* eof */