Blender  V2.93
wm_jobs.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2009 Blender Foundation.
17  * All rights reserved.
18  */
19 
26 #include <string.h>
27 
29 
30 #include "MEM_guardedalloc.h"
31 
32 #include "BLI_blenlib.h"
33 #include "BLI_threads.h"
34 #include "BLI_utildefines.h"
35 
36 #include "BKE_context.h"
37 #include "BKE_global.h"
38 
39 #include "SEQ_prefetch.h"
40 
41 #include "WM_api.h"
42 #include "WM_types.h"
43 #include "wm.h"
44 #include "wm_event_types.h"
45 
46 #include "PIL_time.h"
47 
48 /*
49  * Add new job
50  * - register in WM
51  * - configure callbacks
52  *
53  * Start or re-run job
54  * - if job running
55  * - signal job to end
56  * - add timer notifier to verify when it has ended, to start it
57  * - else
58  * - start job
59  * - add timer notifier to handle progress
60  *
61  * Stop job
62  * - signal job to end
63  * on end, job will tag itself as sleeping
64  *
65  * Remove job
66  * - signal job to end
67  * on end, job will remove itself
68  *
69  * When job is done:
70  * - it puts timer to sleep (or removes?)
71  */
72 
73 struct wmJob {
74  struct wmJob *next, *prev;
75 
78 
80  void *customdata;
85  void (*initjob)(void *);
95  void (*update)(void *);
100  void (*free)(void *);
105  void (*endjob)(void *);
106 
108  double timestep;
113  unsigned int note, endnote;
114 
115  /* internal */
116  void *owner;
117  int flag;
119  float progress;
120 
122  char name[128];
123 
126  void (*run_free)(void *);
127 
130 
131  double start_time;
132 
136 };
137 
138 /* Main thread locking */
139 
141 {
143 }
144 
146 {
148 }
149 
150 static void wm_job_main_thread_yield(wmJob *wm_job)
151 {
152  /* unlock and lock the ticket mutex. because it's a fair mutex any job that
153  * is waiting to acquire the lock will get it first, before we can lock */
156 }
157 
161 static wmJob *wm_job_find(wmWindowManager *wm, void *owner, const int job_type)
162 {
163  if (owner && job_type) {
164  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
165  if (wm_job->owner == owner && wm_job->job_type == job_type) {
166  return wm_job;
167  }
168  }
169  }
170  else if (owner) {
171  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
172  if (wm_job->owner == owner) {
173  return wm_job;
174  }
175  }
176  }
177  else if (job_type) {
178  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
179  if (wm_job->job_type == job_type) {
180  return wm_job;
181  }
182  }
183  }
184 
185  return NULL;
186 }
187 
188 /* ******************* public API ***************** */
189 
197  wmWindowManager *wm, wmWindow *win, void *owner, const char *name, int flag, int job_type)
198 {
199  wmJob *wm_job = wm_job_find(wm, owner, job_type);
200 
201  if (wm_job == NULL) {
202  wm_job = MEM_callocN(sizeof(wmJob), "new job");
203 
204  BLI_addtail(&wm->jobs, wm_job);
205  wm_job->win = win;
206  wm_job->owner = owner;
207  wm_job->flag = flag;
208  wm_job->job_type = job_type;
209  BLI_strncpy(wm_job->name, name, sizeof(wm_job->name));
210 
213  }
214  /* else: a running job, be careful */
215 
216  /* prevent creating a job with an invalid type */
217  BLI_assert(wm_job->job_type != WM_JOB_TYPE_ANY);
218 
219  return wm_job;
220 }
221 
222 /* returns true if job runs, for UI (progress) indicators */
224 {
225  /* job can be running or about to run (suspended) */
226  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
227  if (wm_job->owner == owner) {
228  if (ELEM(job_type, WM_JOB_TYPE_ANY, wm_job->job_type)) {
229  if (wm_job->running || wm_job->suspended) {
230  return true;
231  }
232  }
233  }
234  }
235 
236  return false;
237 }
238 
240 {
241  wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
242 
243  if (wm_job && wm_job->flag & WM_JOB_PROGRESS) {
244  return wm_job->progress;
245  }
246 
247  return 0.0;
248 }
249 
251 {
252  float total_progress = 0.0f;
253  float jobs_progress = 0;
254 
255  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
256  if (wm_job->threads.first && !wm_job->ready) {
257  if (wm_job->flag & WM_JOB_PROGRESS) {
258  /* accumulate global progress for running jobs */
259  jobs_progress++;
260  total_progress += wm_job->progress;
261  }
262  }
263  }
264 
265  /* if there are running jobs, set the global progress indicator */
266  if (jobs_progress > 0) {
267  float progress = total_progress / (float)jobs_progress;
268 
269  LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
271  }
272  }
273  else {
274  LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
276  }
277  }
278 }
279 
280 /* time that job started */
282 {
283  wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
284 
285  if (wm_job && wm_job->flag & WM_JOB_PROGRESS) {
286  return wm_job->start_time;
287  }
288 
289  return 0;
290 }
291 
293 {
294  wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
295 
296  if (wm_job) {
297  return wm_job->name;
298  }
299 
300  return NULL;
301 }
302 
304 {
305  wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
306 
307  if (wm_job) {
308  return WM_jobs_customdata_get(wm_job);
309  }
310 
311  return NULL;
312 }
313 
315 {
316  wmJob *wm_job = wm_job_find(wm, NULL, job_type);
317 
318  if (wm_job) {
319  return WM_jobs_customdata_get(wm_job);
320  }
321 
322  return NULL;
323 }
324 
326 {
327  return wm_job->running;
328 }
329 
331 {
332  wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
333  return wm_job ? wm_job->stop : true; /* XXX to be redesigned properly. */
334 }
335 
337 {
338  if (!wm_job->customdata) {
339  return wm_job->run_customdata;
340  }
341  return wm_job->customdata;
342 }
343 
344 void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void (*free)(void *))
345 {
346  /* pending job? just free */
347  if (wm_job->customdata) {
348  wm_job->free(wm_job->customdata);
349  }
350 
351  wm_job->customdata = customdata;
352  wm_job->free = free;
353 
354  if (wm_job->running) {
355  /* signal job to end */
356  wm_job->stop = true;
357  }
358 }
359 
360 void WM_jobs_timer(wmJob *wm_job, double timestep, unsigned int note, unsigned int endnote)
361 {
362  wm_job->timestep = timestep;
363  wm_job->note = note;
364  wm_job->endnote = endnote;
365 }
366 
367 void WM_jobs_delay_start(wmJob *wm_job, double delay_time)
368 {
369  wm_job->start_delay_time = delay_time;
370 }
371 
374  void (*initjob)(void *),
375  void (*update)(void *),
376  void (*endjob)(void *))
377 {
378  wm_job->startjob = startjob;
379  wm_job->initjob = initjob;
380  wm_job->update = update;
381  wm_job->endjob = endjob;
382 }
383 
384 static void *do_job_thread(void *job_v)
385 {
386  wmJob *wm_job = job_v;
387 
389  wm_job->startjob(wm_job->run_customdata, &wm_job->stop, &wm_job->do_update, &wm_job->progress);
390  wm_job->ready = true;
391 
392  return NULL;
393 }
394 
395 /* don't allow same startjob to be executed twice */
397 {
398  bool suspend = false;
399 
400  /* job added with suspend flag, we wait 1 timer step before activating it */
401  if (test->start_delay_time > 0.0) {
402  suspend = true;
403  test->start_delay_time = 0.0;
404  }
405  else {
406  /* check other jobs */
407  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
408  /* obvious case, no test needed */
409  if (wm_job == test || !wm_job->running) {
410  continue;
411  }
412 
413  /* if new job is not render, then check for same startjob */
414  if (0 == (test->flag & WM_JOB_EXCL_RENDER)) {
415  if (wm_job->startjob != test->startjob) {
416  continue;
417  }
418  }
419 
420  /* if new job is render, any render job should be stopped */
421  if (test->flag & WM_JOB_EXCL_RENDER) {
422  if (0 == (wm_job->flag & WM_JOB_EXCL_RENDER)) {
423  continue;
424  }
425  }
426 
427  suspend = true;
428 
429  /* if this job has higher priority, stop others */
430  if (test->flag & WM_JOB_PRIORITY) {
431  wm_job->stop = true;
432  // printf("job stopped: %s\n", wm_job->name);
433  }
434  }
435  }
436 
437  /* Possible suspend ourselves, waiting for other jobs, or de-suspend. */
438  test->suspended = suspend;
439 #if 0
440  if (suspend) {
441  printf("job suspended: %s\n", test->name);
442  }
443 #endif
444 }
445 
451 {
452  if (wm_job->running) {
453  /* signal job to end and restart */
454  wm_job->stop = true;
455  // printf("job started a running job, ending... %s\n", wm_job->name);
456  }
457  else {
458 
459  if (wm_job->customdata && wm_job->startjob) {
460  const double timestep = (wm_job->start_delay_time > 0.0) ? wm_job->start_delay_time :
461  wm_job->timestep;
462 
463  wm_jobs_test_suspend_stop(wm, wm_job);
464 
465  if (wm_job->suspended == false) {
466  /* copy to ensure proper free in end */
467  wm_job->run_customdata = wm_job->customdata;
468  wm_job->run_free = wm_job->free;
469  wm_job->free = NULL;
470  wm_job->customdata = NULL;
471  wm_job->running = true;
472 
473  if (wm_job->initjob) {
474  wm_job->initjob(wm_job->run_customdata);
475  }
476 
477  wm_job->stop = false;
478  wm_job->ready = false;
479  wm_job->progress = 0.0;
480 
481  // printf("job started: %s\n", wm_job->name);
482 
484  BLI_threadpool_insert(&wm_job->threads, wm_job);
485  }
486 
487  /* restarted job has timer already */
488  if (wm_job->wt && (wm_job->wt->timestep > timestep)) {
489  WM_event_remove_timer(wm, wm_job->win, wm_job->wt);
490  wm_job->wt = WM_event_add_timer(wm, wm_job->win, TIMERJOBS, timestep);
491  }
492  if (wm_job->wt == NULL) {
493  wm_job->wt = WM_event_add_timer(wm, wm_job->win, TIMERJOBS, timestep);
494  }
495 
497  }
498  else {
499  printf("job fails, not initialized\n");
500  }
501  }
502 }
503 
504 static void wm_job_free(wmWindowManager *wm, wmJob *wm_job)
505 {
506  BLI_remlink(&wm->jobs, wm_job);
509  MEM_freeN(wm_job);
510 }
511 
512 /* stop job, end thread, free data completely */
513 static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job)
514 {
515  bool update_progress = (wm_job->flag & WM_JOB_PROGRESS) != 0;
516 
517  if (wm_job->running) {
518  /* signal job to end */
519  wm_job->stop = true;
520 
522  BLI_threadpool_end(&wm_job->threads);
524 
525  if (wm_job->endjob) {
526  wm_job->endjob(wm_job->run_customdata);
527  }
528  }
529 
530  if (wm_job->wt) {
531  WM_event_remove_timer(wm, wm_job->win, wm_job->wt);
532  }
533  if (wm_job->customdata) {
534  wm_job->free(wm_job->customdata);
535  }
536  if (wm_job->run_customdata) {
537  wm_job->run_free(wm_job->run_customdata);
538  }
539 
540  /* remove wm_job */
541  wm_job_free(wm, wm_job);
542 
543  /* Update progress bars in windows. */
544  if (update_progress) {
546  }
547 }
548 
549 /* wait until every job ended */
551 {
552  wmJob *wm_job;
553 
554  while ((wm_job = wm->jobs.first)) {
555  wm_jobs_kill_job(wm, wm_job);
556  }
557 
558  /* This job will be automatically restarted */
560 }
561 
562 /* wait until every job ended, except for one owner (used in undo to keep screen job alive) */
564 {
565  LISTBASE_FOREACH_MUTABLE (wmJob *, wm_job, &wm->jobs) {
566  if (wm_job->owner != owner) {
567  wm_jobs_kill_job(wm, wm_job);
568  }
569  }
570 }
571 
572 void WM_jobs_kill_type(struct wmWindowManager *wm, void *owner, int job_type)
573 {
574  LISTBASE_FOREACH_MUTABLE (wmJob *, wm_job, &wm->jobs) {
575  if (!owner || wm_job->owner == owner) {
576  if (ELEM(job_type, WM_JOB_TYPE_ANY, wm_job->job_type)) {
577  wm_jobs_kill_job(wm, wm_job);
578  }
579  }
580  }
581 }
582 
583 /* signal job(s) from this owner or callback to stop, timer is required to get handled */
585 {
586  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
587  if (wm_job->owner == owner || wm_job->startjob == startjob) {
588  if (wm_job->running) {
589  wm_job->stop = true;
590  }
591  }
592  }
593 }
594 
595 /* actually terminate thread and job timer */
597  void *owner,
598  void (*startjob)(void *, short int *, short int *, float *))
599 {
600  LISTBASE_FOREACH_MUTABLE (wmJob *, wm_job, &wm->jobs) {
601  if (wm_job->owner == owner || wm_job->startjob == startjob) {
602  wm_jobs_kill_job(wm, wm_job);
603  }
604  }
605 }
606 
607 /* kill job entirely, also removes timer itself */
609 {
610  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
611  if (wm_job->wt == wt) {
612  wm_jobs_kill_job(wm, wm_job);
613  return;
614  }
615  }
616 }
617 
618 /* hardcoded to event TIMERJOBS */
620 {
621  LISTBASE_FOREACH_MUTABLE (wmJob *, wm_job, &wm->jobs) {
622  if (wm_job->wt == wt) {
623  /* running threads */
624  if (wm_job->threads.first) {
625 
626  /* let threads get temporary lock over main thread if needed */
627  wm_job_main_thread_yield(wm_job);
628 
629  /* always call note and update when ready */
630  if (wm_job->do_update || wm_job->ready) {
631  if (wm_job->update) {
632  wm_job->update(wm_job->run_customdata);
633  }
634  if (wm_job->note) {
635  WM_event_add_notifier_ex(wm, wm_job->win, wm_job->note, NULL);
636  }
637 
638  if (wm_job->flag & WM_JOB_PROGRESS) {
639  WM_event_add_notifier_ex(wm, wm_job->win, NC_WM | ND_JOB, NULL);
640  }
641  wm_job->do_update = false;
642  }
643 
644  if (wm_job->ready) {
645  if (wm_job->endjob) {
646  wm_job->endjob(wm_job->run_customdata);
647  }
648 
649  /* free own data */
650  wm_job->run_free(wm_job->run_customdata);
651  wm_job->run_customdata = NULL;
652  wm_job->run_free = NULL;
653 
654 #if 0
655  if (wm_job->stop) {
656  printf("job ready but stopped %s\n", wm_job->name);
657  }
658  else {
659  printf("job finished %s\n", wm_job->name);
660  }
661 #endif
662 
663  if (G.debug & G_DEBUG_JOBS) {
664  printf("Job '%s' finished in %f seconds\n",
665  wm_job->name,
666  PIL_check_seconds_timer() - wm_job->start_time);
667  }
668 
669  wm_job->running = false;
670 
672  BLI_threadpool_end(&wm_job->threads);
674 
675  if (wm_job->endnote) {
676  WM_event_add_notifier_ex(wm, wm_job->win, wm_job->endnote, NULL);
677  }
678 
679  WM_event_add_notifier_ex(wm, wm_job->win, NC_WM | ND_JOB, NULL);
680 
681  /* new job added for wm_job? */
682  if (wm_job->customdata) {
683  // printf("job restarted with new data %s\n", wm_job->name);
684  WM_jobs_start(wm, wm_job);
685  }
686  else {
687  WM_event_remove_timer(wm, wm_job->win, wm_job->wt);
688  wm_job->wt = NULL;
689 
690  /* remove wm_job */
691  wm_job_free(wm, wm_job);
692  }
693  }
694  }
695  else if (wm_job->suspended) {
696  WM_jobs_start(wm, wm_job);
697  }
698  }
699  }
700 
701  /* Update progress bars in windows. */
703 }
704 
706 {
707  LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) {
708  if (wm_job->running) {
709  return true;
710  }
711  }
712 
713  return false;
714 }
typedef float(TangentPoint)[2]
@ G_DEBUG_JOBS
Definition: BKE_global.h:139
#define BLI_assert(a)
Definition: BLI_assert.h:58
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:116
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:188
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
void BLI_ticket_mutex_unlock(TicketMutex *ticket)
Definition: threads.cc:590
void BLI_thread_put_thread_on_fast_node(void)
Definition: threads.cc:918
void BLI_threadpool_init(struct ListBase *threadbase, void *(*do_thread)(void *), int tot)
Definition: threads.cc:159
void BLI_threadpool_end(struct ListBase *threadbase)
Definition: threads.cc:289
void BLI_ticket_mutex_lock(TicketMutex *ticket)
Definition: threads.cc:576
void BLI_ticket_mutex_free(TicketMutex *ticket)
Definition: threads.cc:569
void BLI_threadpool_insert(struct ListBase *threadbase, void *callerdata)
Definition: threads.cc:239
TicketMutex * BLI_ticket_mutex_alloc(void)
Definition: threads.cc:558
#define ELEM(...)
Read Guarded memory(de)allocation.
Platform independent time functions.
@ WM_JOB_TYPE_ANY
Definition: WM_api.h:734
void(* wm_jobs_start_callback)(void *custom_data, short *stop, short *do_update, float *progress)
Definition: WM_api.h:784
@ WM_JOB_EXCL_RENDER
Definition: WM_api.h:725
@ WM_JOB_PROGRESS
Definition: WM_api.h:726
@ WM_JOB_PRIORITY
Definition: WM_api.h:724
#define ND_JOB
Definition: WM_types.h:315
#define NC_WM
Definition: WM_types.h:276
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
static void update(bNodeTree *ntree)
void SEQ_prefetch_stop_all(void)
Definition: prefetch.c:261
void * first
Definition: DNA_listBase.h:47
Definition: wm_jobs.c:73
double timestep
Definition: wm_jobs.c:108
int flag
Definition: wm_jobs.c:117
short job_type
Definition: wm_jobs.c:118
wm_jobs_start_callback startjob
Definition: wm_jobs.c:90
void(* update)(void *)
Definition: wm_jobs.c:95
short running
Definition: wm_jobs.c:118
void(* initjob)(void *)
Definition: wm_jobs.c:85
struct wmJob * prev
Definition: wm_jobs.c:74
short ready
Definition: wm_jobs.c:118
void(* run_free)(void *)
Definition: wm_jobs.c:126
void * run_customdata
Definition: wm_jobs.c:125
short do_update
Definition: wm_jobs.c:118
unsigned int note
Definition: wm_jobs.c:113
TicketMutex * main_thread_mutex
Definition: wm_jobs.c:135
void * customdata
Definition: wm_jobs.c:80
void(* free)(void *)
Definition: wm_jobs.c:100
void * owner
Definition: wm_jobs.c:116
ListBase threads
Definition: wm_jobs.c:129
char name[128]
Definition: wm_jobs.c:122
short suspended
Definition: wm_jobs.c:118
unsigned int endnote
Definition: wm_jobs.c:113
short stop
Definition: wm_jobs.c:118
double start_time
Definition: wm_jobs.c:131
double start_delay_time
Definition: wm_jobs.c:111
wmTimer * wt
Definition: wm_jobs.c:109
void(* endjob)(void *)
Definition: wm_jobs.c:105
float progress
Definition: wm_jobs.c:119
struct wmJob * next
Definition: wm_jobs.c:74
wmWindow * win
Definition: wm_jobs.c:77
double timestep
Definition: WM_types.h:696
double PIL_check_seconds_timer(void)
Definition: time.c:80
#define G(x, y, z)
void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
@ TIMERJOBS
static void wm_job_free(wmWindowManager *wm, wmJob *wm_job)
Definition: wm_jobs.c:504
static void wm_job_main_thread_yield(wmJob *wm_job)
Definition: wm_jobs.c:150
void WM_jobs_kill(wmWindowManager *wm, void *owner, void(*startjob)(void *, short int *, short int *, float *))
Definition: wm_jobs.c:596
void WM_jobs_kill_all_except(wmWindowManager *wm, void *owner)
Definition: wm_jobs.c:563
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition: wm_jobs.c:450
static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job)
Definition: wm_jobs.c:513
void * WM_jobs_customdata(wmWindowManager *wm, void *owner)
Definition: wm_jobs.c:303
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, const char *name, int flag, int job_type)
Definition: wm_jobs.c:196
void wm_jobs_timer_end(wmWindowManager *wm, wmTimer *wt)
Definition: wm_jobs.c:608
char * WM_jobs_name(wmWindowManager *wm, void *owner)
Definition: wm_jobs.c:292
void * WM_jobs_customdata_from_type(wmWindowManager *wm, int job_type)
Definition: wm_jobs.c:314
static void wm_jobs_test_suspend_stop(wmWindowManager *wm, wmJob *test)
Definition: wm_jobs.c:396
bool WM_jobs_test(wmWindowManager *wm, void *owner, int job_type)
Definition: wm_jobs.c:223
void WM_jobs_kill_all(wmWindowManager *wm)
Definition: wm_jobs.c:550
void WM_jobs_delay_start(wmJob *wm_job, double delay_time)
Definition: wm_jobs.c:367
static wmJob * wm_job_find(wmWindowManager *wm, void *owner, const int job_type)
Definition: wm_jobs.c:161
static void wm_jobs_update_progress_bars(wmWindowManager *wm)
Definition: wm_jobs.c:250
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition: wm_jobs.c:372
void wm_jobs_timer(wmWindowManager *wm, wmTimer *wt)
Definition: wm_jobs.c:619
void WM_jobs_kill_type(struct wmWindowManager *wm, void *owner, int job_type)
Definition: wm_jobs.c:572
void * WM_jobs_customdata_get(wmJob *wm_job)
Definition: wm_jobs.c:336
bool WM_jobs_is_stopped(wmWindowManager *wm, void *owner)
Definition: wm_jobs.c:330
bool WM_jobs_is_running(wmJob *wm_job)
Definition: wm_jobs.c:325
void WM_job_main_thread_lock_acquire(wmJob *wm_job)
Definition: wm_jobs.c:140
static void * do_job_thread(void *job_v)
Definition: wm_jobs.c:384
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *))
Definition: wm_jobs.c:344
void WM_jobs_timer(wmJob *wm_job, double timestep, unsigned int note, unsigned int endnote)
Definition: wm_jobs.c:360
void WM_jobs_stop(wmWindowManager *wm, void *owner, void *startjob)
Definition: wm_jobs.c:584
double WM_jobs_starttime(wmWindowManager *wm, void *owner)
Definition: wm_jobs.c:281
void WM_job_main_thread_lock_release(wmJob *wm_job)
Definition: wm_jobs.c:145
float WM_jobs_progress(wmWindowManager *wm, void *owner)
Definition: wm_jobs.c:239
bool WM_jobs_has_running(wmWindowManager *wm)
Definition: wm_jobs.c:705
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1669
void WM_progress_clear(wmWindow *win)
Definition: wm_window.c:1831
void WM_progress_set(wmWindow *win, float progress)
Definition: wm_window.c:1823
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1632