31#include "external/ThreadSafetyAnalysis.h"
45static inline double dt_pthread_get_wtime()
48 gettimeofday(&time, NULL);
49 return time.tv_sec - 1290608000 + (1.0 / 1000000.0) * time.tv_usec;
54typedef struct CAPABILITY(
"mutex") dt_pthread_mutex_t
56 pthread_mutex_t
mutex;
60 double time_sum_locked;
61 char top_locked_name[TOPN][256];
62 double top_locked_sum[TOPN];
63 char top_wait_name[TOPN][256];
64 double top_wait_sum[TOPN];
69 pthread_rwlock_t lock;
78 const int ret = pthread_mutex_destroy(&(
mutex->mutex));
82 printf(
"\n[mutex] stats for mutex `%s':\n",
mutex->name);
83 printf(
"[mutex] total time locked: %.3f secs\n",
mutex->time_sum_locked);
84 printf(
"[mutex] total wait time : %.3f secs\n",
mutex->time_sum_wait);
85 printf(
"[mutex] top %d lockers :\n", TOPN);
86 for(
int k=0;
k<TOPN;
k++) printf(
"[mutex] %.3f secs : `%s'\n",
mutex->top_locked_sum[k],
87 mutex->top_locked_name[k]);
88 printf(
"[mutex] top %d waiters :\n", TOPN);
89 for(
int k=0;
k<TOPN;
k++) printf(
"[mutex] %.3f secs : `%s'\n",
mutex->top_wait_sum[k],
90 mutex->top_wait_name[k]);
96#define dt_pthread_mutex_init(A, B) dt_pthread_mutex_init_with_caller(A, B, __FILE__, __LINE__, __FUNCTION__)
97static inline int dt_pthread_mutex_init_with_caller(dt_pthread_mutex_t *
mutex,
98 const pthread_mutexattr_t *attr,
const char *file,
99 const int line,
const char *function)
101 memset(
mutex, 0x0,
sizeof(dt_pthread_mutex_t));
102 snprintf(
mutex->name,
sizeof(
mutex->name),
"%s:%d (%s)", file, line, function);
103#if defined(__OpenBSD__)
106 pthread_mutexattr_t
a;
107 pthread_mutexattr_init(&
a);
108 pthread_mutexattr_settype(&
a, PTHREAD_MUTEX_NORMAL);
109 const int ret = pthread_mutex_init(&(
mutex->mutex), &
a);
110 pthread_mutexattr_destroy(&
a);
114 const int ret = pthread_mutex_init(&(
mutex->mutex), attr);
119#define dt_pthread_mutex_lock(A) dt_pthread_mutex_lock_with_caller(A, __FILE__, __LINE__, __FUNCTION__)
120static inline int dt_pthread_mutex_lock_with_caller(dt_pthread_mutex_t *
mutex,
const char *file,
121 const int line,
const char *function)
124 const double t0 = dt_pthread_get_wtime();
125 const int ret = pthread_mutex_lock(&(
mutex->mutex));
127 mutex->time_locked = dt_pthread_get_wtime();
128 double wait =
mutex->time_locked - t0;
129 mutex->time_sum_wait += wait;
131 snprintf(
mutex->name,
sizeof(
mutex->name),
"%s:%d (%s)", file, line, function);
134 int min_wait_slot = 0;
135 for(
int k = 0;
k < TOPN;
k++)
137 if(
mutex->top_wait_sum[k] <
mutex->top_wait_sum[min_wait_slot]) min_wait_slot =
k;
138 if(!strncmp(
name,
mutex->top_wait_name[k], 256))
140 mutex->top_wait_sum[
k] += wait;
144 g_strlcpy(
mutex->top_wait_name[min_wait_slot],
name,
sizeof(
mutex->top_wait_name[min_wait_slot]));
145 mutex->top_wait_sum[min_wait_slot] = wait;
149#define dt_pthread_mutex_trylock(A) dt_pthread_mutex_trylock_with_caller(A, __FILE__, __LINE__, __FUNCTION__)
150static inline int dt_pthread_mutex_trylock_with_caller(dt_pthread_mutex_t *
mutex,
const char *file,
151 const int line,
const char *function)
152 TRY_ACQUIRE(0,
mutex)
154 const double t0 = dt_pthread_get_wtime();
155 const int ret = pthread_mutex_trylock(&(
mutex->mutex));
156 assert(!ret || (ret == EBUSY));
158 mutex->time_locked = dt_pthread_get_wtime();
159 double wait =
mutex->time_locked - t0;
160 mutex->time_sum_wait += wait;
162 snprintf(
mutex->name,
sizeof(
mutex->name),
"%s:%d (%s)", file, line, function);
163 int min_wait_slot = 0;
164 for(
int k = 0;
k < TOPN;
k++)
166 if(
mutex->top_wait_sum[k] <
mutex->top_wait_sum[min_wait_slot]) min_wait_slot =
k;
167 if(!strncmp(
name,
mutex->top_wait_name[k], 256))
169 mutex->top_wait_sum[
k] += wait;
173 g_strlcpy(
mutex->top_wait_name[min_wait_slot],
name,
sizeof(
mutex->top_wait_name[min_wait_slot]));
174 mutex->top_wait_sum[min_wait_slot] = wait;
178#define dt_pthread_mutex_unlock(A) dt_pthread_mutex_unlock_with_caller(A, __FILE__, __LINE__, __FUNCTION__)
179static inline int dt_pthread_mutex_unlock_with_caller(dt_pthread_mutex_t *
mutex,
const char *file,
180 const int line,
const char *function)
181 RELEASE(
mutex) NO_THREAD_SAFETY_ANALYSIS
183 const double t0 = dt_pthread_get_wtime();
184 const double locked = t0 -
mutex->time_locked;
185 mutex->time_sum_locked += locked;
188 snprintf(
mutex->name,
sizeof(
mutex->name),
"%s:%d (%s)", file, line, function);
191 int min_locked_slot = 0;
192 for(
int k = 0;
k < TOPN;
k++)
194 if(
mutex->top_locked_sum[k] <
mutex->top_locked_sum[min_locked_slot]) min_locked_slot =
k;
195 if(!strncmp(
name,
mutex->top_locked_name[k], 256))
197 mutex->top_locked_sum[
k] += locked;
198 min_locked_slot = -1;
202 if(min_locked_slot >= 0)
204 g_strlcpy(
mutex->top_locked_name[min_locked_slot],
name,
sizeof(
mutex->top_locked_name[min_locked_slot]));
205 mutex->top_locked_sum[min_locked_slot] = locked;
209 const int ret = pthread_mutex_unlock(&(
mutex->mutex));
216 return pthread_cond_wait(cond, &(
mutex->mutex));
221 const pthread_rwlockattr_t *attr)
225 lock->writer_depth = 0;
226 const int res = pthread_rwlock_init(&lock->lock, attr);
233 if(lock->writer_depth != 0)
234 fprintf(stderr,
"ERROR: destroying rwlock still owned by writer (depth=%d cnt=%d last=%s)\n",
235 lock->writer_depth, lock->cnt, lock->name);
236 assert(lock->writer_depth == 0);
237 snprintf(lock->name,
sizeof(lock->name),
"destroyed with cnt %d", lock->cnt);
238 const int res = pthread_rwlock_destroy(&lock->lock);
248#define dt_pthread_rwlock_unlock(A) dt_pthread_rwlock_unlock_with_caller(A, __FILE__, __LINE__)
249static inline int dt_pthread_rwlock_unlock_with_caller(
dt_pthread_rwlock_t *rwlock,
const char *file,
int line)
251 if(pthread_equal(rwlock->writer, pthread_self()) && rwlock->writer_depth > 1)
253 __sync_fetch_and_sub(&(rwlock->cnt), 1);
254 rwlock->writer_depth--;
255 assert(rwlock->writer_depth >= 1);
256 assert(rwlock->cnt >= 0);
257 snprintf(rwlock->name,
sizeof(rwlock->name),
"nu:%s:%d", file, line);
258 fprintf(stdout,
"Warning: thread lock owned by the same thread is unlocked again by %s\n", rwlock->name);
262 const gboolean writer_was_self = pthread_equal(rwlock->writer, pthread_self());
263 const int res = pthread_rwlock_unlock(&rwlock->lock);
265 __sync_fetch_and_sub(&(rwlock->cnt), 1);
266 assert(rwlock->cnt >= 0);
267 __sync_bool_compare_and_swap(&(rwlock->writer), pthread_self(), 0);
268 if(writer_was_self) rwlock->writer_depth = 0;
269 if(!res) snprintf(rwlock->name,
sizeof(rwlock->name),
"u:%s:%d", file, line);
273#define dt_pthread_rwlock_rdlock(A) dt_pthread_rwlock_rdlock_with_caller(A, __FILE__, __LINE__)
276 if(pthread_equal(rwlock->writer, pthread_self()) && rwlock->writer_depth >= 1)
278 __sync_fetch_and_add(&(rwlock->cnt), 1);
279 rwlock->writer_depth++;
280 snprintf(rwlock->name,
sizeof(rwlock->name),
"wr:%s:%d", file, line);
281 fprintf(stdout,
"Warning: thread lock owned by the same thread is locked again by %s\n", rwlock->name);
285 const int res = pthread_rwlock_rdlock(&rwlock->lock);
287 __sync_fetch_and_add(&(rwlock->cnt), 1);
289 snprintf(rwlock->name,
sizeof(rwlock->name),
"r:%s:%d", file, line);
292#define dt_pthread_rwlock_wrlock(A) dt_pthread_rwlock_wrlock_with_caller(A, __FILE__, __LINE__)
295 if(pthread_equal(rwlock->writer, pthread_self()) && rwlock->writer_depth >= 1)
297 __sync_fetch_and_add(&(rwlock->cnt), 1);
298 rwlock->writer_depth++;
299 snprintf(rwlock->name,
sizeof(rwlock->name),
"ww:%s:%d", file, line);
300 fprintf(stdout,
"Warning: thread lock owned by the same thread is locked again by %s\n", rwlock->name);
304 const int res = pthread_rwlock_wrlock(&rwlock->lock);
306 __sync_fetch_and_add(&(rwlock->cnt), 1);
309 __sync_lock_test_and_set(&(rwlock->writer), pthread_self());
310 rwlock->writer_depth = 1;
311 snprintf(rwlock->name,
sizeof(rwlock->name),
"w:%s:%d", file, line);
315#define dt_pthread_rwlock_tryrdlock(A) dt_pthread_rwlock_tryrdlock_with_caller(A, __FILE__, __LINE__)
321 if(pthread_equal(rwlock->writer, pthread_self()) && rwlock->writer_depth >= 1)
return EBUSY;
323 const int res = pthread_rwlock_tryrdlock(&rwlock->lock);
324 assert(!res || (res == EBUSY));
327 __sync_fetch_and_add(&(rwlock->cnt), 1);
328 snprintf(rwlock->name,
sizeof(rwlock->name),
"tr:%s:%d", file, line);
332#define dt_pthread_rwlock_trywrlock(A) dt_pthread_rwlock_trywrlock_with_caller(A, __FILE__, __LINE__)
336 if(pthread_equal(rwlock->writer, pthread_self()) && rwlock->writer_depth >= 1)
return EBUSY;
338 const int res = pthread_rwlock_trywrlock(&rwlock->lock);
339 assert(!res || (res == EBUSY));
342 __sync_fetch_and_add(&(rwlock->cnt), 1);
343 __sync_lock_test_and_set(&(rwlock->writer), pthread_self());
344 rwlock->writer_depth = 1;
345 snprintf(rwlock->name,
sizeof(rwlock->name),
"tw:%s:%d", file, line);
353typedef struct CAPABILITY(
"mutex") dt_pthread_mutex_t
355 pthread_mutex_t
mutex;