Replacing SDL threads with pthread.
[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
88 /* just a max for security reasons */
89 #define RE_MAX_THREAD   8
90
91 typedef struct ThreadSlot {
92         struct ThreadSlot *next, *prev;
93         void *(*do_thread)(void *);
94         void *callerdata;
95         pthread_t pthread;
96         int avail;
97 } ThreadSlot;
98
99 void BLI_init_threads(ListBase *threadbase, void *(*do_thread)(void *), int tot)
100 {
101         int a;
102         
103         if(threadbase==NULL)
104                 return;
105         threadbase->first= threadbase->last= NULL;
106         
107         if(tot>RE_MAX_THREAD) tot= RE_MAX_THREAD;
108         else if(tot<1) tot= 1;
109         
110         for(a=0; a<tot; a++) {
111                 ThreadSlot *tslot= MEM_callocN(sizeof(ThreadSlot), "threadslot");
112                 BLI_addtail(threadbase, tslot);
113                 tslot->do_thread= do_thread;
114                 tslot->avail= 1;
115         }
116 }
117
118 /* amount of available threads */
119 int BLI_available_threads(ListBase *threadbase)
120 {
121         ThreadSlot *tslot;
122         int counter=0;
123         
124         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
125                 if(tslot->avail)
126                         counter++;
127         }
128         return counter;
129 }
130
131 /* returns thread number, for sample patterns or threadsafe tables */
132 int BLI_available_thread_index(ListBase *threadbase)
133 {
134         ThreadSlot *tslot;
135         int counter=0;
136         
137         for(tslot= threadbase->first; tslot; tslot= tslot->next, counter++) {
138                 if(tslot->avail)
139                         return counter;
140         }
141         return 0;
142 }
143
144
145 void BLI_insert_thread(ListBase *threadbase, void *callerdata)
146 {
147         ThreadSlot *tslot;
148         
149         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
150                 if(tslot->avail) {
151                         tslot->avail= 0;
152                         tslot->callerdata= callerdata;
153                         pthread_create(&tslot->pthread, NULL, tslot->do_thread, tslot->callerdata);
154                         return;
155                 }
156         }
157         printf("ERROR: could not insert thread slot\n");
158 }
159
160 void BLI_remove_thread(ListBase *threadbase, void *callerdata)
161 {
162         ThreadSlot *tslot;
163         
164         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
165                 if(tslot->callerdata==callerdata) {
166                         tslot->callerdata= NULL;
167                         pthread_join(tslot->pthread, NULL);
168                         tslot->avail= 1;
169                 }
170         }
171 }
172
173 void BLI_end_threads(ListBase *threadbase)
174 {
175         ThreadSlot *tslot;
176         
177         for(tslot= threadbase->first; tslot; tslot= tslot->next) {
178                 if(tslot->avail==0) {
179                         pthread_join(tslot->pthread, NULL);
180                 }
181         }
182         BLI_freelistN(threadbase);
183         
184 }
185
186 void BLI_lock_thread(void)
187 {
188         pthread_mutex_lock(&_malloc_lock);
189 }
190
191 void BLI_unlock_thread(void)
192 {
193         pthread_mutex_unlock(&_malloc_lock);
194 }
195
196
197 /* ***************** Thread safe MEM_malloc/calloc/free ************************** */
198
199 void *MEM_mallocT(int len, char *name)
200 {
201         void *mem;
202         pthread_mutex_lock(&_malloc_lock);
203         mem= MEM_mallocN(len, name);
204         pthread_mutex_unlock(&_malloc_lock);
205         return mem;
206 }
207 void *MEM_callocT(int len, char *name)
208 {
209         void *mem;
210         pthread_mutex_lock(&_malloc_lock);
211         mem= MEM_callocN(len, name);
212         pthread_mutex_unlock(&_malloc_lock);
213         return mem;
214 }
215 void *MEM_mapallocT(int len, char *name)
216 {
217         void *mem;
218         pthread_mutex_lock(&_malloc_lock);
219         mem= MEM_mapallocN(len, name);
220         pthread_mutex_unlock(&_malloc_lock);
221         return mem;
222 }
223 void MEM_freeT(void *poin)
224 {
225         pthread_mutex_lock(&_malloc_lock);
226         MEM_freeN(poin);
227         pthread_mutex_unlock(&_malloc_lock);
228 }
229
230
231 /* eof */