Blender V2.61 - r43446
|
00001 /* 00002 * ***** BEGIN GPL LICENSE BLOCK ***** 00003 * 00004 * This program is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License 00006 * as published by the Free Software Foundation; either version 2 00007 * of the License, or (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software Foundation, 00016 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 * 00018 * The Original Code is Copyright (C) 2009 Blender Foundation. 00019 * All rights reserved. 00020 * 00021 * 00022 * Contributor(s): Blender Foundation 00023 * 00024 * ***** END GPL LICENSE BLOCK ***** 00025 */ 00026 00032 #include <string.h> 00033 00034 #include "DNA_windowmanager_types.h" 00035 00036 #include "MEM_guardedalloc.h" 00037 00038 #include "BLI_blenlib.h" 00039 #include "BLI_threads.h" 00040 00041 #include "BKE_blender.h" 00042 #include "BKE_context.h" 00043 #include "BKE_idprop.h" 00044 #include "BKE_global.h" 00045 #include "BKE_library.h" 00046 #include "BKE_main.h" 00047 #include "BKE_report.h" 00048 00049 #include "WM_api.h" 00050 #include "WM_types.h" 00051 #include "wm_window.h" 00052 #include "wm_event_system.h" 00053 #include "wm_event_types.h" 00054 #include "wm.h" 00055 00056 00057 00058 /* ********************** Threaded Jobs Manager ****************************** */ 00059 00060 /* 00061 Add new job 00062 - register in WM 00063 - configure callbacks 00064 00065 Start or re-run job 00066 - if job running 00067 - signal job to end 00068 - add timer notifier to verify when it has ended, to start it 00069 - else 00070 - start job 00071 - add timer notifier to handle progress 00072 00073 Stop job 00074 - signal job to end 00075 on end, job will tag itself as sleeping 00076 00077 Remove job 00078 - signal job to end 00079 on end, job will remove itself 00080 00081 When job is done: 00082 - it puts timer to sleep (or removes?) 00083 00084 */ 00085 00086 struct wmJob { 00087 struct wmJob *next, *prev; 00088 00089 /* job originating from, keep track of this when deleting windows */ 00090 wmWindow *win; 00091 00092 /* should store entire own context, for start, update, free */ 00093 void *customdata; 00094 /* to prevent cpu overhead, use this one which only gets called when job really starts, not in thread */ 00095 void (*initjob)(void *); 00096 /* this runs inside thread, and does full job */ 00097 void (*startjob)(void *, short *stop, short *do_update, float *progress); 00098 /* update gets called if thread defines so, and max once per timerstep */ 00099 /* it runs outside thread, blocking blender, no drawing! */ 00100 void (*update)(void *); 00101 /* free entire customdata, doesn't run in thread */ 00102 void (*free)(void *); 00103 /* gets called when job is stopped, not in thread */ 00104 void (*endjob)(void *); 00105 00106 /* running jobs each have own timer */ 00107 double timestep; 00108 wmTimer *wt; 00109 /* the notifier event timers should send */ 00110 unsigned int note, endnote; 00111 00112 00113 /* internal */ 00114 void *owner; 00115 int flag; 00116 short suspended, running, ready, do_update, stop; 00117 float progress; 00118 00119 /* for display in header, identification */ 00120 char name[128]; 00121 00122 /* once running, we store this separately */ 00123 void *run_customdata; 00124 void (*run_free)(void *); 00125 00126 /* we use BLI_threads api, but per job only 1 thread runs */ 00127 ListBase threads; 00128 00129 }; 00130 00131 /* finds: 00132 * 1st priority: job with same owner and name 00133 * 2nd priority: job with same owner 00134 */ 00135 static wmJob *wm_job_find(wmWindowManager *wm, void *owner, const char *name) 00136 { 00137 wmJob *steve, *found=NULL; 00138 00139 for(steve= wm->jobs.first; steve; steve= steve->next) 00140 if(steve->owner==owner) { 00141 found= steve; 00142 if (name && strcmp(steve->name, name)==0) 00143 return steve; 00144 } 00145 00146 return found; 00147 } 00148 00149 /* ******************* public API ***************** */ 00150 00151 /* returns current or adds new job, but doesnt run it */ 00152 /* every owner only gets a single job, adding a new one will stop running stop and 00153 when stopped it starts the new one */ 00154 wmJob *WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, const char *name, int flag) 00155 { 00156 wmJob *steve= wm_job_find(wm, owner, name); 00157 00158 if(steve==NULL) { 00159 steve= MEM_callocN(sizeof(wmJob), "new job"); 00160 00161 BLI_addtail(&wm->jobs, steve); 00162 steve->win= win; 00163 steve->owner= owner; 00164 steve->flag= flag; 00165 BLI_strncpy(steve->name, name, sizeof(steve->name)); 00166 } 00167 00168 return steve; 00169 } 00170 00171 /* returns true if job runs, for UI (progress) indicators */ 00172 int WM_jobs_test(wmWindowManager *wm, void *owner) 00173 { 00174 wmJob *steve; 00175 00176 for(steve= wm->jobs.first; steve; steve= steve->next) 00177 if(steve->owner==owner) 00178 if(steve->running) 00179 return 1; 00180 return 0; 00181 } 00182 00183 float WM_jobs_progress(wmWindowManager *wm, void *owner) 00184 { 00185 wmJob *steve= wm_job_find(wm, owner, NULL); 00186 00187 if (steve && steve->flag & WM_JOB_PROGRESS) 00188 return steve->progress; 00189 00190 return 0.0; 00191 } 00192 00193 char *WM_jobs_name(wmWindowManager *wm, void *owner) 00194 { 00195 wmJob *steve= wm_job_find(wm, owner, NULL); 00196 00197 if (steve) 00198 return steve->name; 00199 00200 return NULL; 00201 } 00202 00203 int WM_jobs_is_running(wmJob *steve) 00204 { 00205 return steve->running; 00206 } 00207 00208 void* WM_jobs_get_customdata(wmJob * steve) 00209 { 00210 if (!steve->customdata) { 00211 return steve->run_customdata; 00212 } else { 00213 return steve->customdata; 00214 } 00215 } 00216 00217 void WM_jobs_customdata(wmJob *steve, void *customdata, void (*free)(void *)) 00218 { 00219 /* pending job? just free */ 00220 if(steve->customdata) 00221 steve->free(steve->customdata); 00222 00223 steve->customdata= customdata; 00224 steve->free= free; 00225 00226 if(steve->running) { 00227 /* signal job to end */ 00228 steve->stop= 1; 00229 } 00230 } 00231 00232 void WM_jobs_timer(wmJob *steve, double timestep, unsigned int note, unsigned int endnote) 00233 { 00234 steve->timestep = timestep; 00235 steve->note = note; 00236 steve->endnote = endnote; 00237 } 00238 00239 void WM_jobs_callbacks(wmJob *steve, 00240 void (*startjob)(void *, short *, short *, float *), 00241 void (*initjob)(void *), 00242 void (*update)(void *), 00243 void (*endjob)(void *)) 00244 { 00245 steve->startjob= startjob; 00246 steve->initjob= initjob; 00247 steve->update= update; 00248 steve->endjob= endjob; 00249 } 00250 00251 static void *do_job_thread(void *job_v) 00252 { 00253 wmJob *steve= job_v; 00254 00255 steve->startjob(steve->run_customdata, &steve->stop, &steve->do_update, &steve->progress); 00256 steve->ready= 1; 00257 00258 return NULL; 00259 } 00260 00261 /* dont allow same startjob to be executed twice */ 00262 static void wm_jobs_test_suspend_stop(wmWindowManager *wm, wmJob *test) 00263 { 00264 wmJob *steve; 00265 int suspend= 0; 00266 00267 /* job added with suspend flag, we wait 1 timer step before activating it */ 00268 if(test->flag & WM_JOB_SUSPEND) { 00269 suspend= 1; 00270 test->flag &= ~WM_JOB_SUSPEND; 00271 } 00272 else { 00273 /* check other jobs */ 00274 for(steve= wm->jobs.first; steve; steve= steve->next) { 00275 /* obvious case, no test needed */ 00276 if(steve==test || !steve->running) continue; 00277 00278 /* if new job is not render, then check for same startjob */ 00279 if(0==(test->flag & WM_JOB_EXCL_RENDER)) 00280 if(steve->startjob!=test->startjob) 00281 continue; 00282 00283 /* if new job is render, any render job should be stopped */ 00284 if(test->flag & WM_JOB_EXCL_RENDER) 00285 if(0==(steve->flag & WM_JOB_EXCL_RENDER)) 00286 continue; 00287 00288 suspend= 1; 00289 00290 /* if this job has higher priority, stop others */ 00291 if(test->flag & WM_JOB_PRIORITY) { 00292 steve->stop= 1; 00293 // printf("job stopped: %s\n", steve->name); 00294 } 00295 } 00296 } 00297 00298 /* possible suspend ourselfs, waiting for other jobs, or de-suspend */ 00299 test->suspended= suspend; 00300 // if(suspend) printf("job suspended: %s\n", test->name); 00301 } 00302 00303 /* if job running, the same owner gave it a new job */ 00304 /* if different owner starts existing startjob, it suspends itself */ 00305 void WM_jobs_start(wmWindowManager *wm, wmJob *steve) 00306 { 00307 if(steve->running) { 00308 /* signal job to end and restart */ 00309 steve->stop= 1; 00310 // printf("job started a running job, ending... %s\n", steve->name); 00311 } 00312 else { 00313 00314 if(steve->customdata && steve->startjob) { 00315 00316 wm_jobs_test_suspend_stop(wm, steve); 00317 00318 if(steve->suspended==0) { 00319 /* copy to ensure proper free in end */ 00320 steve->run_customdata= steve->customdata; 00321 steve->run_free= steve->free; 00322 steve->free= NULL; 00323 steve->customdata= NULL; 00324 steve->running= 1; 00325 00326 if(steve->initjob) 00327 steve->initjob(steve->run_customdata); 00328 00329 steve->stop= 0; 00330 steve->ready= 0; 00331 steve->progress= 0.0; 00332 00333 // printf("job started: %s\n", steve->name); 00334 00335 BLI_init_threads(&steve->threads, do_job_thread, 1); 00336 BLI_insert_thread(&steve->threads, steve); 00337 } 00338 00339 /* restarted job has timer already */ 00340 if(steve->wt==NULL) 00341 steve->wt= WM_event_add_timer(wm, steve->win, TIMERJOBS, steve->timestep); 00342 } 00343 else printf("job fails, not initialized\n"); 00344 } 00345 } 00346 00347 /* stop job, free data completely */ 00348 static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *steve) 00349 { 00350 if(steve->running) { 00351 /* signal job to end */ 00352 steve->stop= 1; 00353 BLI_end_threads(&steve->threads); 00354 00355 if(steve->endjob) 00356 steve->endjob(steve->run_customdata); 00357 } 00358 00359 if(steve->wt) 00360 WM_event_remove_timer(wm, steve->win, steve->wt); 00361 if(steve->customdata) 00362 steve->free(steve->customdata); 00363 if(steve->run_customdata) 00364 steve->run_free(steve->run_customdata); 00365 00366 /* remove steve */ 00367 BLI_remlink(&wm->jobs, steve); 00368 MEM_freeN(steve); 00369 00370 } 00371 00372 void WM_jobs_stop_all(wmWindowManager *wm) 00373 { 00374 wmJob *steve; 00375 00376 while((steve= wm->jobs.first)) 00377 wm_jobs_kill_job(wm, steve); 00378 00379 } 00380 00381 /* signal job(s) from this owner or callback to stop, timer is required to get handled */ 00382 void WM_jobs_stop(wmWindowManager *wm, void *owner, void *startjob) 00383 { 00384 wmJob *steve; 00385 00386 for(steve= wm->jobs.first; steve; steve= steve->next) 00387 if(steve->owner==owner || steve->startjob==startjob) 00388 if(steve->running) 00389 steve->stop= 1; 00390 } 00391 00392 /* actually terminate thread and job timer */ 00393 void WM_jobs_kill(wmWindowManager *wm, void *owner, void (*startjob)(void *, short int *, short int *, float *)) 00394 { 00395 wmJob *steve; 00396 00397 steve= wm->jobs.first; 00398 while(steve) { 00399 if(steve->owner==owner || steve->startjob==startjob) { 00400 wmJob* bill = steve; 00401 steve= steve->next; 00402 wm_jobs_kill_job(wm, bill); 00403 } else { 00404 steve= steve->next; 00405 } 00406 } 00407 } 00408 00409 00410 /* kill job entirely, also removes timer itself */ 00411 void wm_jobs_timer_ended(wmWindowManager *wm, wmTimer *wt) 00412 { 00413 wmJob *steve; 00414 00415 for(steve= wm->jobs.first; steve; steve= steve->next) { 00416 if(steve->wt==wt) { 00417 wm_jobs_kill_job(wm, steve); 00418 return; 00419 } 00420 } 00421 } 00422 00423 /* hardcoded to event TIMERJOBS */ 00424 void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) 00425 { 00426 wmJob *steve= wm->jobs.first, *stevenext; 00427 float total_progress= 0.f; 00428 float jobs_progress=0; 00429 00430 00431 for(; steve; steve= stevenext) { 00432 stevenext= steve->next; 00433 00434 if(steve->wt==wt) { 00435 00436 /* running threads */ 00437 if(steve->threads.first) { 00438 00439 /* always call note and update when ready */ 00440 if(steve->do_update || steve->ready) { 00441 if(steve->update) 00442 steve->update(steve->run_customdata); 00443 if(steve->note) 00444 WM_event_add_notifier(C, steve->note, NULL); 00445 00446 if (steve->flag & WM_JOB_PROGRESS) 00447 WM_event_add_notifier(C, NC_WM|ND_JOB, NULL); 00448 steve->do_update= 0; 00449 } 00450 00451 if(steve->ready) { 00452 if(steve->endjob) 00453 steve->endjob(steve->run_customdata); 00454 00455 /* free own data */ 00456 steve->run_free(steve->run_customdata); 00457 steve->run_customdata= NULL; 00458 steve->run_free= NULL; 00459 00460 // if(steve->stop) printf("job ready but stopped %s\n", steve->name); 00461 // else printf("job finished %s\n", steve->name); 00462 00463 steve->running= 0; 00464 BLI_end_threads(&steve->threads); 00465 00466 if(steve->endnote) 00467 WM_event_add_notifier(C, steve->endnote, NULL); 00468 00469 WM_event_add_notifier(C, NC_WM|ND_JOB, NULL); 00470 00471 /* new job added for steve? */ 00472 if(steve->customdata) { 00473 // printf("job restarted with new data %s\n", steve->name); 00474 WM_jobs_start(wm, steve); 00475 } 00476 else { 00477 WM_event_remove_timer(wm, steve->win, steve->wt); 00478 steve->wt= NULL; 00479 00480 /* remove steve */ 00481 BLI_remlink(&wm->jobs, steve); 00482 MEM_freeN(steve); 00483 } 00484 } else if (steve->flag & WM_JOB_PROGRESS) { 00485 /* accumulate global progress for running jobs */ 00486 jobs_progress++; 00487 total_progress += steve->progress; 00488 } 00489 } 00490 else if(steve->suspended) { 00491 WM_jobs_start(wm, steve); 00492 } 00493 } 00494 else if(steve->threads.first && !steve->ready) { 00495 if(steve->flag & WM_JOB_PROGRESS) { 00496 /* accumulate global progress for running jobs */ 00497 jobs_progress++; 00498 total_progress += steve->progress; 00499 } 00500 } 00501 } 00502 00503 /* on file load 'winactive' can be NULL, possibly it should not happen but for now do a NULL check - campbell */ 00504 if(wm->winactive) { 00505 /* if there are running jobs, set the global progress indicator */ 00506 if (jobs_progress > 0) { 00507 float progress = total_progress / (float)jobs_progress; 00508 WM_progress_set(wm->winactive, progress); 00509 } else { 00510 WM_progress_clear(wm->winactive); 00511 } 00512 } 00513 } 00514 00515 int WM_jobs_has_running(wmWindowManager *wm) 00516 { 00517 wmJob *steve; 00518 00519 for(steve= wm->jobs.first; steve; steve= steve->next) 00520 if(steve->running) 00521 return 1; 00522 00523 return 0; 00524 }