Blender V2.61 - r43446

threads.c

Go to the documentation of this file.
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 }