Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
dtpthread.h
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#pragma once
20
21#include "external/ThreadSafetyAnalysis.h"
22#include <assert.h>
23#include <errno.h>
24#include <float.h>
25#include <glib.h>
26#include <pthread.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#ifdef _DEBUG
32
33// copied from darktable.h so we don't need to include the header
34#include <sys/time.h>
35static inline double dt_pthread_get_wtime()
36{
37 struct timeval time;
38 gettimeofday(&time, NULL);
39 return time.tv_sec - 1290608000 + (1.0 / 1000000.0) * time.tv_usec;
40}
41
42
43#define TOPN 3
44typedef struct CAPABILITY("mutex") dt_pthread_mutex_t
45{
46 pthread_mutex_t mutex;
47 char name[256];
48 double time_locked;
49 double time_sum_wait;
50 double time_sum_locked;
51 char top_locked_name[TOPN][256];
52 double top_locked_sum[TOPN];
53 char top_wait_name[TOPN][256];
54 double top_wait_sum[TOPN];
55} CAPABILITY("mutex") dt_pthread_mutex_t;
56
57typedef struct dt_pthread_rwlock_t
58{
59 pthread_rwlock_t lock;
60 int cnt;
61 pthread_t writer;
62 char name[256];
64
65static inline int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
66{
67 const int ret = pthread_mutex_destroy(&(mutex->mutex));
68 assert(!ret);
69
70#if 0
71 printf("\n[mutex] stats for mutex `%s':\n", mutex->name);
72 printf("[mutex] total time locked: %.3f secs\n", mutex->time_sum_locked);
73 printf("[mutex] total wait time : %.3f secs\n", mutex->time_sum_wait);
74 printf("[mutex] top %d lockers :\n", TOPN);
75 for(int k=0; k<TOPN; k++) printf("[mutex] %.3f secs : `%s'\n", mutex->top_locked_sum[k],
76 mutex->top_locked_name[k]);
77 printf("[mutex] top %d waiters :\n", TOPN);
78 for(int k=0; k<TOPN; k++) printf("[mutex] %.3f secs : `%s'\n", mutex->top_wait_sum[k],
79 mutex->top_wait_name[k]);
80#endif
81
82 return ret;
83}
84
85#define dt_pthread_mutex_init(A, B) dt_pthread_mutex_init_with_caller(A, B, __FILE__, __LINE__, __FUNCTION__)
86static inline int dt_pthread_mutex_init_with_caller(dt_pthread_mutex_t *mutex,
87 const pthread_mutexattr_t *attr, const char *file,
88 const int line, const char *function)
89{
90 memset(mutex, 0x0, sizeof(dt_pthread_mutex_t));
91 snprintf(mutex->name, sizeof(mutex->name), "%s:%d (%s)", file, line, function);
92#if defined(__OpenBSD__)
93 if(attr == NULL)
94 {
95 pthread_mutexattr_t a;
96 pthread_mutexattr_init(&a);
97 pthread_mutexattr_settype(&a, PTHREAD_MUTEX_NORMAL);
98 const int ret = pthread_mutex_init(&(mutex->mutex), &a);
99 pthread_mutexattr_destroy(&a);
100 return ret;
101 }
102#endif
103 const int ret = pthread_mutex_init(&(mutex->mutex), attr);
104 assert(!ret);
105 return ret;
106}
107
108#define dt_pthread_mutex_lock(A) dt_pthread_mutex_lock_with_caller(A, __FILE__, __LINE__, __FUNCTION__)
109static inline int dt_pthread_mutex_lock_with_caller(dt_pthread_mutex_t *mutex, const char *file,
110 const int line, const char *function)
111 ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
112{
113 const double t0 = dt_pthread_get_wtime();
114 const int ret = pthread_mutex_lock(&(mutex->mutex));
115 assert(!ret);
116 mutex->time_locked = dt_pthread_get_wtime();
117 double wait = mutex->time_locked - t0;
118 mutex->time_sum_wait += wait;
119 char *name = mutex->name;
120 snprintf(mutex->name, sizeof(mutex->name), "%s:%d (%s)", file, line, function);
121 // TODO: have a -d thread option
122 //fprintf(stdout, "Thread lock %s acquired\n", mutex->name);
123 int min_wait_slot = 0;
124 for(int k = 0; k < TOPN; k++)
125 {
126 if(mutex->top_wait_sum[k] < mutex->top_wait_sum[min_wait_slot]) min_wait_slot = k;
127 if(!strncmp(name, mutex->top_wait_name[k], 256))
128 {
129 mutex->top_wait_sum[k] += wait;
130 return ret;
131 }
132 }
133 g_strlcpy(mutex->top_wait_name[min_wait_slot], name, sizeof(mutex->top_wait_name[min_wait_slot]));
134 mutex->top_wait_sum[min_wait_slot] = wait;
135 return ret;
136}
137
138#define dt_pthread_mutex_trylock(A) dt_pthread_mutex_trylock_with_caller(A, __FILE__, __LINE__, __FUNCTION__)
139static inline int dt_pthread_mutex_trylock_with_caller(dt_pthread_mutex_t *mutex, const char *file,
140 const int line, const char *function)
141 TRY_ACQUIRE(0, mutex)
142{
143 const double t0 = dt_pthread_get_wtime();
144 const int ret = pthread_mutex_trylock(&(mutex->mutex));
145 assert(!ret || (ret == EBUSY));
146 if(ret) return ret;
147 mutex->time_locked = dt_pthread_get_wtime();
148 double wait = mutex->time_locked - t0;
149 mutex->time_sum_wait += wait;
150 char *name = mutex->name;
151 snprintf(mutex->name, sizeof(mutex->name), "%s:%d (%s)", file, line, function);
152 int min_wait_slot = 0;
153 for(int k = 0; k < TOPN; k++)
154 {
155 if(mutex->top_wait_sum[k] < mutex->top_wait_sum[min_wait_slot]) min_wait_slot = k;
156 if(!strncmp(name, mutex->top_wait_name[k], 256))
157 {
158 mutex->top_wait_sum[k] += wait;
159 return ret;
160 }
161 }
162 g_strlcpy(mutex->top_wait_name[min_wait_slot], name, sizeof(mutex->top_wait_name[min_wait_slot]));
163 mutex->top_wait_sum[min_wait_slot] = wait;
164 return ret;
165}
166
167#define dt_pthread_mutex_unlock(A) dt_pthread_mutex_unlock_with_caller(A, __FILE__, __LINE__, __FUNCTION__)
168static inline int dt_pthread_mutex_unlock_with_caller(dt_pthread_mutex_t *mutex, const char *file,
169 const int line, const char *function)
170 RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
171{
172 const double t0 = dt_pthread_get_wtime();
173 const double locked = t0 - mutex->time_locked;
174 mutex->time_sum_locked += locked;
175
176 char *name = mutex->name;
177 snprintf(mutex->name, sizeof(mutex->name), "%s:%d (%s)", file, line, function);
178 // TODO: have a -d thread debug arg
179 //fprintf(stdout, "Thread lock %s released\n", mutex->name);
180 int min_locked_slot = 0;
181 for(int k = 0; k < TOPN; k++)
182 {
183 if(mutex->top_locked_sum[k] < mutex->top_locked_sum[min_locked_slot]) min_locked_slot = k;
184 if(!strncmp(name, mutex->top_locked_name[k], 256))
185 {
186 mutex->top_locked_sum[k] += locked;
187 min_locked_slot = -1;
188 break;
189 }
190 }
191 if(min_locked_slot >= 0)
192 {
193 g_strlcpy(mutex->top_locked_name[min_locked_slot], name, sizeof(mutex->top_locked_name[min_locked_slot]));
194 mutex->top_locked_sum[min_locked_slot] = locked;
195 }
196
197 // need to unlock last, to shield our internal data.
198 const int ret = pthread_mutex_unlock(&(mutex->mutex));
199 assert(!ret);
200 return ret;
201}
202
203static inline int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex)
204{
205 return pthread_cond_wait(cond, &(mutex->mutex));
206}
207
208
209static inline int dt_pthread_rwlock_init(dt_pthread_rwlock_t *lock,
210 const pthread_rwlockattr_t *attr)
211{
212 memset(lock, 0, sizeof(dt_pthread_rwlock_t));
213 lock->cnt = 0;
214 const int res = pthread_rwlock_init(&lock->lock, attr);
215 assert(!res);
216 return res;
217}
218
219static inline int dt_pthread_rwlock_destroy(dt_pthread_rwlock_t *lock)
220{
221 snprintf(lock->name, sizeof(lock->name), "destroyed with cnt %d", lock->cnt);
222 const int res = pthread_rwlock_destroy(&lock->lock);
223 assert(!res);
224 return res;
225}
226
227static inline pthread_t dt_pthread_rwlock_get_writer(dt_pthread_rwlock_t *lock)
228{
229 return lock->writer;
230}
231
232#define dt_pthread_rwlock_unlock(A) dt_pthread_rwlock_unlock_with_caller(A, __FILE__, __LINE__)
233static inline int dt_pthread_rwlock_unlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
234{
235 const int res = pthread_rwlock_unlock(&rwlock->lock);
236
237 assert(!res);
238
239 __sync_fetch_and_sub(&(rwlock->cnt), 1);
240 assert(rwlock->cnt >= 0);
241 __sync_bool_compare_and_swap(&(rwlock->writer), pthread_self(), 0);
242 if(!res) snprintf(rwlock->name, sizeof(rwlock->name), "u:%s:%d", file, line);
243 return res;
244}
245
246#define dt_pthread_rwlock_rdlock(A) dt_pthread_rwlock_rdlock_with_caller(A, __FILE__, __LINE__)
247static inline int dt_pthread_rwlock_rdlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
248{
249 const int res = pthread_rwlock_rdlock(&rwlock->lock);
250 assert(!res);
251 assert(!(res && pthread_equal(rwlock->writer, pthread_self())));
252 __sync_fetch_and_add(&(rwlock->cnt), 1);
253 if(!res)
254 snprintf(rwlock->name, sizeof(rwlock->name), "r:%s:%d", file, line);
255 return res;
256}
257#define dt_pthread_rwlock_wrlock(A) dt_pthread_rwlock_wrlock_with_caller(A, __FILE__, __LINE__)
258static inline int dt_pthread_rwlock_wrlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
259{
260 const int res = pthread_rwlock_wrlock(&rwlock->lock);
261 assert(!res);
262 __sync_fetch_and_add(&(rwlock->cnt), 1);
263 if(!res)
264 {
265 __sync_lock_test_and_set(&(rwlock->writer), pthread_self());
266 snprintf(rwlock->name, sizeof(rwlock->name), "w:%s:%d", file, line);
267 }
268 return res;
269}
270#define dt_pthread_rwlock_tryrdlock(A) dt_pthread_rwlock_tryrdlock_with_caller(A, __FILE__, __LINE__)
271static inline int dt_pthread_rwlock_tryrdlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
272{
273 const int res = pthread_rwlock_tryrdlock(&rwlock->lock);
274 assert(!res || (res == EBUSY));
275 assert(!(res && pthread_equal(rwlock->writer, pthread_self())));
276 if(!res)
277 {
278 __sync_fetch_and_add(&(rwlock->cnt), 1);
279 snprintf(rwlock->name, sizeof(rwlock->name), "tr:%s:%d", file, line);
280 }
281 return res;
282}
283#define dt_pthread_rwlock_trywrlock(A) dt_pthread_rwlock_trywrlock_with_caller(A, __FILE__, __LINE__)
284static inline int dt_pthread_rwlock_trywrlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
285{
286 const int res = pthread_rwlock_trywrlock(&rwlock->lock);
287 assert(!res || (res == EBUSY));
288 if(!res)
289 {
290 __sync_fetch_and_add(&(rwlock->cnt), 1);
291 __sync_lock_test_and_set(&(rwlock->writer), pthread_self());
292 snprintf(rwlock->name, sizeof(rwlock->name), "tw:%s:%d", file, line);
293 }
294 return res;
295}
296
297#undef TOPN
298#else
299
300typedef struct CAPABILITY("mutex") dt_pthread_mutex_t
301{
302 pthread_mutex_t mutex;
303} CAPABILITY("mutex") dt_pthread_mutex_t;
304
305// *please* do use these;
306static inline int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
307{
308 return pthread_mutex_init(&mutex->mutex, mutexattr);
309};
310
311static inline int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
312{
313 return pthread_mutex_lock(&mutex->mutex);
314};
315
316static inline int dt_pthread_mutex_trylock(dt_pthread_mutex_t *mutex) TRY_ACQUIRE(0, mutex)
317{
318 return pthread_mutex_trylock(&mutex->mutex);
319};
320
321static inline int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
322{
323 return pthread_mutex_unlock(&mutex->mutex);
324};
325
326static inline int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
327{
328 return pthread_mutex_destroy(&mutex->mutex);
329};
330
331static inline int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex)
332{
333 return pthread_cond_wait(cond, &mutex->mutex);
334};
335
336#define dt_pthread_rwlock_t pthread_rwlock_t
337#define dt_pthread_rwlock_init pthread_rwlock_init
338#define dt_pthread_rwlock_destroy pthread_rwlock_destroy
339#define dt_pthread_rwlock_unlock pthread_rwlock_unlock
340#define dt_pthread_rwlock_rdlock pthread_rwlock_rdlock
341#define dt_pthread_rwlock_wrlock pthread_rwlock_wrlock
342#define dt_pthread_rwlock_tryrdlock pthread_rwlock_tryrdlock
343#define dt_pthread_rwlock_trywrlock pthread_rwlock_trywrlock
344
345#define dt_pthread_rwlock_rdlock_with_caller(A,B,C) pthread_rwlock_rdlock(A)
346#define dt_pthread_rwlock_wrlock_with_caller(A,B,C) pthread_rwlock_wrlock(A)
347#define dt_pthread_rwlock_tryrdlock_with_caller(A,B,C) pthread_rwlock_tryrdlock(A)
348#define dt_pthread_rwlock_trywrlock_with_caller(A,B,C) pthread_rwlock_trywrlock(A)
349
350#endif
351
352// if at all possible, do NOT use.
353static inline int dt_pthread_mutex_BAD_lock(dt_pthread_mutex_t *mutex)
354{
355 return pthread_mutex_lock(&mutex->mutex);
356};
357
358static inline int dt_pthread_mutex_BAD_trylock(dt_pthread_mutex_t *mutex)
359{
360 return pthread_mutex_trylock(&mutex->mutex);
361};
362
363static inline int dt_pthread_mutex_BAD_unlock(dt_pthread_mutex_t *mutex)
364{
365 return pthread_mutex_unlock(&mutex->mutex);
366};
367
368int dt_pthread_create(pthread_t *thread, void *(*start_routine)(void *), void *arg, const gboolean realtime);
369
370void dt_pthread_setname(const char *name);
371
372// clang-format off
373// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
374// vim: shiftwidth=2 expandtab tabstop=2 cindent
375// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
376// clang-format on
@ ACQUIRE
Definition colormapping.c:72
char * name
Definition common/metadata.c:41
static int dt_pthread_mutex_BAD_lock(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:353
int dt_pthread_create(pthread_t *thread, void *(*start_routine)(void *), void *arg, const gboolean realtime)
Definition dtpthread.c:36
struct CAPABILITY("mutex") dt_pthread_mutex_t
Definition dtpthread.h:300
#define dt_pthread_rwlock_destroy
Definition dtpthread.h:338
static int dt_pthread_mutex_BAD_trylock(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:358
#define dt_pthread_rwlock_wrlock_with_caller(A, B, C)
Definition dtpthread.h:346
static int dt_pthread_mutex_BAD_unlock(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:363
#define dt_pthread_rwlock_trywrlock_with_caller(A, B, C)
Definition dtpthread.h:348
#define dt_pthread_rwlock_tryrdlock_with_caller(A, B, C)
Definition dtpthread.h:347
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:321
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:306
static int mutex
Definition dtpthread.h:317
#define dt_pthread_rwlock_t
Definition dtpthread.h:336
static int dt_pthread_mutex_trylock(dt_pthread_mutex_t *mutex) TRY_ACQUIRE(0
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:326
void dt_pthread_setname(const char *name)
Definition dtpthread.c:107
static int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex)
Definition dtpthread.h:331
#define dt_pthread_rwlock_init
Definition dtpthread.h:337
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:311
#define dt_pthread_rwlock_rdlock_with_caller(A, B, C)
Definition dtpthread.h:345
k
Definition derive_filmic_v6_gamut_mapping.py:43
ret
Definition update_modelines.py:95