Blender V2.61 - r43446
|
00001 /* 00002 * 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software Foundation, 00018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 * 00020 * The Original Code is Copyright (C) 2006 Blender Foundation 00021 * All rights reserved. 00022 * 00023 * The Original Code is: all of this file. 00024 * 00025 * Contributor(s): none yet. 00026 * 00027 * ***** END GPL LICENSE BLOCK ***** 00028 */ 00029 00035 #include <errno.h> 00036 #include <string.h> 00037 00038 #include "MEM_guardedalloc.h" 00039 00040 00041 #include "BLI_blenlib.h" 00042 #include "BLI_gsqueue.h" 00043 #include "BLI_threads.h" 00044 00045 #include "PIL_time.h" 00046 00047 /* for checking system threads - BLI_system_thread_count */ 00048 #ifdef WIN32 00049 #include "windows.h" 00050 #include <sys/timeb.h> 00051 #elif defined(__APPLE__) 00052 #include <sys/types.h> 00053 #include <sys/sysctl.h> 00054 #else 00055 #include <unistd.h> 00056 #include <sys/time.h> 00057 #endif 00058 00059 #if defined(__APPLE__) && (PARALLEL == 1) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2) 00060 /* ************** libgomp (Apple gcc 4.2.1) TLS bug workaround *************** */ 00061 extern pthread_key_t gomp_tls_key; 00062 static void *thread_tls_data; 00063 #endif 00064 00065 /* ********** basic thread control API ************ 00066 00067 Many thread cases have an X amount of jobs, and only an Y amount of 00068 threads are useful (typically amount of cpus) 00069 00070 This code can be used to start a maximum amount of 'thread slots', which 00071 then can be filled in a loop with an idle timer. 00072 00073 A sample loop can look like this (pseudo c); 00074 00075 ListBase lb; 00076 int maxthreads= 2; 00077 int cont= 1; 00078 00079 BLI_init_threads(&lb, do_something_func, maxthreads); 00080 00081 while(cont) { 00082 if(BLI_available_threads(&lb) && !(escape loop event)) { 00083 // get new job (data pointer) 00084 // tag job 'processed 00085 BLI_insert_thread(&lb, job); 00086 } 00087 else PIL_sleep_ms(50); 00088 00089 // find if a job is ready, this the do_something_func() should write in job somewhere 00090 cont= 0; 00091 for(go over all jobs) 00092 if(job is ready) { 00093 if(job was not removed) { 00094 BLI_remove_thread(&lb, job); 00095 } 00096 } 00097 else cont= 1; 00098 } 00099 // conditions to exit loop 00100 if(if escape loop event) { 00101 if(BLI_available_threadslots(&lb)==maxthreads) 00102 break; 00103 } 00104 } 00105 00106 BLI_end_threads(&lb); 00107 00108 ************************************************ */ 00109 static pthread_mutex_t _malloc_lock = PTHREAD_MUTEX_INITIALIZER; 00110 static pthread_mutex_t _image_lock = PTHREAD_MUTEX_INITIALIZER; 00111 static pthread_mutex_t _preview_lock = PTHREAD_MUTEX_INITIALIZER; 00112 static pthread_mutex_t _viewer_lock = PTHREAD_MUTEX_INITIALIZER; 00113 static pthread_mutex_t _custom1_lock = PTHREAD_MUTEX_INITIALIZER; 00114 static pthread_mutex_t _rcache_lock = PTHREAD_MUTEX_INITIALIZER; 00115 static pthread_mutex_t _opengl_lock = PTHREAD_MUTEX_INITIALIZER; 00116 static pthread_mutex_t _nodes_lock = PTHREAD_MUTEX_INITIALIZER; 00117 static pthread_mutex_t _movieclip_lock = PTHREAD_MUTEX_INITIALIZER; 00118 static pthread_t mainid; 00119 static int thread_levels= 0; /* threads can be invoked inside threads */ 00120 00121 /* just a max for security reasons */ 00122 #define RE_MAX_THREAD BLENDER_MAX_THREADS 00123 00124 typedef struct ThreadSlot { 00125 struct ThreadSlot *next, *prev; 00126 void *(*do_thread)(void *); 00127 void *callerdata; 00128 pthread_t pthread; 00129 int avail; 00130 } ThreadSlot; 00131 00132 static void BLI_lock_malloc_thread(void) 00133 { 00134 pthread_mutex_lock(&_malloc_lock); 00135 } 00136 00137 static void BLI_unlock_malloc_thread(void) 00138 { 00139 pthread_mutex_unlock(&_malloc_lock); 00140 } 00141 00142 void BLI_threadapi_init(void) 00143 { 00144 mainid = pthread_self(); 00145 } 00146 00147 /* tot = 0 only initializes malloc mutex in a safe way (see sequence.c) 00148 problem otherwise: scene render will kill of the mutex! 00149 */ 00150 00151 void BLI_init_threads(ListBase *threadbase, void *(*do_thread)(void *), int tot) 00152 { 00153 int a; 00154 00155 if(threadbase != NULL && tot > 0) { 00156 threadbase->first= threadbase->last= NULL; 00157 00158 if(tot>RE_MAX_THREAD) tot= RE_MAX_THREAD; 00159 else if(tot<1) tot= 1; 00160 00161 for(a=0; a<tot; a++) { 00162 ThreadSlot *tslot= MEM_callocN(sizeof(ThreadSlot), "threadslot"); 00163 BLI_addtail(threadbase, tslot); 00164 tslot->do_thread= do_thread; 00165 tslot->avail= 1; 00166 } 00167 } 00168 00169 if(thread_levels == 0) { 00170 MEM_set_lock_callback(BLI_lock_malloc_thread, BLI_unlock_malloc_thread); 00171 00172 #if defined(__APPLE__) && (PARALLEL == 1) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2) 00173 /* workaround for Apple gcc 4.2.1 omp vs background thread bug, 00174 we copy gomp thread local storage pointer to setting it again 00175 inside the thread that we start */ 00176 thread_tls_data = pthread_getspecific(gomp_tls_key); 00177 #endif 00178 } 00179 00180 thread_levels++; 00181 } 00182 00183 /* amount of available threads */ 00184 int BLI_available_threads(ListBase *threadbase) 00185 { 00186 ThreadSlot *tslot; 00187 int counter=0; 00188 00189 for(tslot= threadbase->first; tslot; tslot= tslot->next) { 00190 if(tslot->avail) 00191 counter++; 00192 } 00193 return counter; 00194 } 00195 00196 /* returns thread number, for sample patterns or threadsafe tables */ 00197 int BLI_available_thread_index(ListBase *threadbase) 00198 { 00199 ThreadSlot *tslot; 00200 int counter=0; 00201 00202 for(tslot= threadbase->first; tslot; tslot= tslot->next, counter++) { 00203 if(tslot->avail) 00204 return counter; 00205 } 00206 return 0; 00207 } 00208 00209 static void *tslot_thread_start(void *tslot_p) 00210 { 00211 ThreadSlot *tslot= (ThreadSlot*)tslot_p; 00212 00213 #if defined(__APPLE__) && (PARALLEL == 1) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2) 00214 /* workaround for Apple gcc 4.2.1 omp vs background thread bug, 00215 set gomp thread local storage pointer which was copied beforehand */ 00216 pthread_setspecific (gomp_tls_key, thread_tls_data); 00217 #endif 00218 00219 return tslot->do_thread(tslot->callerdata); 00220 } 00221 00222 int BLI_thread_is_main(void) 00223 { 00224 return pthread_equal(pthread_self(), mainid); 00225 } 00226 00227 void BLI_insert_thread(ListBase *threadbase, void *callerdata) 00228 { 00229 ThreadSlot *tslot; 00230 00231 for(tslot= threadbase->first; tslot; tslot= tslot->next) { 00232 if(tslot->avail) { 00233 tslot->avail= 0; 00234 tslot->callerdata= callerdata; 00235 pthread_create(&tslot->pthread, NULL, tslot_thread_start, tslot); 00236 return; 00237 } 00238 } 00239 printf("ERROR: could not insert thread slot\n"); 00240 } 00241 00242 void BLI_remove_thread(ListBase *threadbase, void *callerdata) 00243 { 00244 ThreadSlot *tslot; 00245 00246 for(tslot= threadbase->first; tslot; tslot= tslot->next) { 00247 if(tslot->callerdata==callerdata) { 00248 pthread_join(tslot->pthread, NULL); 00249 tslot->callerdata= NULL; 00250 tslot->avail= 1; 00251 } 00252 } 00253 } 00254 00255 void BLI_remove_thread_index(ListBase *threadbase, int index) 00256 { 00257 ThreadSlot *tslot; 00258 int counter=0; 00259 00260 for(tslot = threadbase->first; tslot; tslot = tslot->next, counter++) { 00261 if (counter == index && tslot->avail == 0) { 00262 pthread_join(tslot->pthread, NULL); 00263 tslot->callerdata = NULL; 00264 tslot->avail = 1; 00265 break; 00266 } 00267 } 00268 } 00269 00270 void BLI_remove_threads(ListBase *threadbase) 00271 { 00272 ThreadSlot *tslot; 00273 00274 for(tslot = threadbase->first; tslot; tslot = tslot->next) { 00275 if (tslot->avail == 0) { 00276 pthread_join(tslot->pthread, NULL); 00277 tslot->callerdata = NULL; 00278 tslot->avail = 1; 00279 } 00280 } 00281 } 00282 00283 void BLI_end_threads(ListBase *threadbase) 00284 { 00285 ThreadSlot *tslot; 00286 00287 /* only needed if there's actually some stuff to end 00288 * this way we don't end up decrementing thread_levels on an empty threadbase 00289 * */ 00290 if (threadbase && threadbase->first != NULL) { 00291 for(tslot= threadbase->first; tslot; tslot= tslot->next) { 00292 if(tslot->avail==0) { 00293 pthread_join(tslot->pthread, NULL); 00294 } 00295 } 00296 BLI_freelistN(threadbase); 00297 } 00298 00299 thread_levels--; 00300 if(thread_levels==0) 00301 MEM_set_lock_callback(NULL, NULL); 00302 } 00303 00304 /* System Information */ 00305 00306 /* how many threads are native on this system? */ 00307 int BLI_system_thread_count( void ) 00308 { 00309 int t; 00310 #ifdef WIN32 00311 SYSTEM_INFO info; 00312 GetSystemInfo(&info); 00313 t = (int) info.dwNumberOfProcessors; 00314 #else 00315 # ifdef __APPLE__ 00316 int mib[2]; 00317 size_t len; 00318 00319 mib[0] = CTL_HW; 00320 mib[1] = HW_NCPU; 00321 len = sizeof(t); 00322 sysctl(mib, 2, &t, &len, NULL, 0); 00323 # else 00324 t = (int)sysconf(_SC_NPROCESSORS_ONLN); 00325 # endif 00326 #endif 00327 00328 if (t>RE_MAX_THREAD) 00329 return RE_MAX_THREAD; 00330 if (t<1) 00331 return 1; 00332 00333 return t; 00334 } 00335 00336 /* Global Mutex Locks */ 00337 00338 void BLI_lock_thread(int type) 00339 { 00340 if (type==LOCK_IMAGE) 00341 pthread_mutex_lock(&_image_lock); 00342 else if (type==LOCK_PREVIEW) 00343 pthread_mutex_lock(&_preview_lock); 00344 else if (type==LOCK_VIEWER) 00345 pthread_mutex_lock(&_viewer_lock); 00346 else if (type==LOCK_CUSTOM1) 00347 pthread_mutex_lock(&_custom1_lock); 00348 else if (type==LOCK_RCACHE) 00349 pthread_mutex_lock(&_rcache_lock); 00350 else if (type==LOCK_OPENGL) 00351 pthread_mutex_lock(&_opengl_lock); 00352 else if (type==LOCK_NODES) 00353 pthread_mutex_lock(&_nodes_lock); 00354 else if (type==LOCK_MOVIECLIP) 00355 pthread_mutex_lock(&_movieclip_lock); 00356 } 00357 00358 void BLI_unlock_thread(int type) 00359 { 00360 if (type==LOCK_IMAGE) 00361 pthread_mutex_unlock(&_image_lock); 00362 else if (type==LOCK_PREVIEW) 00363 pthread_mutex_unlock(&_preview_lock); 00364 else if (type==LOCK_VIEWER) 00365 pthread_mutex_unlock(&_viewer_lock); 00366 else if(type==LOCK_CUSTOM1) 00367 pthread_mutex_unlock(&_custom1_lock); 00368 else if(type==LOCK_RCACHE) 00369 pthread_mutex_unlock(&_rcache_lock); 00370 else if(type==LOCK_OPENGL) 00371 pthread_mutex_unlock(&_opengl_lock); 00372 else if(type==LOCK_NODES) 00373 pthread_mutex_unlock(&_nodes_lock); 00374 else if(type==LOCK_MOVIECLIP) 00375 pthread_mutex_unlock(&_movieclip_lock); 00376 } 00377 00378 /* Mutex Locks */ 00379 00380 void BLI_mutex_init(ThreadMutex *mutex) 00381 { 00382 pthread_mutex_init(mutex, NULL); 00383 } 00384 00385 void BLI_mutex_lock(ThreadMutex *mutex) 00386 { 00387 pthread_mutex_lock(mutex); 00388 } 00389 00390 void BLI_mutex_unlock(ThreadMutex *mutex) 00391 { 00392 pthread_mutex_unlock(mutex); 00393 } 00394 00395 void BLI_mutex_end(ThreadMutex *mutex) 00396 { 00397 pthread_mutex_destroy(mutex); 00398 } 00399 00400 /* Read/Write Mutex Lock */ 00401 00402 void BLI_rw_mutex_init(ThreadRWMutex *mutex) 00403 { 00404 pthread_rwlock_init(mutex, NULL); 00405 } 00406 00407 void BLI_rw_mutex_lock(ThreadRWMutex *mutex, int mode) 00408 { 00409 if(mode == THREAD_LOCK_READ) 00410 pthread_rwlock_rdlock(mutex); 00411 else 00412 pthread_rwlock_wrlock(mutex); 00413 } 00414 00415 void BLI_rw_mutex_unlock(ThreadRWMutex *mutex) 00416 { 00417 pthread_rwlock_unlock(mutex); 00418 } 00419 00420 void BLI_rw_mutex_end(ThreadRWMutex *mutex) 00421 { 00422 pthread_rwlock_destroy(mutex); 00423 } 00424 00425 /* ************************************************ */ 00426 00427 typedef struct ThreadedWorker { 00428 ListBase threadbase; 00429 void *(*work_fnct)(void *); 00430 char busy[RE_MAX_THREAD]; 00431 int total; 00432 int sleep_time; 00433 } ThreadedWorker; 00434 00435 typedef struct WorkParam { 00436 ThreadedWorker *worker; 00437 void *param; 00438 int index; 00439 } WorkParam; 00440 00441 static void *exec_work_fnct(void *v_param) 00442 { 00443 WorkParam *p = (WorkParam*)v_param; 00444 void *value; 00445 00446 value = p->worker->work_fnct(p->param); 00447 00448 p->worker->busy[p->index] = 0; 00449 MEM_freeN(p); 00450 00451 return value; 00452 } 00453 00454 ThreadedWorker *BLI_create_worker(void *(*do_thread)(void *), int tot, int sleep_time) 00455 { 00456 ThreadedWorker *worker; 00457 00458 (void)sleep_time; /* unused */ 00459 00460 worker = MEM_callocN(sizeof(ThreadedWorker), "threadedworker"); 00461 00462 if (tot > RE_MAX_THREAD) 00463 { 00464 tot = RE_MAX_THREAD; 00465 } 00466 else if (tot < 1) 00467 { 00468 tot= 1; 00469 } 00470 00471 worker->total = tot; 00472 worker->work_fnct = do_thread; 00473 00474 BLI_init_threads(&worker->threadbase, exec_work_fnct, tot); 00475 00476 return worker; 00477 } 00478 00479 void BLI_end_worker(ThreadedWorker *worker) 00480 { 00481 BLI_remove_threads(&worker->threadbase); 00482 } 00483 00484 void BLI_destroy_worker(ThreadedWorker *worker) 00485 { 00486 BLI_end_worker(worker); 00487 BLI_freelistN(&worker->threadbase); 00488 MEM_freeN(worker); 00489 } 00490 00491 void BLI_insert_work(ThreadedWorker *worker, void *param) 00492 { 00493 WorkParam *p = MEM_callocN(sizeof(WorkParam), "workparam"); 00494 int index; 00495 00496 if (BLI_available_threads(&worker->threadbase) == 0) 00497 { 00498 index = worker->total; 00499 while(index == worker->total) 00500 { 00501 PIL_sleep_ms(worker->sleep_time); 00502 00503 for (index = 0; index < worker->total; index++) 00504 { 00505 if (worker->busy[index] == 0) 00506 { 00507 BLI_remove_thread_index(&worker->threadbase, index); 00508 break; 00509 } 00510 } 00511 } 00512 } 00513 else 00514 { 00515 index = BLI_available_thread_index(&worker->threadbase); 00516 } 00517 00518 worker->busy[index] = 1; 00519 00520 p->param = param; 00521 p->index = index; 00522 p->worker = worker; 00523 00524 BLI_insert_thread(&worker->threadbase, p); 00525 } 00526 00527 /* ************************************************ */ 00528 00529 struct ThreadQueue { 00530 GSQueue *queue; 00531 pthread_mutex_t mutex; 00532 pthread_cond_t cond; 00533 int nowait; 00534 }; 00535 00536 ThreadQueue *BLI_thread_queue_init(void) 00537 { 00538 ThreadQueue *queue; 00539 00540 queue= MEM_callocN(sizeof(ThreadQueue), "ThreadQueue"); 00541 queue->queue= BLI_gsqueue_new(sizeof(void*)); 00542 00543 pthread_mutex_init(&queue->mutex, NULL); 00544 pthread_cond_init(&queue->cond, NULL); 00545 00546 return queue; 00547 } 00548 00549 void BLI_thread_queue_free(ThreadQueue *queue) 00550 { 00551 pthread_cond_destroy(&queue->cond); 00552 pthread_mutex_destroy(&queue->mutex); 00553 00554 BLI_gsqueue_free(queue->queue); 00555 00556 MEM_freeN(queue); 00557 } 00558 00559 void BLI_thread_queue_push(ThreadQueue *queue, void *work) 00560 { 00561 pthread_mutex_lock(&queue->mutex); 00562 00563 BLI_gsqueue_push(queue->queue, &work); 00564 00565 /* signal threads waiting to pop */ 00566 pthread_cond_signal(&queue->cond); 00567 pthread_mutex_unlock(&queue->mutex); 00568 } 00569 00570 void *BLI_thread_queue_pop(ThreadQueue *queue) 00571 { 00572 void *work= NULL; 00573 00574 /* wait until there is work */ 00575 pthread_mutex_lock(&queue->mutex); 00576 while(BLI_gsqueue_is_empty(queue->queue) && !queue->nowait) 00577 pthread_cond_wait(&queue->cond, &queue->mutex); 00578 00579 /* if we have something, pop it */ 00580 if(!BLI_gsqueue_is_empty(queue->queue)) 00581 BLI_gsqueue_pop(queue->queue, &work); 00582 00583 pthread_mutex_unlock(&queue->mutex); 00584 00585 return work; 00586 } 00587 00588 static void wait_timeout(struct timespec *timeout, int ms) 00589 { 00590 ldiv_t div_result; 00591 long sec, usec, x; 00592 00593 #ifdef WIN32 00594 { 00595 struct _timeb now; 00596 _ftime(&now); 00597 sec = now.time; 00598 usec = now.millitm*1000; /* microsecond precision would be better */ 00599 } 00600 #else 00601 { 00602 struct timeval now; 00603 gettimeofday(&now, NULL); 00604 sec = now.tv_sec; 00605 usec = now.tv_usec; 00606 } 00607 #endif 00608 00609 /* add current time + millisecond offset */ 00610 div_result = ldiv(ms, 1000); 00611 timeout->tv_sec = sec + div_result.quot; 00612 00613 x = usec + (div_result.rem*1000); 00614 00615 if (x >= 1000000) { 00616 timeout->tv_sec++; 00617 x -= 1000000; 00618 } 00619 00620 timeout->tv_nsec = x*1000; 00621 } 00622 00623 void *BLI_thread_queue_pop_timeout(ThreadQueue *queue, int ms) 00624 { 00625 double t; 00626 void *work= NULL; 00627 struct timespec timeout; 00628 00629 t= PIL_check_seconds_timer(); 00630 wait_timeout(&timeout, ms); 00631 00632 /* wait until there is work */ 00633 pthread_mutex_lock(&queue->mutex); 00634 while(BLI_gsqueue_is_empty(queue->queue) && !queue->nowait) { 00635 if(pthread_cond_timedwait(&queue->cond, &queue->mutex, &timeout) == ETIMEDOUT) 00636 break; 00637 else if(PIL_check_seconds_timer() - t >= ms*0.001) 00638 break; 00639 } 00640 00641 /* if we have something, pop it */ 00642 if(!BLI_gsqueue_is_empty(queue->queue)) 00643 BLI_gsqueue_pop(queue->queue, &work); 00644 00645 pthread_mutex_unlock(&queue->mutex); 00646 00647 return work; 00648 } 00649 00650 int BLI_thread_queue_size(ThreadQueue *queue) 00651 { 00652 int size; 00653 00654 pthread_mutex_lock(&queue->mutex); 00655 size= BLI_gsqueue_size(queue->queue); 00656 pthread_mutex_unlock(&queue->mutex); 00657 00658 return size; 00659 } 00660 00661 void BLI_thread_queue_nowait(ThreadQueue *queue) 00662 { 00663 pthread_mutex_lock(&queue->mutex); 00664 00665 queue->nowait= 1; 00666 00667 /* signal threads waiting to pop */ 00668 pthread_cond_signal(&queue->cond); 00669 pthread_mutex_unlock(&queue->mutex); 00670 } 00671 00672 void BLI_begin_threaded_malloc(void) 00673 { 00674 if(thread_levels == 0) { 00675 MEM_set_lock_callback(BLI_lock_malloc_thread, BLI_unlock_malloc_thread); 00676 } 00677 thread_levels++; 00678 } 00679 00680 void BLI_end_threaded_malloc(void) 00681 { 00682 thread_levels--; 00683 if(thread_levels==0) 00684 MEM_set_lock_callback(NULL, NULL); 00685 }