Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
jobs.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2010 johannes hanika.
4 Copyright (C) 2010 Alexandre Prokoudine.
5 Copyright (C) 2010 Henrik Andersson.
6 Copyright (C) 2014-2017 Tobias Ellinghaus.
7 Copyright (C) 2015-2017 Roman Lebedev.
8 Copyright (C) 2020-2021 Hanno Schwalm.
9 Copyright (C) 2020 Pascal Obry.
10 Copyright (C) 2022, 2025-2026 Aurélien PIERRE.
11 Copyright (C) 2022 Martin Bařinka.
12 Copyright (C) 2026 Guillaume Stutin.
13
14 darktable is free software: you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 darktable is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with darktable. If not, see <http://www.gnu.org/licenses/>.
26*/
27
29#include "control/jobs.h"
30#include "control/control.h"
31#include "common/times.h"
32#include "common/logging.h"
33
34#define DT_CONTROL_FG_PRIORITY 4
35
36// The thumbtable allows at most 840 thumbs at once.
37// Once an order to recompute has been dispatched, thumbnails are not drawn
38// until it finishes and we get the final buffer.
39// If jobs are flushed from the queue before completion,
40// those thumbnails will never be redrawn.
41#define DT_CONTROL_MAX_JOBS 840
42
43/* the queue can have scheduled jobs but all
44 the workers are sleeping, so this kicks the workers
45 on timed interval.
46*/
52
74
81static inline int dt_control_job_equal(_dt_job_t *j1, _dt_job_t *j2)
82{
83 if(IS_NULL_PTR(j1) || IS_NULL_PTR(j2)) return 0;
84 if(j1->params_size != 0 && j1->params_size == j2->params_size)
85 return (j1->execute == j2->execute && j1->state_changed_cb == j2->state_changed_cb
86 && j1->queue == j2->queue && (memcmp(j1->params, j2->params, j1->params_size) == 0));
87 return (j1->execute == j2->execute && j1->state_changed_cb == j2->state_changed_cb && j1->queue == j2->queue
88 && (g_strcmp0(j1->description, j2->description) == 0));
89}
90
92{
93 if(IS_NULL_PTR(job)) return;
96 {
98 job->progress = NULL;
99 }
100 job->state = state;
101 /* pass state change to callback */
102 if(job->state_changed_cb) job->state_changed_cb(job, state);
104}
105
114
116{
118 job->params = params;
119 job->params_size = 0;
120 job->params_destroy = callback;
121}
122
125{
127 job->params = params;
129 job->params_destroy = callback;
130}
131
133{
134 if(IS_NULL_PTR(job)) return NULL;
135 return job->params;
136}
137
139{
140 _dt_job_t *job = (_dt_job_t *)calloc(1, sizeof(_dt_job_t));
141 if(IS_NULL_PTR(job)) return NULL;
142
143 va_list ap;
144 va_start(ap, msg);
145 vsnprintf(job->description, DT_CONTROL_DESCRIPTION_LEN, msg, ap);
146 va_end(ap);
147
148 job->execute = execute;
150
153 return job;
154}
155
167
169{
170 // once the job got added to the queue it may not be changed from the outside
172 return; // get_state returns DISPOSED when IS_NULL_PTR(job)
173 job->state_changed_cb = cb;
174}
175
176
178{
179 if(IS_NULL_PTR(job)) return;
180 dt_print(DT_DEBUG_CONTROL, "%s | queue: %d | priority: %d", job->description, job->queue, job->priority);
181}
182
187
189{
190 if(IS_NULL_PTR(job)) return;
192
193 // NOTE: could also use signals.
194
195 /* if job execution is not finished let's wait for it */
197 {
198 // once the job finishes, it unlocks the mutex
199 // so by locking the mutex here, we will only get the lock once the job
200 // has finished and unlocked it.
202 // yay, the job finished, we got the lock. nothing more to do.
204 }
205}
206
207// The job each reserved worker runs, from the moment it takes it off job_res[] until it disposes
208// of it. Guarded by res_mutex. Nothing else refers to it meanwhile, and
209// dt_control_running_jobs_foreach() reports it.
211
212static int32_t dt_control_run_job_res(dt_control_t *control, int32_t res)
213{
214 if(((unsigned int)res) >= DT_CTL_WORKER_RESERVED) return -1;
215
216 _dt_job_t *job = NULL;
218 if(control->new_res[res])
219 {
220 job = control->job_res[res];
221 control->job_res[res] = NULL; // this job belongs to us now, the queue may not touch it any longer
222 _job_res_running[res] = job;
223 }
224 control->new_res[res] = 0;
226 if(IS_NULL_PTR(job)) return -1;
227
228 /* change state to running */
231 {
232 dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", res, dt_get_wtime());
235
236 /* execute job */
238 job->result = job->execute(job);
239
241 dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", res, dt_get_wtime());
244 }
246
248 _job_res_running[res] = NULL;
250
252 return 0;
253}
254
256{
257 /*
258 * job scheduling works like this:
259 * - when there is a single job in the queue head with a maximal priority -> pick it
260 * - otherwise pick among the ones with the maximal priority in the following order:
261 * * user foreground
262 * * system foreground
263 * * user background
264 * * system background
265 * - the jobs that didn't get picked this round get their priority incremented
266 */
267
269
270 // find the job
271 _dt_job_t *job = NULL;
272 int winner_queue = DT_JOB_QUEUE_MAX;
273 int max_priority = -1;
274 for(int i = 0; i < DT_JOB_QUEUE_MAX; i++)
275 {
276 if(control->queues[i] == NULL) continue;
277 if(control->export_scheduled && i == DT_JOB_QUEUE_USER_EXPORT) continue;
278 _dt_job_t *_job = (_dt_job_t *)control->queues[i]->data;
279 if(_job->priority > max_priority)
280 {
281 max_priority = _job->priority;
282 job = _job;
283 winner_queue = i;
284 }
285 }
286
287 if(IS_NULL_PTR(job))
288 {
290 return NULL;
291 }
292
293 // the order of the queues in control->queues matches our priority, and we only update job when the priority
294 // is strictly bigger
295 // invariant -> job is the one we are looking for
296
297 // remove the to be scheduled job from its queue
298 GList **queue = &control->queues[winner_queue];
299 *queue = g_list_delete_link(*queue, *queue);
300 control->queue_length[winner_queue]--;
301 if(winner_queue == DT_JOB_QUEUE_USER_EXPORT) control->export_scheduled = TRUE;
302
303 // and place it in scheduled job array (for job deduping)
304 control->job[dt_control_get_threadid()] = job;
305
306 // increment the priorities of the others
307 for(int i = 0; i < DT_JOB_QUEUE_MAX; i++)
308 {
309 if(i == winner_queue || control->queues[i] == NULL) continue;
310 ((_dt_job_t *)control->queues[i]->data)->priority++;
311 }
312
314
315 return job;
316}
317
319{
321 dt_get_wtime());
324
326
327 /* execute job */
328 job->result = job->execute(job);
329
331
333 dt_get_wtime());
336}
337
338static int32_t dt_control_run_job(dt_control_t *control)
339{
340 _dt_job_t *job = dt_control_schedule_job(control);
341
342 if(IS_NULL_PTR(job)) return -1;
343
344 /* change state to running */
348
350
351 // remove the job from scheduled job array (for job deduping)
353 control->job[dt_control_get_threadid()] = NULL;
356
357 // and free it
359
360 return 0;
361}
362
363int32_t dt_control_add_job_res(dt_control_t *control, _dt_job_t *job, int32_t res)
364{
365 if(((unsigned int)res) >= DT_CTL_WORKER_RESERVED || IS_NULL_PTR(job))
366 {
368 return 1;
369 }
370
371 // TODO: pthread cancel and restart in tough cases?
373
374 // if there is a job in the queue we have to discard that first
375 if(control->job_res[res])
376 {
379 }
380
381 dt_print(DT_DEBUG_CONTROL, "[add_job_res] %d | ", res);
384
386 control->job_res[res] = job;
387 control->new_res[res] = 1;
388
390
392 pthread_cond_broadcast(&control->cond);
394
395 return 0;
396}
397
399{
401
402 int count = 0;
403
404 for(int k = 0; k < control->num_threads; k++)
405 {
406 _dt_job_t *job = (_dt_job_t *)control->job[k];
407 if(!IS_NULL_PTR(job))
408 {
410 count++;
411 }
412 }
413
414 dt_print(DT_DEBUG_CONTROL, "[jobs] flushed %i pending jobs from queue %i\n", count, queue_id);
415
417}
418
420{
421 if(((unsigned int)queue_id) >= DT_JOB_QUEUE_MAX || IS_NULL_PTR(job))
422 {
424 return 1;
425 }
426
427 if(!control->running)
428 {
429 // whatever we are adding here won't be scheduled as the system isn't running. execute it synchronous instead.
430 dt_pthread_mutex_lock(&job->wait_mutex); // is that even needed?
433
435 return 0;
436 }
437
438 job->queue = queue_id;
439
440 _dt_job_t *job_for_disposal = NULL;
441
443
444 GList **queue = &control->queues[queue_id];
445 size_t length = control->queue_length[queue_id];
446
447 dt_print(DT_DEBUG_CONTROL, "[add_job] %" G_GSIZE_FORMAT " | ", length);
450
451 if(queue_id == DT_JOB_QUEUE_SYSTEM_FG)
452 {
453 // this is a stack with limited size and bubble up and all that stuff
455
456 // check if we have already scheduled the job
457 for(int k = 0; k < control->num_threads; k++)
458 {
459 _dt_job_t *other_job = (_dt_job_t *)control->job[k];
460 if(dt_control_job_equal(job, other_job))
461 {
462 dt_print(DT_DEBUG_CONTROL, "[add_job] found job already in scheduled: ");
463 dt_control_job_print(other_job);
465
467
470
471 return 0; // there can't be any further copy
472 }
473 }
474
475 // if the job is already in the queue -> move it to the top
476 for(GList *iter = *queue; iter; iter = g_list_next(iter))
477 {
478 _dt_job_t *other_job = (_dt_job_t *)iter->data;
479 if(dt_control_job_equal(job, other_job))
480 {
481 dt_print(DT_DEBUG_CONTROL, "[add_job] found job already in queue: ");
482 dt_control_job_print(other_job);
484
485 *queue = g_list_delete_link(*queue, iter);
486 length--;
487
488 job_for_disposal = job;
489
490 job = other_job;
491 break; // there can't be any further copy in the list
492 }
493 }
494
495 // now we can add the new job to the list
496 *queue = g_list_prepend(*queue, job);
497 length++;
498
499 // and take care of the maximal queue size
500 if(length > DT_CONTROL_MAX_JOBS)
501 {
502 GList *last = g_list_last(*queue);
504 dt_control_job_dispose((_dt_job_t *)last->data);
505 *queue = g_list_delete_link(*queue, last);
506 length--;
507 }
508
509 control->queue_length[queue_id] = length;
510 }
511 else
512 {
513 // the rest are FIFOs
514 if(queue_id == DT_JOB_QUEUE_USER_BG ||
515 queue_id == DT_JOB_QUEUE_USER_EXPORT ||
516 queue_id == DT_JOB_QUEUE_SYSTEM_BG)
517 job->priority = 0;
518 else
520 *queue = g_list_append(*queue, job);
521 control->queue_length[queue_id]++;
522 }
525
526 // notify workers
528 pthread_cond_broadcast(&control->cond);
530
531 // dispose of dropped job, if any
533 dt_control_job_dispose(job_for_disposal);
534
535 return 0;
536}
537
538static __thread int threadid = -1;
539
540// Threads of this file -- pool, reserved and kicker -- that have started and not returned yet.
541// dt_control_shutdown() joins every one of them, so this is what that join still waits for.
542static gint _workers_alive = 0;
543
545{
546 return g_atomic_int_get(&_workers_alive);
547}
548
550 void *data)
551{
552 // A running job is disposed only after it has left control->job[] or _job_res_running[], which
553 // it leaves under the mutex held around each here: its description outlives the callback.
555 for(int k = 0; k < control->num_threads; k++)
556 {
557 const _dt_job_t *job = control->job[k];
558 if(!IS_NULL_PTR(job)) callback(job->description, job->queue, FALSE, data);
559 }
561
563 for(int k = 0; k < DT_CTL_WORKER_RESERVED; k++)
564 {
565 const _dt_job_t *job = _job_res_running[k];
566 if(!IS_NULL_PTR(job)) callback(job->description, job->queue, TRUE, data);
567 }
569}
570
572{
573 size_t count = 0;
575 for(int i = 0; i < DT_JOB_QUEUE_MAX; i++) count += control->queue_length[i];
577
579 for(int k = 0; k < DT_CTL_WORKER_RESERVED; k++)
580 if(control->new_res[k] && !IS_NULL_PTR(control->job_res[k])) count++;
582
583 return (int32_t)count;
584}
585
587{
588 if(threadid > -1) return threadid;
590}
591
593{
594 if(threadid > -1) return threadid;
596}
597
598static void *dt_control_work_res(void *ptr)
599{
600#ifdef _OPENMP // need to do this in every thread
601 omp_set_num_threads(dt_get_num_openmp_threads());
602#endif
604 dt_control_t *s = params->self;
605 threadid = params->threadid;
606 char name[16] = {0};
607 snprintf(name, sizeof(name), "worker res %d", threadid);
610 int32_t threadid_res = dt_control_get_threadid_res();
611 g_atomic_int_inc(&_workers_alive);
612 while(dt_control_running())
613 {
614 // dt_print(DT_DEBUG_CONTROL, "[control_work] %d\n", threadid_res);
615 if(dt_control_run_job_res(s, threadid_res) < 0)
616 {
617 // wait for a new job. `running' is read under the mutex it is cleared under: a quit that
618 // lands between the loop test above and this wait would otherwise broadcast to nobody.
619 int old;
620 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
624 int tmp;
625 pthread_setcancelstate(old, &tmp);
626 }
627 }
628 g_atomic_int_add(&_workers_alive, -1);
629 return NULL;
630}
631
632static void *dt_control_worker_kicker(void *ptr)
633{
634 dt_control_t *control = (dt_control_t *)ptr;
635 dt_pthread_setname("kicker");
636 g_atomic_int_inc(&_workers_alive);
638 while(dt_control_running())
639 {
640 // Two seconds spent on the condition, not in sleep(): the broadcast of a quit ends them at
641 // once, so dt_control_shutdown() never joins a thread that is asleep. Any other broadcast
642 // goes back to waiting for the same deadline, so the kicks keep their pace.
643 const gint64 end = g_get_real_time() + 2 * G_USEC_PER_SEC;
644 const struct timespec deadline = { .tv_sec = end / G_USEC_PER_SEC,
645 .tv_nsec = (end % G_USEC_PER_SEC) * 1000 };
646 int timed_out = 0;
647 while(!timed_out && dt_control_running())
648 timed_out = pthread_cond_timedwait(&control->cond, &control->cond_mutex.mutex, &deadline);
649 pthread_cond_broadcast(&control->cond);
650 }
652 g_atomic_int_add(&_workers_alive, -1);
653 return NULL;
654}
655
656static void *dt_control_work(void *ptr)
657{
658#ifdef _OPENMP // need to do this in every thread
659 omp_set_num_threads(dt_get_num_openmp_threads());
660#endif
662 dt_control_t *control = params->self;
663 threadid = params->threadid;
664 char name[16] = {0};
665 snprintf(name, sizeof(name), "worker %d", threadid);
668 // int32_t threadid = dt_control_get_threadid();
669 g_atomic_int_inc(&_workers_alive);
670 while(dt_control_running())
671 {
672 // dt_print(DT_DEBUG_CONTROL, "[control_work] %d\n", threadid);
673 if(dt_control_run_job(control) < 0)
674 {
675 // wait for a new job, unless a quit landed since the loop test: see dt_control_work_res().
677 if(dt_control_running()) dt_pthread_cond_wait(&control->cond, &control->cond_mutex);
679 }
680 }
681 g_atomic_int_add(&_workers_alive, -1);
682 return NULL;
683}
684
685// convenience functions to have a progress bar for the job.
686// this allows to show the gui indicator of the job even before it got scheduled
687void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
688{
689 if(IS_NULL_PTR(job)) return;
691 if(cancellable)
693}
694
695void dt_control_job_set_progress_message(dt_job_t *job, const char *message)
696{
697 if(IS_NULL_PTR(job) || !job->progress) return;
699}
700
706
708{
709 if(IS_NULL_PTR(job) || !job->progress) return -1.0;
711}
712
713
714// moved out of control.c to be able to make some helper functions static
716{
717 // start threads
718 control->num_threads = dt_worker_threads();
719 control->thread = (pthread_t *)calloc(control->num_threads, sizeof(pthread_t));
720 control->job = (dt_job_t **)calloc(control->num_threads, sizeof(dt_job_t *));
722 control->running = 1;
724 for(int k = 0; k < control->num_threads; k++)
725 {
728 params->self = control;
729 params->threadid = k;
731 }
732
733 /* create queue kicker thread */
735
736 for(int k = 0; k < DT_CTL_WORKER_RESERVED; k++)
737 {
738 control->job_res[k] = NULL;
739 control->new_res[k] = 0;
742 params->self = control;
743 params->threadid = k;
745 }
746}
747
749{
750 // Detach every queued job under the lock, then dispose them outside it. Disposing a job runs
751 // its callbacks (state_changed_cb via DT_JOB_STATE_DISPOSED, then params_destroy), which for
752 // module jobs point into a plug-in .so -- so these callbacks must NOT be invoked while holding
753 // queue_mutex (re-entrancy), and the whole drain must happen before those .so files are
754 // unloaded. Workers are already joined when this runs, so detaching the lists is race-free.
755 GList *doomed = NULL;
757 for(int i = 0; i < DT_JOB_QUEUE_MAX; i++)
758 {
759 doomed = g_list_concat(doomed, control->queues[i]);
760 control->queues[i] = NULL;
761 control->queue_length[i] = 0;
762 }
763 control->export_scheduled = FALSE;
765
766 for(GList *l = doomed; l; l = g_list_next(l))
768 g_list_free(doomed);
769}
770
772{
773 // Normally the queues were already drained by dt_control_shutdown() (while plug-in .so files
774 // were still mapped). Drain again as a safety net for any path that skips shutdown (e.g. the
775 // headless export run); it is a no-op when the queues are already empty.
776 dt_control_jobs_drain(control);
777
778 dt_free(control->job);
779 dt_free(control->thread);
780}
781
782// clang-format off
783// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
784// vim: shiftwidth=2 expandtab tabstop=2 cindent
785// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
786// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
size_t params_size(dt_imageio_module_format_t *self)
Definition avif.c:571
int dt_control_running()
Definition control.c:442
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:654
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
Definition darktable.c:521
int dt_worker_threads()
Definition darktable.c:2390
int dt_pthread_create(pthread_t *thread, void *(*start_routine)(void *), void *arg, const gboolean realtime)
Definition dtpthread.c:52
void dt_pthread_setname(const char *name)
Definition dtpthread.c:147
const int res
Definition dtpthread.h:351
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:127
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Initialise a mutex. With mutexattr NULL – which is how 54 of the 56 call sites in this tree spell it ...
Definition dtpthread.h:104
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:132
static int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex)
Wait on cond, releasing mutex for the duration.
Definition dtpthread.h:149
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:117
void dt_control_running_jobs_foreach(dt_control_t *control, dt_control_running_jobs_foreach_callback_t callback, void *data)
Definition jobs.c:549
int32_t dt_control_queued_jobs_count(dt_control_t *control)
Definition jobs.c:571
static void dt_control_job_execute(_dt_job_t *job)
Definition jobs.c:318
void dt_control_flush_jobs_queue(dt_control_t *control, dt_job_queue_t queue_id)
Definition jobs.c:398
static gint _workers_alive
Definition jobs.c:542
static _dt_job_t * dt_control_schedule_job(dt_control_t *control)
Definition jobs.c:255
static int32_t dt_control_run_job(dt_control_t *control)
Definition jobs.c:338
static int32_t dt_control_run_job_res(dt_control_t *control, int32_t res)
Definition jobs.c:212
static void dt_control_job_set_state(_dt_job_t *job, dt_job_state_t state)
Definition jobs.c:91
dt_job_state_t dt_control_job_get_state(_dt_job_t *job)
Definition jobs.c:106
void dt_control_job_cancel(_dt_job_t *job)
Definition jobs.c:183
static void * dt_control_work(void *ptr)
Definition jobs.c:656
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:138
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:419
int32_t dt_control_add_job_res(dt_control_t *control, _dt_job_t *job, int32_t res)
Definition jobs.c:363
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:132
double dt_control_job_get_progress(dt_job_t *job)
Definition jobs.c:707
static void dt_control_job_print(_dt_job_t *job)
Definition jobs.c:177
void dt_control_jobs_drain(dt_control_t *control)
Definition jobs.c:748
void dt_control_job_set_progress(dt_job_t *job, double value)
Definition jobs.c:701
void dt_control_jobs_cleanup(dt_control_t *control)
Definition jobs.c:771
void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
Definition jobs.c:687
void dt_control_job_set_progress_message(dt_job_t *job, const char *message)
Definition jobs.c:695
#define DT_CONTROL_FG_PRIORITY
Definition jobs.c:34
void dt_control_job_set_params_with_size(dt_job_t *job, void *params, size_t params_size, dt_job_destroy_callback callback)
Definition jobs.c:123
static _dt_job_t * _job_res_running[DT_CTL_WORKER_RESERVED]
Definition jobs.c:210
void dt_control_job_wait(_dt_job_t *job)
Definition jobs.c:188
void dt_control_job_set_state_callback(_dt_job_t *job, dt_job_state_change_callback cb)
Definition jobs.c:168
int32_t dt_control_get_threadid()
Definition jobs.c:586
static void * dt_control_work_res(void *ptr)
Definition jobs.c:598
int32_t dt_control_workers_alive(void)
Definition jobs.c:544
void dt_control_jobs_init(dt_control_t *control)
Definition jobs.c:715
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:115
static void * dt_control_worker_kicker(void *ptr)
Definition jobs.c:632
static int dt_control_job_equal(_dt_job_t *j1, _dt_job_t *j2)
Definition jobs.c:81
#define DT_CONTROL_MAX_JOBS
Definition jobs.c:41
static __thread int threadid
Definition jobs.c:538
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:156
static int32_t dt_control_get_threadid_res()
Definition jobs.c:592
void(* dt_job_state_change_callback)(dt_job_t *, dt_job_state_t state)
Definition jobs.h:66
#define DT_CONTROL_DESCRIPTION_LEN
Definition jobs.h:37
dt_job_queue_t
Definition jobs.h:54
@ DT_JOB_QUEUE_MAX
Definition jobs.h:60
@ DT_JOB_QUEUE_USER_BG
Definition jobs.h:57
@ DT_JOB_QUEUE_USER_EXPORT
Definition jobs.h:58
@ DT_JOB_QUEUE_SYSTEM_BG
Definition jobs.h:59
@ DT_JOB_QUEUE_SYSTEM_FG
Definition jobs.h:56
int32_t(* dt_job_execute_callback)(dt_job_t *)
Definition jobs.h:65
void(* dt_job_destroy_callback)(void *data)
Definition jobs.h:67
#define DT_CTL_WORKER_RESERVED
Definition jobs.h:39
void(* dt_control_running_jobs_foreach_callback_t)(const char *description, dt_job_queue_t queue, gboolean reserved, void *data)
Definition jobs.h:114
dt_job_state_t
Definition jobs.h:43
@ DT_JOB_STATE_DISCARDED
Definition jobs.h:49
@ DT_JOB_STATE_INITIALIZED
Definition jobs.h:44
@ DT_JOB_STATE_DISPOSED
Definition jobs.h:50
@ DT_JOB_STATE_RUNNING
Definition jobs.h:46
@ DT_JOB_STATE_CANCELLED
Definition jobs.h:48
@ DT_JOB_STATE_FINISHED
Definition jobs.h:47
@ DT_JOB_STATE_QUEUED
Definition jobs.h:45
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
@ DT_DEBUG_CONTROL
Definition logging.h:52
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
const char * name
Definition pdf.h:90
void dt_control_progress_set_progress(dt_control_t *control, dt_progress_t *progress, double value)
Definition progress.c:375
void dt_control_progress_set_message(dt_control_t *control, dt_progress_t *progress, const char *message)
Definition progress.c:409
double dt_control_progress_get_progress(dt_progress_t *progress)
Definition progress.c:393
dt_progress_t * dt_control_progress_create(dt_control_t *control, gboolean has_progress_bar, const gchar *message)
Definition progress.c:282
void dt_control_progress_destroy(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:310
void dt_control_progress_attach_job(dt_control_t *control, dt_progress_t *progress, dt_job_t *job)
Definition progress.c:353
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
const float uint32_t state[4]
dt_job_state_t state
Definition jobs.c:64
dt_job_queue_t queue
Definition jobs.c:66
dt_progress_t * progress
Definition jobs.c:70
dt_pthread_mutex_t state_mutex
Definition jobs.c:61
size_t params_size
Definition jobs.c:57
dt_job_destroy_callback params_destroy
Definition jobs.c:58
unsigned char priority
Definition jobs.c:65
dt_job_state_change_callback state_changed_cb
Definition jobs.c:68
int32_t result
Definition jobs.c:59
void * params
Definition jobs.c:56
dt_pthread_mutex_t wait_mutex
Definition jobs.c:62
char description[DT_CONTROL_DESCRIPTION_LEN]
Definition jobs.c:72
dt_job_execute_callback execute
Definition jobs.c:55
pthread_t * thread
Definition control.h:251
dt_pthread_mutex_t run_mutex
Definition control.h:248
int32_t num_threads
Definition control.h:250
int32_t running
Definition control.h:246
GList * queues[DT_JOB_QUEUE_MAX]
Definition control.h:254
gboolean export_scheduled
Definition control.h:247
pthread_t thread_res[DT_CTL_WORKER_RESERVED]
Definition control.h:260
pthread_cond_t cond
Definition control.h:249
dt_pthread_mutex_t res_mutex
Definition control.h:257
pthread_t kick_on_workers_thread
Definition control.h:251
dt_pthread_mutex_t queue_mutex
Definition control.h:248
uint8_t new_res[DT_CTL_WORKER_RESERVED]
Definition control.h:259
dt_job_t * job_res[DT_CTL_WORKER_RESERVED]
Definition control.h:258
dt_pthread_mutex_t cond_mutex
Definition control.h:248
size_t queue_length[DT_JOB_QUEUE_MAX]
Definition control.h:255
dt_job_t ** job
Definition control.h:252
dt_control_t * self
Definition jobs.c:49
static double dt_get_wtime(void)
Definition times.h:43