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>
dt_pthread_get_wtime()35 static 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
44 typedef 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 
57 typedef struct dt_pthread_rwlock_t
58 {
59   pthread_rwlock_t lock;
60   int cnt;
61   pthread_t writer;
62   char name[256];
63 } dt_pthread_rwlock_t;
64 
dt_pthread_mutex_destroy(dt_pthread_mutex_t * mutex)65 static 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__)
dt_pthread_mutex_init_with_caller(dt_pthread_mutex_t * mutex,const pthread_mutexattr_t * attr,const char * file,const int line,const char * function)86 static 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__)
dt_pthread_mutex_lock_with_caller(dt_pthread_mutex_t * mutex,const char * file,const int line,const char * function)109 static 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   int min_wait_slot = 0;
122   for(int k = 0; k < TOPN; k++)
123   {
124     if(mutex->top_wait_sum[k] < mutex->top_wait_sum[min_wait_slot]) min_wait_slot = k;
125     if(!strncmp(name, mutex->top_wait_name[k], 256))
126     {
127       mutex->top_wait_sum[k] += wait;
128       return ret;
129     }
130   }
131   g_strlcpy(mutex->top_wait_name[min_wait_slot], name, sizeof(mutex->top_wait_name[min_wait_slot]));
132   mutex->top_wait_sum[min_wait_slot] = wait;
133   return ret;
134 }
135 
136 #define dt_pthread_mutex_trylock(A) dt_pthread_mutex_trylock_with_caller(A, __FILE__, __LINE__, __FUNCTION__)
dt_pthread_mutex_trylock_with_caller(dt_pthread_mutex_t * mutex,const char * file,const int line,const char * function)137 static inline int dt_pthread_mutex_trylock_with_caller(dt_pthread_mutex_t *mutex, const char *file,
138                                                        const int line, const char *function)
139   TRY_ACQUIRE(0, mutex)
140 {
141   const double t0 = dt_pthread_get_wtime();
142   const int ret = pthread_mutex_trylock(&(mutex->mutex));
143   assert(!ret || (ret == EBUSY));
144   if(ret) return ret;
145   mutex->time_locked = dt_pthread_get_wtime();
146   double wait = mutex->time_locked - t0;
147   mutex->time_sum_wait += wait;
148   char *name = mutex->name;
149   snprintf(mutex->name, sizeof(mutex->name), "%s:%d (%s)", file, line, function);
150   int min_wait_slot = 0;
151   for(int k = 0; k < TOPN; k++)
152   {
153     if(mutex->top_wait_sum[k] < mutex->top_wait_sum[min_wait_slot]) min_wait_slot = k;
154     if(!strncmp(name, mutex->top_wait_name[k], 256))
155     {
156       mutex->top_wait_sum[k] += wait;
157       return ret;
158     }
159   }
160   g_strlcpy(mutex->top_wait_name[min_wait_slot], name, sizeof(mutex->top_wait_name[min_wait_slot]));
161   mutex->top_wait_sum[min_wait_slot] = wait;
162   return ret;
163 }
164 
165 #define dt_pthread_mutex_unlock(A) dt_pthread_mutex_unlock_with_caller(A, __FILE__, __LINE__, __FUNCTION__)
dt_pthread_mutex_unlock_with_caller(dt_pthread_mutex_t * mutex,const char * file,const int line,const char * function)166 static inline int dt_pthread_mutex_unlock_with_caller(dt_pthread_mutex_t *mutex, const char *file,
167                                                       const int line, const char *function)
168   RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
169 {
170   const double t0 = dt_pthread_get_wtime();
171   const double locked = t0 - mutex->time_locked;
172   mutex->time_sum_locked += locked;
173 
174   char *name = mutex->name;
175   snprintf(mutex->name, sizeof(mutex->name), "%s:%d (%s)", file, line, function);
176   int min_locked_slot = 0;
177   for(int k = 0; k < TOPN; k++)
178   {
179     if(mutex->top_locked_sum[k] < mutex->top_locked_sum[min_locked_slot]) min_locked_slot = k;
180     if(!strncmp(name, mutex->top_locked_name[k], 256))
181     {
182       mutex->top_locked_sum[k] += locked;
183       min_locked_slot = -1;
184       break;
185     }
186   }
187   if(min_locked_slot >= 0)
188   {
189     g_strlcpy(mutex->top_locked_name[min_locked_slot], name, sizeof(mutex->top_locked_name[min_locked_slot]));
190     mutex->top_locked_sum[min_locked_slot] = locked;
191   }
192 
193   // need to unlock last, to shield our internal data.
194   const int ret = pthread_mutex_unlock(&(mutex->mutex));
195   assert(!ret);
196   return ret;
197 }
198 
dt_pthread_cond_wait(pthread_cond_t * cond,dt_pthread_mutex_t * mutex)199 static inline int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex)
200 {
201   return pthread_cond_wait(cond, &(mutex->mutex));
202 }
203 
204 
dt_pthread_rwlock_init(dt_pthread_rwlock_t * lock,const pthread_rwlockattr_t * attr)205 static inline int dt_pthread_rwlock_init(dt_pthread_rwlock_t *lock,
206     const pthread_rwlockattr_t *attr)
207 {
208   memset(lock, 0, sizeof(dt_pthread_rwlock_t));
209   lock->cnt = 0;
210   const int res = pthread_rwlock_init(&lock->lock, attr);
211   assert(!res);
212   return res;
213 }
214 
dt_pthread_rwlock_destroy(dt_pthread_rwlock_t * lock)215 static inline int dt_pthread_rwlock_destroy(dt_pthread_rwlock_t *lock)
216 {
217   snprintf(lock->name, sizeof(lock->name), "destroyed with cnt %d", lock->cnt);
218   const int res = pthread_rwlock_destroy(&lock->lock);
219   assert(!res);
220   return res;
221 }
222 
dt_pthread_rwlock_get_writer(dt_pthread_rwlock_t * lock)223 static inline pthread_t dt_pthread_rwlock_get_writer(dt_pthread_rwlock_t *lock)
224 {
225   return lock->writer;
226 }
227 
228 #define dt_pthread_rwlock_unlock(A) dt_pthread_rwlock_unlock_with_caller(A, __FILE__, __LINE__)
dt_pthread_rwlock_unlock_with_caller(dt_pthread_rwlock_t * rwlock,const char * file,int line)229 static inline int dt_pthread_rwlock_unlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
230 {
231   const int res = pthread_rwlock_unlock(&rwlock->lock);
232 
233   assert(!res);
234 
235   __sync_fetch_and_sub(&(rwlock->cnt), 1);
236   assert(rwlock->cnt >= 0);
237   __sync_bool_compare_and_swap(&(rwlock->writer), pthread_self(), 0);
238   if(!res) snprintf(rwlock->name, sizeof(rwlock->name), "u:%s:%d", file, line);
239   return res;
240 }
241 
242 #define dt_pthread_rwlock_rdlock(A) dt_pthread_rwlock_rdlock_with_caller(A, __FILE__, __LINE__)
dt_pthread_rwlock_rdlock_with_caller(dt_pthread_rwlock_t * rwlock,const char * file,int line)243 static inline int dt_pthread_rwlock_rdlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
244 {
245   const int res = pthread_rwlock_rdlock(&rwlock->lock);
246   assert(!res);
247   assert(!(res && pthread_equal(rwlock->writer, pthread_self())));
248   __sync_fetch_and_add(&(rwlock->cnt), 1);
249   if(!res)
250     snprintf(rwlock->name, sizeof(rwlock->name), "r:%s:%d", file, line);
251   return res;
252 }
253 #define dt_pthread_rwlock_wrlock(A) dt_pthread_rwlock_wrlock_with_caller(A, __FILE__, __LINE__)
dt_pthread_rwlock_wrlock_with_caller(dt_pthread_rwlock_t * rwlock,const char * file,int line)254 static inline int dt_pthread_rwlock_wrlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
255 {
256   const int res = pthread_rwlock_wrlock(&rwlock->lock);
257   assert(!res);
258   __sync_fetch_and_add(&(rwlock->cnt), 1);
259   if(!res)
260   {
261     __sync_lock_test_and_set(&(rwlock->writer), pthread_self());
262     snprintf(rwlock->name, sizeof(rwlock->name), "w:%s:%d", file, line);
263   }
264   return res;
265 }
266 #define dt_pthread_rwlock_tryrdlock(A) dt_pthread_rwlock_tryrdlock_with_caller(A, __FILE__, __LINE__)
dt_pthread_rwlock_tryrdlock_with_caller(dt_pthread_rwlock_t * rwlock,const char * file,int line)267 static inline int dt_pthread_rwlock_tryrdlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
268 {
269   const int res = pthread_rwlock_tryrdlock(&rwlock->lock);
270   assert(!res || (res == EBUSY));
271   assert(!(res && pthread_equal(rwlock->writer, pthread_self())));
272   if(!res)
273   {
274     __sync_fetch_and_add(&(rwlock->cnt), 1);
275     snprintf(rwlock->name, sizeof(rwlock->name), "tr:%s:%d", file, line);
276   }
277   return res;
278 }
279 #define dt_pthread_rwlock_trywrlock(A) dt_pthread_rwlock_trywrlock_with_caller(A, __FILE__, __LINE__)
dt_pthread_rwlock_trywrlock_with_caller(dt_pthread_rwlock_t * rwlock,const char * file,int line)280 static inline int dt_pthread_rwlock_trywrlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line)
281 {
282   const int res = pthread_rwlock_trywrlock(&rwlock->lock);
283   assert(!res || (res == EBUSY));
284   if(!res)
285   {
286     __sync_fetch_and_add(&(rwlock->cnt), 1);
287     __sync_lock_test_and_set(&(rwlock->writer), pthread_self());
288     snprintf(rwlock->name, sizeof(rwlock->name), "tw:%s:%d", file, line);
289   }
290   return res;
291 }
292 
293 #undef TOPN
294 #else
295 
296 typedef struct CAPABILITY("mutex") dt_pthread_mutex_t
297 {
298   pthread_mutex_t mutex;
299 } CAPABILITY("mutex") dt_pthread_mutex_t;
300 
301 // *please* do use these;
dt_pthread_mutex_init(dt_pthread_mutex_t * mutex,const pthread_mutexattr_t * mutexattr)302 static inline int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
303 {
304   return pthread_mutex_init(&mutex->mutex, mutexattr);
305 };
306 
dt_pthread_mutex_lock(dt_pthread_mutex_t * mutex)307 static inline int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
308 {
309   return pthread_mutex_lock(&mutex->mutex);
310 };
311 
dt_pthread_mutex_trylock(dt_pthread_mutex_t * mutex)312 static inline int dt_pthread_mutex_trylock(dt_pthread_mutex_t *mutex) TRY_ACQUIRE(0, mutex)
313 {
314   return pthread_mutex_trylock(&mutex->mutex);
315 };
316 
dt_pthread_mutex_unlock(dt_pthread_mutex_t * mutex)317 static inline int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
318 {
319   return pthread_mutex_unlock(&mutex->mutex);
320 };
321 
dt_pthread_mutex_destroy(dt_pthread_mutex_t * mutex)322 static inline int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
323 {
324   return pthread_mutex_destroy(&mutex->mutex);
325 };
326 
dt_pthread_cond_wait(pthread_cond_t * cond,dt_pthread_mutex_t * mutex)327 static inline int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex)
328 {
329   return pthread_cond_wait(cond, &mutex->mutex);
330 };
331 
332 #define dt_pthread_rwlock_t pthread_rwlock_t
333 #define dt_pthread_rwlock_init pthread_rwlock_init
334 #define dt_pthread_rwlock_destroy pthread_rwlock_destroy
335 #define dt_pthread_rwlock_unlock pthread_rwlock_unlock
336 #define dt_pthread_rwlock_rdlock pthread_rwlock_rdlock
337 #define dt_pthread_rwlock_wrlock pthread_rwlock_wrlock
338 #define dt_pthread_rwlock_tryrdlock pthread_rwlock_tryrdlock
339 #define dt_pthread_rwlock_trywrlock pthread_rwlock_trywrlock
340 
341 #define dt_pthread_rwlock_rdlock_with_caller(A,B,C) pthread_rwlock_rdlock(A)
342 #define dt_pthread_rwlock_wrlock_with_caller(A,B,C) pthread_rwlock_wrlock(A)
343 #define dt_pthread_rwlock_tryrdlock_with_caller(A,B,C) pthread_rwlock_tryrdlock(A)
344 #define dt_pthread_rwlock_trywrlock_with_caller(A,B,C) pthread_rwlock_trywrlock(A)
345 
346 #endif
347 
348 // if at all possible, do NOT use.
dt_pthread_mutex_BAD_lock(dt_pthread_mutex_t * mutex)349 static inline int dt_pthread_mutex_BAD_lock(dt_pthread_mutex_t *mutex)
350 {
351   return pthread_mutex_lock(&mutex->mutex);
352 };
353 
dt_pthread_mutex_BAD_trylock(dt_pthread_mutex_t * mutex)354 static inline int dt_pthread_mutex_BAD_trylock(dt_pthread_mutex_t *mutex)
355 {
356   return pthread_mutex_trylock(&mutex->mutex);
357 };
358 
dt_pthread_mutex_BAD_unlock(dt_pthread_mutex_t * mutex)359 static inline int dt_pthread_mutex_BAD_unlock(dt_pthread_mutex_t *mutex)
360 {
361   return pthread_mutex_unlock(&mutex->mutex);
362 };
363 
364 int dt_pthread_create(pthread_t *thread, void *(*start_routine)(void *), void *arg);
365 
366 void dt_pthread_setname(const char *name);
367 
368 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
369 // vim: shiftwidth=2 expandtab tabstop=2 cindent
370 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
371