4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2009 Blender Foundation.
21 * All rights reserved.
24 * Contributor(s): Blender Foundation
26 * ***** END GPL LICENSE BLOCK *****
29 #include "DNA_windowmanager_types.h"
31 #include "MEM_guardedalloc.h"
33 #include "BLI_blenlib.h"
34 #include "BLI_threads.h"
36 #include "BKE_blender.h"
37 #include "BKE_context.h"
38 #include "BKE_idprop.h"
39 #include "BKE_global.h"
40 #include "BKE_library.h"
42 #include "BKE_report.h"
46 #include "wm_window.h"
47 #include "wm_event_system.h"
48 #include "wm_event_types.h"
53 /* ********************** Threaded Jobs Manager ****************************** */
63 - add timer notifier to verify when it has ended, to start it
66 - add timer notifier to handle progress
70 on end, job will tag itself as sleeping
74 on end, job will remove itself
77 - it puts timer to sleep (or removes?)
82 struct wmJob *next, *prev;
84 /* job originating from, keep track of this when deleting windows */
87 /* should store entire own context, for start, update, free */
89 /* to prevent cpu overhead, use this one which only gets called when job really starts, not in thread */
90 void (*initjob)(void *);
91 /* this runs inside thread, and does full job */
92 void (*startjob)(void *, short *stop, short *do_update);
93 /* update gets called if thread defines so, and max once per timerstep */
94 /* it runs outside thread, blocking blender, no drawing! */
95 void (*update)(void *);
96 /* free entire customdata, doesn't run in thread */
98 /* gets called when job is stopped, not in thread */
99 void (*endjob)(void *);
101 /* running jobs each have own timer */
104 /* the notifier event timers should send */
105 unsigned int note, endnote;
111 short suspended, running, ready, do_update, stop;
113 /* once running, we store this separately */
114 void *run_customdata;
115 void (*run_free)(void *);
117 /* we use BLI_threads api, but per job only 1 thread runs */
122 /* ******************* public API ***************** */
124 /* returns current or adds new job, but doesnt run it */
125 /* every owner only gets a single job, adding a new one will stop running stop and
126 when stopped it starts the new one */
127 wmJob *WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, int flag)
131 for(steve= wm->jobs.first; steve; steve= steve->next)
132 if(steve->owner==owner)
136 steve= MEM_callocN(sizeof(wmJob), "new job");
138 BLI_addtail(&wm->jobs, steve);
147 /* returns true if job runs, for UI (progress) indicators */
148 int WM_jobs_test(wmWindowManager *wm, void *owner)
152 for(steve= wm->jobs.first; steve; steve= steve->next)
153 if(steve->owner==owner)
159 void WM_jobs_customdata(wmJob *steve, void *customdata, void (*free)(void *))
161 /* pending job? just free */
162 if(steve->customdata)
163 steve->free(steve->customdata);
165 steve->customdata= customdata;
169 /* signal job to end */
174 void WM_jobs_timer(wmJob *steve, double timestep, unsigned int note, unsigned int endnote)
176 steve->timestep = timestep;
178 steve->endnote = endnote;
181 void WM_jobs_callbacks(wmJob *steve,
182 void (*startjob)(void *, short *, short *),
183 void (*initjob)(void *),
184 void (*update)(void *),
185 void (*endjob)(void *))
187 steve->startjob= startjob;
188 steve->initjob= initjob;
189 steve->update= update;
190 steve->endjob= endjob;
193 static void *do_job_thread(void *job_v)
197 steve->startjob(steve->run_customdata, &steve->stop, &steve->do_update);
203 /* dont allow same startjob to be executed twice */
204 static void wm_jobs_test_suspend_stop(wmWindowManager *wm, wmJob *test)
209 for(steve= wm->jobs.first; steve; steve= steve->next) {
210 if(steve==test || !steve->running) continue;
211 if(steve->startjob!=test->startjob && !(test->flag & WM_JOB_EXCL_RENDER)) continue;
212 if((test->flag & WM_JOB_EXCL_RENDER) && !(steve->flag & WM_JOB_EXCL_RENDER)) continue;
216 /* if this job has higher priority, stop others */
217 if(test->flag & WM_JOB_PRIORITY)
221 /* possible suspend ourselfs, waiting for other jobs, or de-suspend */
222 test->suspended= suspend;
225 /* if job running, the same owner gave it a new job */
226 /* if different owner starts existing startjob, it suspends itself */
227 void WM_jobs_start(wmWindowManager *wm, wmJob *steve)
230 /* signal job to end and restart */
234 if(steve->customdata && steve->startjob) {
236 wm_jobs_test_suspend_stop(wm, steve);
238 if(steve->suspended==0) {
239 /* copy to ensure proper free in end */
240 steve->run_customdata= steve->customdata;
241 steve->run_free= steve->free;
243 steve->customdata= NULL;
247 steve->initjob(steve->run_customdata);
252 BLI_init_threads(&steve->threads, do_job_thread, 1);
253 BLI_insert_thread(&steve->threads, steve);
255 // printf("job started\n");
258 /* restarted job has timer already */
260 steve->wt= WM_event_add_timer(wm, steve->win, TIMERJOBS, steve->timestep);
262 else printf("job fails, not initialized\n");
266 /* stop job, free data completely */
267 static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *steve)
270 /* signal job to end */
272 BLI_end_threads(&steve->threads);
275 steve->endjob(steve->run_customdata);
279 WM_event_remove_timer(wm, steve->win, steve->wt);
280 if(steve->customdata)
281 steve->free(steve->customdata);
282 if(steve->run_customdata)
283 steve->run_free(steve->run_customdata);
286 BLI_remlink(&wm->jobs, steve);
291 void WM_jobs_stop_all(wmWindowManager *wm)
295 while((steve= wm->jobs.first))
296 wm_jobs_kill_job(wm, steve);
300 /* signal job(s) from this owner to stop, timer is required to get handled */
301 void WM_jobs_stop(wmWindowManager *wm, void *owner)
305 for(steve= wm->jobs.first; steve; steve= steve->next)
306 if(steve->owner==owner)
311 /* actually terminate thread and job timer */
312 void WM_jobs_kill(wmWindowManager *wm, void *owner)
316 for(steve= wm->jobs.first; steve; steve= steve->next)
317 if(steve->owner==owner)
321 wm_jobs_kill_job(wm, steve);
325 /* kill job entirely, also removes timer itself */
326 void wm_jobs_timer_ended(wmWindowManager *wm, wmTimer *wt)
330 for(steve= wm->jobs.first; steve; steve= steve->next) {
332 wm_jobs_kill_job(wm, steve);
338 /* hardcoded to event TIMERJOBS */
339 void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt)
341 wmJob *steve= wm->jobs.first, *stevenext;
343 for(; steve; steve= stevenext) {
344 stevenext= steve->next;
348 /* running threads */
349 if(steve->threads.first) {
351 /* always call note and update when ready */
352 if(steve->do_update || steve->ready) {
354 steve->update(steve->run_customdata);
356 WM_event_add_notifier(C, steve->note, NULL);
362 steve->endjob(steve->run_customdata);
365 steve->run_free(steve->run_customdata);
366 steve->run_customdata= NULL;
367 steve->run_free= NULL;
369 // if(steve->stop) printf("job stopped\n");
370 // else printf("job finished\n");
373 BLI_end_threads(&steve->threads);
376 WM_event_add_notifier(C, steve->endnote, NULL);
378 /* new job added for steve? */
379 if(steve->customdata) {
380 WM_jobs_start(wm, steve);
383 WM_event_remove_timer(wm, steve->win, steve->wt);
387 BLI_remlink(&wm->jobs, steve);
392 else if(steve->suspended) {
393 WM_jobs_start(wm, steve);