126
127
/* we use BLI_threads api, but per job only 1 thread runs */
127
128
ListBase threads;
132
* 1st priority: job with same owner and name
133
* 2nd priority: job with same owner
134
* if type, compare for it, otherwise any matching job
135
static wmJob *wm_job_find(wmWindowManager *wm, void *owner, const char *name)
136
static wmJob *wm_job_find(wmWindowManager *wm, void *owner, const int job_type)
137
wmJob *steve, *found = NULL;
139
for (steve = wm->jobs.first; steve; steve = steve->next)
140
if (steve->owner == owner) {
142
if (name && strcmp(steve->name, name) == 0)
140
for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next)
141
if (wm_job->owner == owner) {
144
if ( wm_job->job_type == job_type)
149
154
/* ******************* public API ***************** */
151
156
/* returns current or adds new job, but doesnt run it */
152
/* every owner only gets a single job, adding a new one will stop running stop and
157
/* every owner only gets a single job, adding a new one will stop running job and
153
158
* when stopped it starts the new one */
154
wmJob *WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, const char *name, int flag)
159
wmJob *WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, const char *name, int flag, int job_type)
156
wmJob *steve = wm_job_find(wm, owner, name);
159
steve = MEM_callocN(sizeof(wmJob), "new job");
161
BLI_addtail(&wm->jobs, steve);
163
steve->owner = owner;
165
BLI_strncpy(steve->name, name, sizeof(steve->name));
161
wmJob *wm_job = wm_job_find(wm, owner, job_type);
163
if (wm_job == NULL) {
164
wm_job = MEM_callocN(sizeof(wmJob), "new job");
166
BLI_addtail(&wm->jobs, wm_job);
168
wm_job->owner = owner;
170
wm_job->job_type = job_type;
171
BLI_strncpy(wm_job->name, name, sizeof(wm_job->name));
173
/* else: a running job, be careful */
175
/* prevent creating a job with an invalid type */
176
BLI_assert(wm_job->job_type != WM_JOB_TYPE_ANY);
171
181
/* returns true if job runs, for UI (progress) indicators */
172
int WM_jobs_test(wmWindowManager *wm, void *owner)
182
int WM_jobs_test(wmWindowManager *wm, void *owner, int job_type)
176
186
/* job can be running or about to run (suspended) */
177
for (steve = wm->jobs.first; steve; steve = steve->next)
178
if (steve->owner == owner)
179
if (steve->running || steve->suspended)
187
for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next) {
188
if (wm_job->owner == owner) {
189
if (job_type == WM_JOB_TYPE_ANY || (wm_job->job_type == job_type)) {
190
if (wm_job->running || wm_job->suspended) {
185
200
float WM_jobs_progress(wmWindowManager *wm, void *owner)
187
wmJob *steve = wm_job_find(wm, owner, NULL);
202
wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
189
if (steve && steve->flag & WM_JOB_PROGRESS)
190
return steve->progress;
204
if (wm_job && wm_job->flag & WM_JOB_PROGRESS)
205
return wm_job->progress;
195
210
char *WM_jobs_name(wmWindowManager *wm, void *owner)
197
wmJob *steve = wm_job_find(wm, owner, NULL);
212
wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
205
int WM_jobs_is_running(wmJob *steve)
220
int WM_jobs_is_running(wmJob *wm_job)
207
return steve->running;
222
return wm_job->running;
210
void *WM_jobs_get_customdata(wmJob *steve)
225
void *WM_jobs_customdata_get(wmJob *wm_job)
212
if (!steve->customdata) {
213
return steve->run_customdata;
227
if (!wm_job->customdata) {
228
return wm_job->run_customdata;
216
return steve->customdata;
231
return wm_job->customdata;
220
void WM_jobs_customdata(wmJob *steve, void *customdata, void (*free)(void *))
235
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void (*free)(void *))
222
237
/* pending job? just free */
223
if (steve->customdata)
224
steve->free(steve->customdata);
238
if (wm_job->customdata)
239
wm_job->free(wm_job->customdata);
226
steve->customdata = customdata;
241
wm_job->customdata = customdata;
229
if (steve->running) {
244
if (wm_job->running) {
230
245
/* signal job to end */
235
void WM_jobs_timer(wmJob *steve, double timestep, unsigned int note, unsigned int endnote)
250
void WM_jobs_timer(wmJob *wm_job, double timestep, unsigned int note, unsigned int endnote)
237
steve->timestep = timestep;
239
steve->endnote = endnote;
252
wm_job->timestep = timestep;
254
wm_job->endnote = endnote;
242
void WM_jobs_callbacks(wmJob *steve,
257
void WM_jobs_callbacks(wmJob *wm_job,
243
258
void (*startjob)(void *, short *, short *, float *),
244
259
void (*initjob)(void *),
245
260
void (*update)(void *),
246
261
void (*endjob)(void *))
248
steve->startjob = startjob;
249
steve->initjob = initjob;
250
steve->update = update;
251
steve->endjob = endjob;
263
wm_job->startjob = startjob;
264
wm_job->initjob = initjob;
265
wm_job->update = update;
266
wm_job->endjob = endjob;
254
269
static void *do_job_thread(void *job_v)
256
wmJob *steve = job_v;
271
wmJob *wm_job = job_v;
258
steve->startjob(steve->run_customdata, &steve->stop, &steve->do_update, &steve->progress);
273
wm_job->startjob(wm_job->run_customdata, &wm_job->stop, &wm_job->do_update, &wm_job->progress);
274
wm_job->ready = TRUE;
264
279
/* don't allow same startjob to be executed twice */
265
280
static void wm_jobs_test_suspend_stop(wmWindowManager *wm, wmJob *test)
270
285
/* job added with suspend flag, we wait 1 timer step before activating it */
271
286
if (test->flag & WM_JOB_SUSPEND) {
273
288
test->flag &= ~WM_JOB_SUSPEND;
276
291
/* check other jobs */
277
for (steve = wm->jobs.first; steve; steve = steve->next) {
292
for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next) {
278
293
/* obvious case, no test needed */
279
if (steve == test || !steve->running) continue;
294
if (wm_job == test || !wm_job->running) {
281
298
/* if new job is not render, then check for same startjob */
282
299
if (0 == (test->flag & WM_JOB_EXCL_RENDER))
283
if (steve->startjob != test->startjob)
300
if (wm_job->startjob != test->startjob)
286
303
/* if new job is render, any render job should be stopped */
287
304
if (test->flag & WM_JOB_EXCL_RENDER)
288
if (0 == (steve->flag & WM_JOB_EXCL_RENDER))
305
if (0 == (wm_job->flag & WM_JOB_EXCL_RENDER))
293
310
/* if this job has higher priority, stop others */
294
311
if (test->flag & WM_JOB_PRIORITY) {
296
// printf("job stopped: %s\n", steve->name);
313
// printf("job stopped: %s\n", wm_job->name);
306
323
/* if job running, the same owner gave it a new job */
307
324
/* if different owner starts existing startjob, it suspends itself */
308
void WM_jobs_start(wmWindowManager *wm, wmJob *steve)
325
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
310
if (steve->running) {
327
if (wm_job->running) {
311
328
/* signal job to end and restart */
313
// printf("job started a running job, ending... %s\n", steve->name);
330
// printf("job started a running job, ending... %s\n", wm_job->name);
317
if (steve->customdata && steve->startjob) {
319
wm_jobs_test_suspend_stop(wm, steve);
321
if (steve->suspended == 0) {
334
if (wm_job->customdata && wm_job->startjob) {
336
wm_jobs_test_suspend_stop(wm, wm_job);
338
if (wm_job->suspended == FALSE) {
322
339
/* copy to ensure proper free in end */
323
steve->run_customdata = steve->customdata;
324
steve->run_free = steve->free;
326
steve->customdata = NULL;
330
steve->initjob(steve->run_customdata);
334
steve->progress = 0.0;
340
wm_job->run_customdata = wm_job->customdata;
341
wm_job->run_free = wm_job->free;
343
wm_job->customdata = NULL;
344
wm_job->running = TRUE;
347
wm_job->initjob(wm_job->run_customdata);
349
wm_job->stop = FALSE;
350
wm_job->ready = FALSE;
351
wm_job->progress = 0.0;
336
// printf("job started: %s\n", steve->name);
353
// printf("job started: %s\n", wm_job->name);
338
BLI_init_threads(&steve->threads, do_job_thread, 1);
339
BLI_insert_thread(&steve->threads, steve);
355
BLI_init_threads(&wm_job->threads, do_job_thread, 1);
356
BLI_insert_thread(&wm_job->threads, wm_job);
342
359
/* restarted job has timer already */
343
if (steve->wt == NULL)
344
steve->wt = WM_event_add_timer(wm, steve->win, TIMERJOBS, steve->timestep);
360
if (wm_job->wt == NULL)
361
wm_job->wt = WM_event_add_timer(wm, wm_job->win, TIMERJOBS, wm_job->timestep);
363
if (G.debug & G_DEBUG_JOBS)
364
wm_job->start_time = PIL_check_seconds_timer();
346
366
else printf("job fails, not initialized\n");
350
/* stop job, free data completely */
351
static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *steve)
370
/* stop job, end thread, free data completely */
371
static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job)
353
if (steve->running) {
373
if (wm_job->running) {
354
374
/* signal job to end */
356
BLI_end_threads(&steve->threads);
359
steve->endjob(steve->run_customdata);
363
WM_event_remove_timer(wm, steve->win, steve->wt);
364
if (steve->customdata)
365
steve->free(steve->customdata);
366
if (steve->run_customdata)
367
steve->run_free(steve->run_customdata);
370
BLI_remlink(&wm->jobs, steve);
375
void WM_jobs_stop_all(wmWindowManager *wm)
379
while ((steve = wm->jobs.first))
380
wm_jobs_kill_job(wm, steve);
376
BLI_end_threads(&wm_job->threads);
379
wm_job->endjob(wm_job->run_customdata);
383
WM_event_remove_timer(wm, wm_job->win, wm_job->wt);
384
if (wm_job->customdata)
385
wm_job->free(wm_job->customdata);
386
if (wm_job->run_customdata)
387
wm_job->run_free(wm_job->run_customdata);
390
BLI_remlink(&wm->jobs, wm_job);
395
/* wait until every job ended */
396
void WM_jobs_kill_all(wmWindowManager *wm)
400
while ((wm_job = wm->jobs.first))
401
wm_jobs_kill_job(wm, wm_job);
405
/* wait until every job ended, except for one owner (used in undo to keep screen job alive) */
406
void WM_jobs_kill_all_except(wmWindowManager *wm, void *owner)
408
wmJob *wm_job, *next_job;
410
for (wm_job = wm->jobs.first; wm_job; wm_job = next_job) {
411
next_job = wm_job->next;
413
if (wm_job->owner != owner)
414
wm_jobs_kill_job(wm, wm_job);
419
void WM_jobs_kill_type(struct wmWindowManager *wm, int job_type)
421
wmJob *wm_job, *next_job;
423
for (wm_job = wm->jobs.first; wm_job; wm_job = next_job) {
424
next_job = wm_job->next;
426
if (wm_job->job_type == job_type)
427
wm_jobs_kill_job(wm, wm_job);
384
431
/* signal job(s) from this owner or callback to stop, timer is required to get handled */
385
432
void WM_jobs_stop(wmWindowManager *wm, void *owner, void *startjob)
389
for (steve = wm->jobs.first; steve; steve = steve->next)
390
if (steve->owner == owner || steve->startjob == startjob)
436
for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next) {
437
if (wm_job->owner == owner || wm_job->startjob == startjob) {
438
if (wm_job->running) {
395
445
/* actually terminate thread and job timer */
396
446
void WM_jobs_kill(wmWindowManager *wm, void *owner, void (*startjob)(void *, short int *, short int *, float *))
400
steve = wm->jobs.first;
402
if (steve->owner == owner || steve->startjob == startjob) {
450
wm_job = wm->jobs.first;
452
if (wm_job->owner == owner || wm_job->startjob == startjob) {
453
wmJob *bill = wm_job;
454
wm_job = wm_job->next;
405
455
wm_jobs_kill_job(wm, bill);
458
wm_job = wm_job->next;
427
477
/* hardcoded to event TIMERJOBS */
428
478
void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt)
430
wmJob *steve = wm->jobs.first, *stevenext;
480
wmJob *wm_job, *wm_jobnext;
431
481
float total_progress = 0.f;
432
482
float jobs_progress = 0;
435
for (; steve; steve = stevenext) {
436
stevenext = steve->next;
485
for (wm_job = wm->jobs.first; wm_job; wm_job = wm_jobnext) {
486
wm_jobnext = wm_job->next;
438
if (steve->wt == wt) {
488
if (wm_job->wt == wt) {
440
490
/* running threads */
441
if (steve->threads.first) {
491
if (wm_job->threads.first) {
443
493
/* always call note and update when ready */
444
if (steve->do_update || steve->ready) {
446
steve->update(steve->run_customdata);
448
WM_event_add_notifier(C, steve->note, NULL);
494
if (wm_job->do_update || wm_job->ready) {
496
wm_job->update(wm_job->run_customdata);
498
WM_event_add_notifier(C, wm_job->note, NULL);
450
if (steve->flag & WM_JOB_PROGRESS)
500
if (wm_job->flag & WM_JOB_PROGRESS)
451
501
WM_event_add_notifier(C, NC_WM | ND_JOB, NULL);
452
steve->do_update = 0;
502
wm_job->do_update = FALSE;
457
steve->endjob(steve->run_customdata);
507
wm_job->endjob(wm_job->run_customdata);
459
509
/* free own data */
460
steve->run_free(steve->run_customdata);
461
steve->run_customdata = NULL;
462
steve->run_free = NULL;
464
// if (steve->stop) printf("job ready but stopped %s\n", steve->name);
465
// else printf("job finished %s\n", steve->name);
468
BLI_end_threads(&steve->threads);
471
WM_event_add_notifier(C, steve->endnote, NULL);
510
wm_job->run_free(wm_job->run_customdata);
511
wm_job->run_customdata = NULL;
512
wm_job->run_free = NULL;
514
// if (wm_job->stop) printf("job ready but stopped %s\n", wm_job->name);
515
// else printf("job finished %s\n", wm_job->name);
517
if (G.debug & G_DEBUG_JOBS) {
518
printf("Job '%s' finished in %f seconds\n", wm_job->name,
519
PIL_check_seconds_timer() - wm_job->start_time);
522
wm_job->running = FALSE;
523
BLI_end_threads(&wm_job->threads);
526
WM_event_add_notifier(C, wm_job->endnote, NULL);
473
528
WM_event_add_notifier(C, NC_WM | ND_JOB, NULL);
475
/* new job added for steve? */
476
if (steve->customdata) {
477
// printf("job restarted with new data %s\n", steve->name);
478
WM_jobs_start(wm, steve);
530
/* so the info header updates on job end even if the mouse doesn't move.
531
* a rather annoying/obscure bug, see [#32537] (second reply) */
532
WM_event_add_mousemove_window(wm_job->win);
534
/* new job added for wm_job? */
535
if (wm_job->customdata) {
536
// printf("job restarted with new data %s\n", wm_job->name);
537
WM_jobs_start(wm, wm_job);
481
WM_event_remove_timer(wm, steve->win, steve->wt);
540
WM_event_remove_timer(wm, wm_job->win, wm_job->wt);
485
BLI_remlink(&wm->jobs, steve);
544
BLI_remlink(&wm->jobs, wm_job);
489
else if (steve->flag & WM_JOB_PROGRESS) {
548
else if (wm_job->flag & WM_JOB_PROGRESS) {
490
549
/* accumulate global progress for running jobs */
492
total_progress += steve->progress;
551
total_progress += wm_job->progress;
495
else if (steve->suspended) {
496
WM_jobs_start(wm, steve);
554
else if (wm_job->suspended) {
555
WM_jobs_start(wm, wm_job);
499
else if (steve->threads.first && !steve->ready) {
500
if (steve->flag & WM_JOB_PROGRESS) {
558
else if (wm_job->threads.first && !wm_job->ready) {
559
if (wm_job->flag & WM_JOB_PROGRESS) {
501
560
/* accumulate global progress for running jobs */
503
total_progress += steve->progress;
562
total_progress += wm_job->progress;