remove unused includes UI_*.h, WM_*.h, ED_*.h
[blender-staging.git] / source / blender / windowmanager / intern / wm_jobs.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
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. 
10  *
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.
15  *
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.
19  *
20  * The Original Code is Copyright (C) 2009 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include "DNA_windowmanager_types.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_blenlib.h"
34 #include "BLI_threads.h"
35
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"
41 #include "BKE_main.h"
42 #include "BKE_report.h"
43
44 #include "WM_api.h"
45 #include "WM_types.h"
46 #include "wm_window.h"
47 #include "wm_event_system.h"
48 #include "wm_event_types.h"
49 #include "wm.h"
50
51
52
53 /* ********************** Threaded Jobs Manager ****************************** */
54
55 /*
56 Add new job
57 - register in WM
58 - configure callbacks
59
60 Start or re-run job
61 - if job running
62   - signal job to end
63   - add timer notifier to verify when it has ended, to start it
64 - else
65   - start job
66   - add timer notifier to handle progress
67
68 Stop job
69   - signal job to end
70         on end, job will tag itself as sleeping
71
72 Remove job
73 - signal job to end
74         on end, job will remove itself
75
76 When job is done:
77 - it puts timer to sleep (or removes?)
78
79  */
80  
81 struct wmJob {
82         struct wmJob *next, *prev;
83         
84         /* job originating from, keep track of this when deleting windows */
85         wmWindow *win;
86         
87         /* should store entire own context, for start, update, free */
88         void *customdata;
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 */
97         void (*free)(void *);
98         
99         /* running jobs each have own timer */
100         double timestep;
101         wmTimer *wt;
102         /* the notifier event timers should send */
103         unsigned int note, endnote;
104         
105         
106 /* internal */
107         void *owner;
108         int flag;
109         short suspended, running, ready, do_update, stop;
110         
111         /* once running, we store this separately */
112         void *run_customdata;
113         void (*run_free)(void *);
114         
115         /* we use BLI_threads api, but per job only 1 thread runs */
116         ListBase threads;
117
118 };
119
120 /* ******************* public API ***************** */
121
122 /* returns current or adds new job, but doesnt run it */
123 /* every owner only gets a single job, adding a new one will stop running stop and 
124    when stopped it starts the new one */
125 wmJob *WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, int flag)
126 {
127         wmJob *steve;
128         
129         for(steve= wm->jobs.first; steve; steve= steve->next)
130                 if(steve->owner==owner)
131                         break;
132         
133         if(steve==NULL) {
134                 steve= MEM_callocN(sizeof(wmJob), "new job");
135         
136                 BLI_addtail(&wm->jobs, steve);
137                 steve->win= win;
138                 steve->owner= owner;
139                 steve->flag= flag;
140         }
141         
142         return steve;
143 }
144
145 /* returns true if job runs, for UI (progress) indicators */
146 int WM_jobs_test(wmWindowManager *wm, void *owner)
147 {
148         wmJob *steve;
149         
150         for(steve= wm->jobs.first; steve; steve= steve->next)
151                 if(steve->owner==owner)
152                         if(steve->running)
153                                 return 1;
154         return 0;
155 }
156
157 void WM_jobs_customdata(wmJob *steve, void *customdata, void (*free)(void *))
158 {
159         /* pending job? just free */
160         if(steve->customdata)
161                 steve->free(steve->customdata);
162         
163         steve->customdata= customdata;
164         steve->free= free;
165
166         if(steve->running) {
167                 /* signal job to end */
168                 steve->stop= 1;
169         }
170 }
171
172 void WM_jobs_timer(wmJob *steve, double timestep, unsigned int note, unsigned int endnote)
173 {
174         steve->timestep = timestep;
175         steve->note = note;
176         steve->endnote = endnote;
177 }
178
179 void WM_jobs_callbacks(wmJob *steve, 
180                                            void (*startjob)(void *, short *, short *),
181                                            void (*initjob)(void *),
182                                            void (*update)(void  *))
183 {
184         steve->startjob= startjob;
185         steve->initjob= initjob;
186         steve->update= update;
187 }
188
189 static void *do_job_thread(void *job_v)
190 {
191         wmJob *steve= job_v;
192         
193         steve->startjob(steve->run_customdata, &steve->stop, &steve->do_update);
194         steve->ready= 1;
195         
196         return NULL;
197 }
198
199 /* dont allow same startjob to be executed twice */
200 static void wm_jobs_test_suspend_stop(wmWindowManager *wm, wmJob *test)
201 {
202         wmJob *steve;
203         int suspend= 0;
204         
205         for(steve= wm->jobs.first; steve; steve= steve->next) {
206                 if(steve==test || !steve->running) continue;
207                 if(steve->startjob!=test->startjob && !(test->flag & WM_JOB_EXCL_RENDER)) continue;
208                 if((test->flag & WM_JOB_EXCL_RENDER) && !(steve->flag & WM_JOB_EXCL_RENDER)) continue;
209
210                 suspend= 1;
211
212                 /* if this job has higher priority, stop others */
213                 if(test->flag & WM_JOB_PRIORITY)
214                         steve->stop= 1;
215         }
216
217         /* possible suspend ourselfs, waiting for other jobs, or de-suspend */
218         test->suspended= suspend;
219 }
220
221 /* if job running, the same owner gave it a new job */
222 /* if different owner starts existing startjob, it suspends itself */
223 void WM_jobs_start(wmWindowManager *wm, wmJob *steve)
224 {
225         if(steve->running) {
226                 /* signal job to end and restart */
227                 steve->stop= 1;
228         }
229         else {
230                 if(steve->customdata && steve->startjob) {
231                         
232                         wm_jobs_test_suspend_stop(wm, steve);
233                         
234                         if(steve->suspended==0) {
235                                 /* copy to ensure proper free in end */
236                                 steve->run_customdata= steve->customdata;
237                                 steve->run_free= steve->free;
238                                 steve->free= NULL;
239                                 steve->customdata= NULL;
240                                 steve->running= 1;
241                                 
242                                 if(steve->initjob)
243                                         steve->initjob(steve->run_customdata);
244                                 
245                                 steve->stop= 0;
246                                 steve->ready= 0;
247
248                                 BLI_init_threads(&steve->threads, do_job_thread, 1);
249                                 BLI_insert_thread(&steve->threads, steve);
250
251                                 // printf("job started\n");
252                         }
253                         
254                         /* restarted job has timer already */
255                         if(steve->wt==NULL)
256                                 steve->wt= WM_event_add_timer(wm, steve->win, TIMERJOBS, steve->timestep);
257                 }
258                 else printf("job fails, not initialized\n");
259         }
260 }
261
262 /* stop job, free data completely */
263 static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *steve)
264 {
265         if(steve->running) {
266                 /* signal job to end */
267                 steve->stop= 1;
268                 BLI_end_threads(&steve->threads);
269         }
270         
271         if(steve->wt)
272                 WM_event_remove_timer(wm, steve->win, steve->wt);
273         if(steve->customdata)
274                 steve->free(steve->customdata);
275         if(steve->run_customdata)
276                 steve->run_free(steve->run_customdata);
277         
278         /* remove steve */
279         BLI_remlink(&wm->jobs, steve);
280         MEM_freeN(steve);
281         
282 }
283
284 void WM_jobs_stop_all(wmWindowManager *wm)
285 {
286         wmJob *steve;
287         
288         while((steve= wm->jobs.first))
289                 wm_jobs_kill_job(wm, steve);
290         
291 }
292
293 /* signal job(s) from this owner to stop, timer is required to get handled */
294 void WM_jobs_stop(wmWindowManager *wm, void *owner)
295 {
296         wmJob *steve;
297         
298         for(steve= wm->jobs.first; steve; steve= steve->next)
299                 if(steve->owner==owner)
300                         if(steve->running)
301                                 steve->stop= 1;
302 }
303
304 /* actually terminate thread and job timer */
305 void WM_jobs_kill(wmWindowManager *wm, void *owner)
306 {
307         wmJob *steve;
308         
309         for(steve= wm->jobs.first; steve; steve= steve->next)
310                 if(steve->owner==owner)
311                         break;
312         
313         if (steve) 
314                 wm_jobs_kill_job(wm, steve);
315 }
316
317
318 /* kill job entirely, also removes timer itself */
319 void wm_jobs_timer_ended(wmWindowManager *wm, wmTimer *wt)
320 {
321         wmJob *steve;
322         
323         for(steve= wm->jobs.first; steve; steve= steve->next) {
324                 if(steve->wt==wt) {
325                         wm_jobs_kill_job(wm, steve);
326                         return;
327                 }
328         }
329 }
330
331 /* hardcoded to event TIMERJOBS */
332 void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt)
333 {
334         wmJob *steve= wm->jobs.first, *stevenext;
335         
336         for(; steve; steve= stevenext) {
337                 stevenext= steve->next;
338                 
339                 if(steve->wt==wt) {
340                         
341                         /* running threads */
342                         if(steve->threads.first) {
343                                 
344                                 /* always call note and update when ready */
345                                 if(steve->do_update || steve->ready) {
346                                         if(steve->update)
347                                                 steve->update(steve->run_customdata);
348                                         if(steve->note)
349                                                 WM_event_add_notifier(C, steve->note, NULL);
350                                         steve->do_update= 0;
351                                 }       
352                                 
353                                 if(steve->ready) {
354                                         /* free own data */
355                                         steve->run_free(steve->run_customdata);
356                                         steve->run_customdata= NULL;
357                                         steve->run_free= NULL;
358                                         
359                                         //      if(steve->stop) printf("job stopped\n");
360                                         //      else printf("job finished\n");
361
362                                         steve->running= 0;
363                                         BLI_end_threads(&steve->threads);
364                                         
365                                         if(steve->endnote)
366                                                 WM_event_add_notifier(C, steve->endnote, NULL);
367                                         
368                                         /* new job added for steve? */
369                                         if(steve->customdata) {
370                                                 WM_jobs_start(wm, steve);
371                                         }
372                                         else {
373                                                 WM_event_remove_timer(wm, steve->win, steve->wt);
374                                                 steve->wt= NULL;
375                                                 
376                                                 /* remove steve */
377                                                 BLI_remlink(&wm->jobs, steve);
378                                                 MEM_freeN(steve);
379                                         }
380                                 }
381                         }
382                         else if(steve->suspended) {
383                                 WM_jobs_start(wm, steve);
384                         }
385                 }
386         }
387 }
388