== Sequencer ==
[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
42 /* ********** basic thread control API ************ 
43
44 Many thread cases have an X amount of jobs, and only an Y amount of
45 threads are useful (typically amount of cpus)
46
47 This code can be used to start a maximum amount of 'thread slots', which
48 then can be filled in a loop with an idle timer. 
49
50 A sample loop can look like this (pseudo c);
51
52         ListBase lb;
53         int maxthreads= 2;
54         int cont= 1;
55
56         BLI_init_threads(&lb, do_something_func, maxthreads);
57
58         while(cont) {
59                 if(BLI_available_threads(&lb) && !(escape loop event)) {
60                         // get new job (data pointer)
61                         // tag job 'processed 
62                         BLI_insert_thread(&lb, job);
63                 }
64                 else PIL_sleep_ms(50);
65                 
66                 // find if a job is ready, this the do_something_func() should write in job somewhere
67                 cont= 0;
68                 for(go over all jobs)
69                         if(job is ready) {
70                                 if(job was not removed) {
71                                         BLI_remove_thread(&lb, job);
72                                 }
73                         }
74                         else cont= 1;
75                 }
76                 // conditions to exit loop 
77                 if(if escape loop event) {
78                         if(BLI_available_threadslots(&lb)==maxthreads)
79                                 break;
80                 }
81         }
82
83         BLI_end_threads(&lb);
84
85  ************************************************ */
86 static pthread_mutex_t _malloc_lock = PTHREAD_MUTEX_INITIALIZER;
87 static pthread_mutex_t _image_lock = PTHREAD_MUTEX_INITIALIZER;
88 static pthread_mutex_t _custom1_lock = PTHREAD_MUTEX_INITIALIZER;
89 static int thread_levels= 0;    /* threads can be invoked inside threads */
90
91 /* just a max for security reasons */
92 #define RE_MAX_THREAD   8
93
94 typedef struct ThreadSlot {
95         struct ThreadSlot *next, *prev;
96         void *(*do_thread)(void *);
97         void *callerdata;
98         pthread_t pthread;
99         int avail;
100 } ThreadSlot;
101
102 static void BLI_lock_malloc_thread(void)
103 {
104         pthread_mutex_lock(&_malloc_lock);
105 }
106
107 static void BLI_unlock_malloc_thread(void)
108 {
109         pthread_mutex_unlock(&_malloc_lock);
110 }
111
112 /* tot = 0 only initializes malloc mutex in a safe way (see sequence.c)
113    problem otherwise: scene render will kill of the mutex!
114 */
115
116 void BLI_init_threads(ListBase *threadbase, void *(*do_thread)(void *), int tot)
117 {
118         int a;
119         
120         if(threadbase != NULL && tot > 0) {
121                 threadbase->first= threadbase->last= NULL;
122         
123                 if(tot>RE_MAX_THREAD) tot= RE_MAX_THREAD;
124                 else if(tot<1) tot= 1;
125         
126                 for(a=0; a<tot; a++) {
127                         ThreadSlot *tslot= MEM_callocN(sizeof(ThreadSlot), "threadslot");
128                         BLI_addtail(threadbase, tslot);
129                         tslot->do_thread= do_thread;
130                         tslot->avail= 1;
131                 }
132         }
133
134         MEM_set_lock_callback(BLI_lock_malloc_thread, BLI_unlock_malloc_thread);
135         thread_levels++;
136 }
137
138 /* amount of available threads */
139 int BLI_available_threads(ListBase *threadbase)
140 {
141         ThreadSlot *tslot;
142         int counter=0;
143         
144         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
145                 if(tslot->avail)
146                         counter++;
147         }
148         return counter;
149 }
150
151 /* returns thread number, for sample patterns or threadsafe tables */
152 int BLI_available_thread_index(ListBase *threadbase)
153 {
154         ThreadSlot *tslot;
155         int counter=0;
156         
157         for(tslot= threadbase->first; tslot; tslot= tslot->next, counter++) {
158                 if(tslot->avail)
159                         return counter;
160         }
161         return 0;
162 }
163
164
165 void BLI_insert_thread(ListBase *threadbase, void *callerdata)
166 {
167         ThreadSlot *tslot;
168         
169         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
170                 if(tslot->avail) {
171                         tslot->avail= 0;
172                         tslot->callerdata= callerdata;
173                         pthread_create(&tslot->pthread, NULL, tslot->do_thread, tslot->callerdata);
174                         return;
175                 }
176         }
177         printf("ERROR: could not insert thread slot\n");
178 }
179
180 void BLI_remove_thread(ListBase *threadbase, void *callerdata)
181 {
182         ThreadSlot *tslot;
183         
184         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
185                 if(tslot->callerdata==callerdata) {
186                         tslot->callerdata= NULL;
187                         pthread_join(tslot->pthread, NULL);
188                         tslot->avail= 1;
189                 }
190         }
191 }
192
193 void BLI_end_threads(ListBase *threadbase)
194 {
195         ThreadSlot *tslot;
196         
197         if (threadbase) {
198                 for(tslot= threadbase->first; tslot; tslot= tslot->next) {
199                         if(tslot->avail==0) {
200                                 pthread_join(tslot->pthread, NULL);
201                         }
202                 }
203                 BLI_freelistN(threadbase);
204         }
205         
206         thread_levels--;
207         if(thread_levels==0)
208                 MEM_set_lock_callback(NULL, NULL);
209 }
210
211 void BLI_lock_thread(int type)
212 {
213         if (type==LOCK_IMAGE)
214                 pthread_mutex_lock(&_image_lock);
215         else if (type==LOCK_CUSTOM1)
216                 pthread_mutex_lock(&_custom1_lock);
217 }
218
219 void BLI_unlock_thread(int type)
220 {
221         if (type==LOCK_IMAGE)
222                 pthread_mutex_unlock(&_image_lock);
223         else if(type==LOCK_CUSTOM1)
224                 pthread_mutex_unlock(&_custom1_lock);
225 }
226
227 /* eof */