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
13 darktable is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 darktable is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with darktable. If not, see <http://www.gnu.org/licenses/>.
25*/
26
28#include "control/jobs.h"
29#include "control/control.h"
30#include "common/times.h"
31#include "common/logging.h"
32
33#define DT_CONTROL_FG_PRIORITY 4
34
35// The thumbtable allows at most 840 thumbs at once.
36// Once an order to recompute has been dispatched, thumbnails are not drawn
37// until it finishes and we get the final buffer.
38// If jobs are flushed from the queue before completion,
39// those thumbnails will never be redrawn.
40#define DT_CONTROL_MAX_JOBS 840
41
42/* the queue can have scheduled jobs but all
43 the workers are sleeping, so this kicks the workers
44 on timed interval.
45*/
51
73
80static inline int dt_control_job_equal(_dt_job_t *j1, _dt_job_t *j2)
81{
82 if(IS_NULL_PTR(j1) || IS_NULL_PTR(j2)) return 0;
83 if(j1->params_size != 0 && j1->params_size == j2->params_size)
84 return (j1->execute == j2->execute && j1->state_changed_cb == j2->state_changed_cb
85 && j1->queue == j2->queue && (memcmp(j1->params, j2->params, j1->params_size) == 0));
86 return (j1->execute == j2->execute && j1->state_changed_cb == j2->state_changed_cb && j1->queue == j2->queue
87 && (g_strcmp0(j1->description, j2->description) == 0));
88}
89
91{
92 if(IS_NULL_PTR(job)) return;
95 {
97 job->progress = NULL;
98 }
99 job->state = state;
100 /* pass state change to callback */
101 if(job->state_changed_cb) job->state_changed_cb(job, state);
103}
104
113
115{
117 job->params = params;
118 job->params_size = 0;
119 job->params_destroy = callback;
120}
121
124{
126 job->params = params;
128 job->params_destroy = callback;
129}
130
132{
133 if(IS_NULL_PTR(job)) return NULL;
134 return job->params;
135}
136
138{
139 _dt_job_t *job = (_dt_job_t *)calloc(1, sizeof(_dt_job_t));
140 if(IS_NULL_PTR(job)) return NULL;
141
142 va_list ap;
143 va_start(ap, msg);
144 vsnprintf(job->description, DT_CONTROL_DESCRIPTION_LEN, msg, ap);
145 va_end(ap);
146
147 job->execute = execute;
149
152 return job;
153}
154
166
168{
169 // once the job got added to the queue it may not be changed from the outside
171 return; // get_state returns DISPOSED when IS_NULL_PTR(job)
172 job->state_changed_cb = cb;
173}
174
175
177{
178 if(IS_NULL_PTR(job)) return;
179 dt_print(DT_DEBUG_CONTROL, "%s | queue: %d | priority: %d", job->description, job->queue, job->priority);
180}
181
186
188{
189 if(IS_NULL_PTR(job)) return;
191
192 // NOTE: could also use signals.
193
194 /* if job execution is not finished let's wait for it */
196 {
197 // once the job finishes, it unlocks the mutex
198 // so by locking the mutex here, we will only get the lock once the job
199 // has finished and unlocked it.
201 // yay, the job finished, we got the lock. nothing more to do.
203 }
204}
205
206static int32_t dt_control_run_job_res(dt_control_t *control, int32_t res)
207{
208 if(((unsigned int)res) >= DT_CTL_WORKER_RESERVED) return -1;
209
210 _dt_job_t *job = NULL;
212 if(control->new_res[res])
213 {
214 job = control->job_res[res];
215 control->job_res[res] = NULL; // this job belongs to us now, the queue may not touch it any longer
216 }
217 control->new_res[res] = 0;
219 if(IS_NULL_PTR(job)) return -1;
220
221 /* change state to running */
224 {
225 dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", res, dt_get_wtime());
228
229 /* execute job */
231 job->result = job->execute(job);
232
234 dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", res, dt_get_wtime());
237 }
240 return 0;
241}
242
244{
245 /*
246 * job scheduling works like this:
247 * - when there is a single job in the queue head with a maximal priority -> pick it
248 * - otherwise pick among the ones with the maximal priority in the following order:
249 * * user foreground
250 * * system foreground
251 * * user background
252 * * system background
253 * - the jobs that didn't get picked this round get their priority incremented
254 */
255
257
258 // find the job
259 _dt_job_t *job = NULL;
260 int winner_queue = DT_JOB_QUEUE_MAX;
261 int max_priority = -1;
262 for(int i = 0; i < DT_JOB_QUEUE_MAX; i++)
263 {
264 if(control->queues[i] == NULL) continue;
265 if(control->export_scheduled && i == DT_JOB_QUEUE_USER_EXPORT) continue;
266 _dt_job_t *_job = (_dt_job_t *)control->queues[i]->data;
267 if(_job->priority > max_priority)
268 {
269 max_priority = _job->priority;
270 job = _job;
271 winner_queue = i;
272 }
273 }
274
275 if(IS_NULL_PTR(job))
276 {
278 return NULL;
279 }
280
281 // the order of the queues in control->queues matches our priority, and we only update job when the priority
282 // is strictly bigger
283 // invariant -> job is the one we are looking for
284
285 // remove the to be scheduled job from its queue
286 GList **queue = &control->queues[winner_queue];
287 *queue = g_list_delete_link(*queue, *queue);
288 control->queue_length[winner_queue]--;
289 if(winner_queue == DT_JOB_QUEUE_USER_EXPORT) control->export_scheduled = TRUE;
290
291 // and place it in scheduled job array (for job deduping)
292 control->job[dt_control_get_threadid()] = job;
293
294 // increment the priorities of the others
295 for(int i = 0; i < DT_JOB_QUEUE_MAX; i++)
296 {
297 if(i == winner_queue || control->queues[i] == NULL) continue;
298 ((_dt_job_t *)control->queues[i]->data)->priority++;
299 }
300
302
303 return job;
304}
305
307{
309 dt_get_wtime());
312
314
315 /* execute job */
316 job->result = job->execute(job);
317
319
321 dt_get_wtime());
324}
325
326static int32_t dt_control_run_job(dt_control_t *control)
327{
328 _dt_job_t *job = dt_control_schedule_job(control);
329
330 if(IS_NULL_PTR(job)) return -1;
331
332 /* change state to running */
336
338
339 // remove the job from scheduled job array (for job deduping)
341 control->job[dt_control_get_threadid()] = NULL;
344
345 // and free it
347
348 return 0;
349}
350
351int32_t dt_control_add_job_res(dt_control_t *control, _dt_job_t *job, int32_t res)
352{
353 if(((unsigned int)res) >= DT_CTL_WORKER_RESERVED || IS_NULL_PTR(job))
354 {
356 return 1;
357 }
358
359 // TODO: pthread cancel and restart in tough cases?
361
362 // if there is a job in the queue we have to discard that first
363 if(control->job_res[res])
364 {
367 }
368
369 dt_print(DT_DEBUG_CONTROL, "[add_job_res] %d | ", res);
372
374 control->job_res[res] = job;
375 control->new_res[res] = 1;
376
378
380 pthread_cond_broadcast(&control->cond);
382
383 return 0;
384}
385
387{
389
390 int count = 0;
391
392 for(int k = 0; k < control->num_threads; k++)
393 {
394 _dt_job_t *job = (_dt_job_t *)control->job[k];
395 if(!IS_NULL_PTR(job))
396 {
398 count++;
399 }
400 }
401
402 dt_print(DT_DEBUG_CONTROL, "[jobs] flushed %i pending jobs from queue %i\n", count, queue_id);
403
405}
406
408{
409 if(((unsigned int)queue_id) >= DT_JOB_QUEUE_MAX || IS_NULL_PTR(job))
410 {
412 return 1;
413 }
414
415 if(!control->running)
416 {
417 // whatever we are adding here won't be scheduled as the system isn't running. execute it synchronous instead.
418 dt_pthread_mutex_lock(&job->wait_mutex); // is that even needed?
421
423 return 0;
424 }
425
426 job->queue = queue_id;
427
428 _dt_job_t *job_for_disposal = NULL;
429
431
432 GList **queue = &control->queues[queue_id];
433 size_t length = control->queue_length[queue_id];
434
435 dt_print(DT_DEBUG_CONTROL, "[add_job] %" G_GSIZE_FORMAT " | ", length);
438
439 if(queue_id == DT_JOB_QUEUE_SYSTEM_FG)
440 {
441 // this is a stack with limited size and bubble up and all that stuff
443
444 // check if we have already scheduled the job
445 for(int k = 0; k < control->num_threads; k++)
446 {
447 _dt_job_t *other_job = (_dt_job_t *)control->job[k];
448 if(dt_control_job_equal(job, other_job))
449 {
450 dt_print(DT_DEBUG_CONTROL, "[add_job] found job already in scheduled: ");
451 dt_control_job_print(other_job);
453
455
458
459 return 0; // there can't be any further copy
460 }
461 }
462
463 // if the job is already in the queue -> move it to the top
464 for(GList *iter = *queue; iter; iter = g_list_next(iter))
465 {
466 _dt_job_t *other_job = (_dt_job_t *)iter->data;
467 if(dt_control_job_equal(job, other_job))
468 {
469 dt_print(DT_DEBUG_CONTROL, "[add_job] found job already in queue: ");
470 dt_control_job_print(other_job);
472
473 *queue = g_list_delete_link(*queue, iter);
474 length--;
475
476 job_for_disposal = job;
477
478 job = other_job;
479 break; // there can't be any further copy in the list
480 }
481 }
482
483 // now we can add the new job to the list
484 *queue = g_list_prepend(*queue, job);
485 length++;
486
487 // and take care of the maximal queue size
488 if(length > DT_CONTROL_MAX_JOBS)
489 {
490 GList *last = g_list_last(*queue);
492 dt_control_job_dispose((_dt_job_t *)last->data);
493 *queue = g_list_delete_link(*queue, last);
494 length--;
495 }
496
497 control->queue_length[queue_id] = length;
498 }
499 else
500 {
501 // the rest are FIFOs
502 if(queue_id == DT_JOB_QUEUE_USER_BG ||
503 queue_id == DT_JOB_QUEUE_USER_EXPORT ||
504 queue_id == DT_JOB_QUEUE_SYSTEM_BG)
505 job->priority = 0;
506 else
508 *queue = g_list_append(*queue, job);
509 control->queue_length[queue_id]++;
510 }
513
514 // notify workers
516 pthread_cond_broadcast(&control->cond);
518
519 // dispose of dropped job, if any
521 dt_control_job_dispose(job_for_disposal);
522
523 return 0;
524}
525
526static __thread int threadid = -1;
527
529{
530 if(threadid > -1) return threadid;
532}
533
535{
536 if(threadid > -1) return threadid;
538}
539
540static void *dt_control_work_res(void *ptr)
541{
542#ifdef _OPENMP // need to do this in every thread
543 omp_set_num_threads(dt_get_num_openmp_threads());
544#endif
546 dt_control_t *s = params->self;
547 threadid = params->threadid;
548 char name[16] = {0};
549 snprintf(name, sizeof(name), "worker res %d", threadid);
551 dt_free(params);
552 int32_t threadid_res = dt_control_get_threadid_res();
553 while(dt_control_running())
554 {
555 // dt_print(DT_DEBUG_CONTROL, "[control_work] %d\n", threadid_res);
556 if(dt_control_run_job_res(s, threadid_res) < 0)
557 {
558 // wait for a new job.
559 int old;
560 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
564 int tmp;
565 pthread_setcancelstate(old, &tmp);
566 }
567 }
568 return NULL;
569}
570
571static void *dt_control_worker_kicker(void *ptr)
572{
573 dt_control_t *control = (dt_control_t *)ptr;
574 dt_pthread_setname("kicker");
575 while(dt_control_running())
576 {
577 sleep(2);
579 pthread_cond_broadcast(&control->cond);
581 }
582 return NULL;
583}
584
585static void *dt_control_work(void *ptr)
586{
587#ifdef _OPENMP // need to do this in every thread
588 omp_set_num_threads(dt_get_num_openmp_threads());
589#endif
591 dt_control_t *control = params->self;
592 threadid = params->threadid;
593 char name[16] = {0};
594 snprintf(name, sizeof(name), "worker %d", threadid);
596 dt_free(params);
597 // int32_t threadid = dt_control_get_threadid();
598 while(dt_control_running())
599 {
600 // dt_print(DT_DEBUG_CONTROL, "[control_work] %d\n", threadid);
601 if(dt_control_run_job(control) < 0)
602 {
603 // wait for a new job.
605 dt_pthread_cond_wait(&control->cond, &control->cond_mutex);
607 }
608 }
609 return NULL;
610}
611
612// convenience functions to have a progress bar for the job.
613// this allows to show the gui indicator of the job even before it got scheduled
614void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
615{
616 if(IS_NULL_PTR(job)) return;
618 if(cancellable)
620}
621
622void dt_control_job_set_progress_message(dt_job_t *job, const char *message)
623{
624 if(IS_NULL_PTR(job) || !job->progress) return;
626}
627
633
635{
636 if(IS_NULL_PTR(job) || !job->progress) return -1.0;
638}
639
640
641// moved out of control.c to be able to make some helper functions static
643{
644 // start threads
645 control->num_threads = dt_worker_threads();
646 control->thread = (pthread_t *)calloc(control->num_threads, sizeof(pthread_t));
647 control->job = (dt_job_t **)calloc(control->num_threads, sizeof(dt_job_t *));
649 control->running = 1;
651 for(int k = 0; k < control->num_threads; k++)
652 {
655 params->self = control;
656 params->threadid = k;
657 dt_pthread_create(&control->thread[k], dt_control_work, params, FALSE);
658 }
659
660 /* create queue kicker thread */
662
663 for(int k = 0; k < DT_CTL_WORKER_RESERVED; k++)
664 {
665 control->job_res[k] = NULL;
666 control->new_res[k] = 0;
669 params->self = control;
670 params->threadid = k;
672 }
673}
674
676{
677 // Detach every queued job under the lock, then dispose them outside it. Disposing a job runs
678 // its callbacks (state_changed_cb via DT_JOB_STATE_DISPOSED, then params_destroy), which for
679 // module jobs point into a plug-in .so -- so these callbacks must NOT be invoked while holding
680 // queue_mutex (re-entrancy), and the whole drain must happen before those .so files are
681 // unloaded. Workers are already joined when this runs, so detaching the lists is race-free.
682 GList *doomed = NULL;
684 for(int i = 0; i < DT_JOB_QUEUE_MAX; i++)
685 {
686 doomed = g_list_concat(doomed, control->queues[i]);
687 control->queues[i] = NULL;
688 control->queue_length[i] = 0;
689 }
690 control->export_scheduled = FALSE;
692
693 for(GList *l = doomed; l; l = g_list_next(l))
695 g_list_free(doomed);
696}
697
699{
700 // Normally the queues were already drained by dt_control_shutdown() (while plug-in .so files
701 // were still mapped). Drain again as a safety net for any path that skips shutdown (e.g. the
702 // headless export run); it is a no-op when the queues are already empty.
703 dt_control_jobs_drain(control);
704
705 dt_free(control->job);
706 dt_free(control->thread);
707}
708
709// clang-format off
710// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
711// vim: shiftwidth=2 expandtab tabstop=2 cindent
712// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
713// 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:651
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
Definition darktable.c:518
int dt_worker_threads()
Definition darktable.c:2381
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
static void dt_control_job_execute(_dt_job_t *job)
Definition jobs.c:306
void dt_control_flush_jobs_queue(dt_control_t *control, dt_job_queue_t queue_id)
Definition jobs.c:386
static _dt_job_t * dt_control_schedule_job(dt_control_t *control)
Definition jobs.c:243
static int32_t dt_control_run_job(dt_control_t *control)
Definition jobs.c:326
static int32_t dt_control_run_job_res(dt_control_t *control, int32_t res)
Definition jobs.c:206
static void dt_control_job_set_state(_dt_job_t *job, dt_job_state_t state)
Definition jobs.c:90
dt_job_state_t dt_control_job_get_state(_dt_job_t *job)
Definition jobs.c:105
void dt_control_job_cancel(_dt_job_t *job)
Definition jobs.c:182
static void * dt_control_work(void *ptr)
Definition jobs.c:585
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:137
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:407
int32_t dt_control_add_job_res(dt_control_t *control, _dt_job_t *job, int32_t res)
Definition jobs.c:351
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:131
double dt_control_job_get_progress(dt_job_t *job)
Definition jobs.c:634
static void dt_control_job_print(_dt_job_t *job)
Definition jobs.c:176
void dt_control_jobs_drain(dt_control_t *control)
Definition jobs.c:675
void dt_control_job_set_progress(dt_job_t *job, double value)
Definition jobs.c:628
void dt_control_jobs_cleanup(dt_control_t *control)
Definition jobs.c:698
void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
Definition jobs.c:614
void dt_control_job_set_progress_message(dt_job_t *job, const char *message)
Definition jobs.c:622
#define DT_CONTROL_FG_PRIORITY
Definition jobs.c:33
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:122
void dt_control_job_wait(_dt_job_t *job)
Definition jobs.c:187
void dt_control_job_set_state_callback(_dt_job_t *job, dt_job_state_change_callback cb)
Definition jobs.c:167
int32_t dt_control_get_threadid()
Definition jobs.c:528
static void * dt_control_work_res(void *ptr)
Definition jobs.c:540
void dt_control_jobs_init(dt_control_t *control)
Definition jobs.c:642
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:114
static void * dt_control_worker_kicker(void *ptr)
Definition jobs.c:571
static int dt_control_job_equal(_dt_job_t *j1, _dt_job_t *j2)
Definition jobs.c:80
#define DT_CONTROL_MAX_JOBS
Definition jobs.c:40
static __thread int threadid
Definition jobs.c:526
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:155
static int32_t dt_control_get_threadid_res()
Definition jobs.c:534
void(* dt_job_state_change_callback)(dt_job_t *, dt_job_state_t state)
Definition jobs.h:65
#define DT_CONTROL_DESCRIPTION_LEN
Definition jobs.h:36
dt_job_queue_t
Definition jobs.h:53
@ DT_JOB_QUEUE_MAX
Definition jobs.h:59
@ DT_JOB_QUEUE_USER_BG
Definition jobs.h:56
@ DT_JOB_QUEUE_USER_EXPORT
Definition jobs.h:57
@ DT_JOB_QUEUE_SYSTEM_BG
Definition jobs.h:58
@ DT_JOB_QUEUE_SYSTEM_FG
Definition jobs.h:55
int32_t(* dt_job_execute_callback)(dt_job_t *)
Definition jobs.h:64
void(* dt_job_destroy_callback)(void *data)
Definition jobs.h:66
#define DT_CTL_WORKER_RESERVED
Definition jobs.h:38
dt_job_state_t
Definition jobs.h:42
@ DT_JOB_STATE_DISCARDED
Definition jobs.h:48
@ DT_JOB_STATE_INITIALIZED
Definition jobs.h:43
@ DT_JOB_STATE_DISPOSED
Definition jobs.h:49
@ DT_JOB_STATE_RUNNING
Definition jobs.h:45
@ DT_JOB_STATE_CANCELLED
Definition jobs.h:47
@ DT_JOB_STATE_FINISHED
Definition jobs.h:46
@ DT_JOB_STATE_QUEUED
Definition jobs.h:44
@ 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:361
void dt_control_progress_set_message(dt_control_t *control, dt_progress_t *progress, const char *message)
Definition progress.c:395
double dt_control_progress_get_progress(dt_progress_t *progress)
Definition progress.c:379
dt_progress_t * dt_control_progress_create(dt_control_t *control, gboolean has_progress_bar, const gchar *message)
Definition progress.c:268
void dt_control_progress_destroy(dt_control_t *control, dt_progress_t *progress)
Definition progress.c:296
void dt_control_progress_attach_job(dt_control_t *control, dt_progress_t *progress, dt_job_t *job)
Definition progress.c:339
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:63
dt_job_queue_t queue
Definition jobs.c:65
dt_progress_t * progress
Definition jobs.c:69
dt_pthread_mutex_t state_mutex
Definition jobs.c:60
size_t params_size
Definition jobs.c:56
dt_job_destroy_callback params_destroy
Definition jobs.c:57
unsigned char priority
Definition jobs.c:64
dt_job_state_change_callback state_changed_cb
Definition jobs.c:67
int32_t result
Definition jobs.c:58
void * params
Definition jobs.c:55
dt_pthread_mutex_t wait_mutex
Definition jobs.c:61
char description[DT_CONTROL_DESCRIPTION_LEN]
Definition jobs.c:71
dt_job_execute_callback execute
Definition jobs.c:54
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:48
static double dt_get_wtime(void)
Definition times.h:43
#define sleep(n)
Definition win.h:49