diff --git a/src/common/camera_control.c b/src/common/camera_control.c index 13ad7c5587a1..3d2efed9fb4a 100644 --- a/src/common/camera_control.c +++ b/src/common/camera_control.c @@ -654,7 +654,7 @@ void dt_camctl_camera_stop_live_view(const dt_camctl_t *c) } dt_print(DT_DEBUG_CAMCTL, "[camera_control] Stopping live view"); cam->is_live_viewing = FALSE; - pthread_join(cam->live_view_thread, NULL); + dt_pthread_join(cam->live_view_thread); // tell camera to get back to normal state (close mirror) dt_camctl_camera_set_property_int(camctl, NULL, "eosviewfinder", 0); dt_camctl_camera_set_property_int(camctl, NULL, "viewfinder", 0); diff --git a/src/common/dtpthread.c b/src/common/dtpthread.c index 2420b97fa312..748daf69f02a 100644 --- a/src/common/dtpthread.c +++ b/src/common/dtpthread.c @@ -1,6 +1,6 @@ /* This file is part of darktable, - Copyright (C) 2016-2023 darktable developers. + Copyright (C) 2016-2024 darktable developers. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,48 +31,61 @@ #include "win/dtwin.h" #endif // _WIN32 +#include "common/dtpthread.h" + int dt_pthread_create(pthread_t *thread, void *(*start_routine)(void *), void *arg) { int ret = 0; pthread_attr_t attr; - ret = pthread_attr_init(&attr); if(ret != 0) { - fprintf(stderr, "[dt_pthread_create] error: pthread_attr_init() returned %i\n", ret); - return ret; + printf("[dt_pthread_create] error: pthread_attr_init() returned %s\n", _pthread_ret_mess(ret)); + fflush(stdout); } + assert(ret == 0); size_t stacksize; - ret = pthread_attr_getstacksize(&attr, &stacksize); - - if(ret != 0) - { - fprintf(stderr, "[dt_pthread_create] error: pthread_attr_getstacksize() returned %i\n", ret); - } - if(ret != 0 || stacksize < WANTED_THREADS_STACK_SIZE) { // looks like we need to bump/set it... ret = pthread_attr_setstacksize(&attr, WANTED_THREADS_STACK_SIZE); if(ret != 0) { - fprintf(stderr, "[dt_pthread_create] error: pthread_attr_setstacksize() returned %i\n", ret); + printf("[dt_pthread_create] error: pthread_attr_setstacksize() returned %s\n", _pthread_ret_mess(ret)); + fflush(stdout); } } + assert(ret == 0); - if(ret == 0) - ret = pthread_create(thread, &attr, start_routine, arg); - + ret = pthread_create(thread, &attr, start_routine, arg); if(ret != 0) { - fprintf(stderr, "[dt_pthread_create] error: pthread_create() returned %i\n", ret); + printf("[dt_pthread_create] error: pthread_create() returned %s\n", _pthread_ret_mess(ret)); + fflush(stdout); } pthread_attr_destroy(&attr); + assert(ret == 0); + return ret; +} + +int dt_pthread_join(pthread_t thread) +{ +#if defined(MUTEX_REPORTING) && ( defined(__linux__) || defined(__APPLE__) ) + char name[256] = { "???" }; + pthread_getname_np(thread, name, sizeof(name)); + const int ret = pthread_join(thread, NULL); + printf("[dt_pthread_join] '%s' returned %s\n", + name, _pthread_ret_mess(ret)); + fflush(stdout); +#else + const int ret = pthread_join(thread, NULL); +#endif + assert(ret == 0); return ret; } @@ -94,8 +107,6 @@ void dt_pthread_setname(const char *name) dtwin_set_thread_name((DWORD)-1, name); #endif } - - // clang-format off // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py // vim: shiftwidth=2 expandtab tabstop=2 cindent diff --git a/src/common/dtpthread.h b/src/common/dtpthread.h index 0cb87e92db49..98f3d38f9aa7 100644 --- a/src/common/dtpthread.h +++ b/src/common/dtpthread.h @@ -28,27 +28,42 @@ #include #include -// #define MUTEX_REPORTING // uncomment to include runtime report of mutex problems also for release builds +// #define MUTEX_REPORTING // if defined there will be runtime report of mutex problems also for release builds static inline const char *_pthread_ret_mess(int error) { switch(error) { - case 0: return "OK"; - case EBUSY: return "EBUSY"; - case EINVAL: return "EINVAL"; - case EAGAIN: return "EAGAIN"; - case EDEADLK: return "EDEADLK"; - case EPERM: return "EPERM"; - case ETIMEDOUT: return "ETIMEDOUT"; - case ESRCH: return "ESRCH"; - default: return "???"; + case 0: return "OK"; + case EBUSY: return "EBUSY"; + case EINVAL: return "EINVAL"; + case EAGAIN: return "EAGAIN"; + case EDEADLK: return "EDEADLK"; + case EPERM: return "EPERM"; + case ETIMEDOUT: return "ETIMEDOUT"; + case ESRCH: return "ESRCH"; + case ENOMEM: return "ENOMEM"; + case EOWNERDEAD: return "EOWNERDEAD"; + case ECANCELED: return "ECANCELED"; + case ENOTRECOVERABLE: return "ENOTRECOVERABLE"; +#if defined __linux__ + case ERFKILL: return "ERFKILL"; + case EHWPOISON: return "EHWPOISON"; +#endif + default: return "???"; } } static inline void _ret_error(const int ret, const char* mess) { - fprintf(stderr, "\n*** [dt_pthread_mutex_%s = %s] ***\n", mess ? mess : "???", _pthread_ret_mess(ret)); + printf("\n*** [dt_pthread_mutex_%s = %s] ***\n", mess ? mess : "???", _pthread_ret_mess(ret)); + fflush(stdout); +} + +static inline void _retrw_error(const int ret, const char* mess) +{ + printf("\n*** [dt_pthread_rwlock_%s = %s] ***\n", mess ? mess : "???", _pthread_ret_mess(ret)); + fflush(stdout); } #ifdef _DEBUG @@ -90,13 +105,21 @@ typedef struct dt_pthread_rwlock_t static inline void _report_ret_error(const int ret, const char* loc, const char* mess) { - fprintf(stderr, "[dt_pthread_mutex_%s = %s] at: %s\n", + printf("\n*** [dt_pthread_mutex_%s = %s] at: %s ***\n", + mess ? mess : "???", _pthread_ret_mess(ret), loc ? loc : "no location"); + fflush(stdout); +} + +static inline void _report_retrw_error(const int ret, const char* loc, const char* mess) +{ + printf("\n*** [dt_pthread_rwlock_%s = %s] at: %s ***\n", mess ? mess : "???", _pthread_ret_mess(ret), loc ? loc : "no location"); + fflush(stdout); } static inline int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex) { - const int ret = pthread_mutex_destroy(&(mutex->mutex)); + const int ret = pthread_mutex_destroy(&mutex->mutex); if(ret) _report_ret_error(ret, mutex->name, "destroy"); assert(ret == 0); @@ -116,8 +139,10 @@ static inline int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex) #define dt_pthread_mutex_init(A, B) dt_pthread_mutex_init_with_caller(A, B, __FILE__, __LINE__, __FUNCTION__) static inline int 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) + const pthread_mutexattr_t *attr, + const char *file, + const int line, + const char *function) { memset(mutex, 0x0, sizeof(dt_pthread_mutex_t)); snprintf(mutex->name, sizeof(mutex->name), "%s:%d (%s)", file, line, function); @@ -127,7 +152,7 @@ static inline int dt_pthread_mutex_init_with_caller(dt_pthread_mutex_t *mutex, pthread_mutexattr_t a; pthread_mutexattr_init(&a); pthread_mutexattr_settype(&a, PTHREAD_MUTEX_NORMAL); - const int ret = pthread_mutex_init(&(mutex->mutex), &a); + const int ret = pthread_mutex_init(&mutex->mutex, &a); pthread_mutexattr_destroy(&a); return ret; } @@ -136,14 +161,13 @@ static inline int dt_pthread_mutex_init_with_caller(dt_pthread_mutex_t *mutex, } #define dt_pthread_mutex_lock(A) dt_pthread_mutex_lock_with_caller(A, __FILE__, __LINE__, __FUNCTION__) -static inline int dt_pthread_mutex_lock_with_caller(dt_pthread_mutex_t *mutex, const char *file, - const int line, const char *function) +static inline int dt_pthread_mutex_lock_with_caller(dt_pthread_mutex_t *mutex, const char *file, const int line, const char *function) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS { const double t0 = dt_pthread_get_wtime(); - const int ret = pthread_mutex_lock(&(mutex->mutex)); + const int ret = pthread_mutex_lock(&mutex->mutex); if(ret) _report_ret_error(ret, mutex->name, "lock"); - assert(!ret); + assert(ret == 0); mutex->time_locked = dt_pthread_get_wtime(); double wait = mutex->time_locked - t0; mutex->time_sum_wait += wait; @@ -165,14 +189,13 @@ static inline int dt_pthread_mutex_lock_with_caller(dt_pthread_mutex_t *mutex, c } #define dt_pthread_mutex_trylock(A) dt_pthread_mutex_trylock_with_caller(A, __FILE__, __LINE__, __FUNCTION__) -static inline int dt_pthread_mutex_trylock_with_caller(dt_pthread_mutex_t *mutex, const char *file, - const int line, const char *function) +static inline int dt_pthread_mutex_trylock_with_caller(dt_pthread_mutex_t *mutex, const char *file, const int line, const char *function) TRY_ACQUIRE(0, mutex) { const double t0 = dt_pthread_get_wtime(); - const int ret = pthread_mutex_trylock(&(mutex->mutex)); + const int ret = pthread_mutex_trylock(&mutex->mutex); if(ret && (ret != EBUSY)) _report_ret_error(ret, mutex->name, "trylock"); - assert(!ret || (ret == EBUSY)); + assert((ret == 0) || (ret == EBUSY)); mutex->time_locked = dt_pthread_get_wtime(); double wait = mutex->time_locked - t0; @@ -195,8 +218,7 @@ static inline int dt_pthread_mutex_trylock_with_caller(dt_pthread_mutex_t *mutex } #define dt_pthread_mutex_unlock(A) dt_pthread_mutex_unlock_with_caller(A, __FILE__, __LINE__, __FUNCTION__) -static inline int dt_pthread_mutex_unlock_with_caller(dt_pthread_mutex_t *mutex, const char *file, - const int line, const char *function) +static inline int dt_pthread_mutex_unlock_with_caller(dt_pthread_mutex_t *mutex, const char *file, const int line, const char *function) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS { const double t0 = dt_pthread_get_wtime(); @@ -223,28 +245,28 @@ static inline int dt_pthread_mutex_unlock_with_caller(dt_pthread_mutex_t *mutex, } // need to unlock last, to shield our internal data. - const int ret = pthread_mutex_unlock(&(mutex->mutex)); + const int ret = pthread_mutex_unlock(&mutex->mutex); if(ret) _report_ret_error(ret, mutex->name, "unlock"); - assert(!ret); + assert(ret == 0); return ret; } static inline int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t *mutex) { - const int ret = pthread_cond_wait(cond, &(mutex->mutex)); + const int ret = pthread_cond_wait(cond, &mutex->mutex); if(ret) _report_ret_error(ret, mutex->name, "cond_wait"); - assert(!ret); + assert(ret == 0); return ret; } - -static inline int dt_pthread_rwlock_init(dt_pthread_rwlock_t *lock, - const pthread_rwlockattr_t *attr) +// rwlock handling +static inline int dt_pthread_rwlock_init(dt_pthread_rwlock_t *lock, const pthread_rwlockattr_t *attr) { memset(lock, 0, sizeof(dt_pthread_rwlock_t)); lock->cnt = 0; const int res = pthread_rwlock_init(&lock->lock, attr); - assert(!res); + if(res) _retrw_error(res, "init"); + assert(res == 0); return res; } @@ -252,12 +274,15 @@ static inline int dt_pthread_rwlock_destroy(dt_pthread_rwlock_t *lock) { snprintf(lock->name, sizeof(lock->name), "destroyed with cnt %d", lock->cnt); const int res = pthread_rwlock_destroy(&lock->lock); - assert(!res); + if(res) _report_retrw_error(res, lock->name, "destroy"); + assert(res == 0); return res; } static inline pthread_t dt_pthread_rwlock_get_writer(dt_pthread_rwlock_t *lock) { + if(!lock) _retrw_error(0, "get_writer"); + assert(lock != NULL); return lock->writer; } @@ -265,13 +290,15 @@ static inline pthread_t dt_pthread_rwlock_get_writer(dt_pthread_rwlock_t *lock) static inline int dt_pthread_rwlock_unlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) { const int res = pthread_rwlock_unlock(&rwlock->lock); + if(res) _report_retrw_error(res, rwlock->name, "unlock"); + assert(res == 0); - assert(!res); + __sync_fetch_and_sub(&rwlock->cnt, 1); + if(rwlock->cnt < 0) _retrw_error(0, "unlock count below zero"); - __sync_fetch_and_sub(&(rwlock->cnt), 1); assert(rwlock->cnt >= 0); - __sync_bool_compare_and_swap(&(rwlock->writer), pthread_self(), 0); - if(!res) snprintf(rwlock->name, sizeof(rwlock->name), "u:%s:%d", file, line); + __sync_bool_compare_and_swap(&rwlock->writer, pthread_self(), 0); + snprintf(rwlock->name, sizeof(rwlock->name), "u:%s:%d", file, line); return res; } @@ -279,11 +306,13 @@ static inline int dt_pthread_rwlock_unlock_with_caller(dt_pthread_rwlock_t *rwlo static inline int dt_pthread_rwlock_rdlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) { const int res = pthread_rwlock_rdlock(&rwlock->lock); - assert(!res); - assert(!(res && pthread_equal(rwlock->writer, pthread_self()))); - __sync_fetch_and_add(&(rwlock->cnt), 1); - if(!res) - snprintf(rwlock->name, sizeof(rwlock->name), "r:%s:%d", file, line); + if(res) _report_retrw_error(res, rwlock->name, "rdlock"); + assert(res == 0); + + const gboolean test = pthread_equal(rwlock->writer, pthread_self()); + if(!test) _report_retrw_error(res, rwlock->name, "rdlock non equal threads"); + __sync_fetch_and_add(&rwlock->cnt, 1); + snprintf(rwlock->name, sizeof(rwlock->name), "r:%s:%d", file, line); return res; } @@ -291,13 +320,11 @@ static inline int dt_pthread_rwlock_rdlock_with_caller(dt_pthread_rwlock_t *rwlo static inline int dt_pthread_rwlock_wrlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) { const int res = pthread_rwlock_wrlock(&rwlock->lock); - assert(!res); - __sync_fetch_and_add(&(rwlock->cnt), 1); - if(!res) - { - __sync_lock_test_and_set(&(rwlock->writer), pthread_self()); - snprintf(rwlock->name, sizeof(rwlock->name), "w:%s:%d", file, line); - } + if(res) _report_retrw_error(res, rwlock->name, "wrlock"); + assert(res == 0); + __sync_fetch_and_add(&rwlock->cnt, 1); + __sync_lock_test_and_set(&rwlock->writer, pthread_self()); + snprintf(rwlock->name, sizeof(rwlock->name), "w:%s:%d", file, line); return res; } @@ -305,11 +332,11 @@ static inline int dt_pthread_rwlock_wrlock_with_caller(dt_pthread_rwlock_t *rwlo static inline int dt_pthread_rwlock_tryrdlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) { const int res = pthread_rwlock_tryrdlock(&rwlock->lock); - assert(!res || (res == EBUSY)); - assert(!(res && pthread_equal(rwlock->writer, pthread_self()))); - if(!res) + if(res && (res != EBUSY)) _report_retrw_error(res, rwlock->name, "tryrdlock"); + assert((res == 0) || (res == EBUSY)); + if(res == 0) { - __sync_fetch_and_add(&(rwlock->cnt), 1); + __sync_fetch_and_add(&rwlock->cnt, 1); snprintf(rwlock->name, sizeof(rwlock->name), "tr:%s:%d", file, line); } return res; @@ -319,11 +346,12 @@ static inline int dt_pthread_rwlock_tryrdlock_with_caller(dt_pthread_rwlock_t *r static inline int dt_pthread_rwlock_trywrlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) { const int res = pthread_rwlock_trywrlock(&rwlock->lock); - assert(!res || (res == EBUSY)); + if(res && (res != EBUSY)) _report_retrw_error(res, rwlock->name, "trywrlock"); + assert((res == 0) || (res == EBUSY)); if(!res) { - __sync_fetch_and_add(&(rwlock->cnt), 1); - __sync_lock_test_and_set(&(rwlock->writer), pthread_self()); + __sync_fetch_and_add(&rwlock->cnt, 1); + __sync_lock_test_and_set(&rwlock->writer, pthread_self()); snprintf(rwlock->name, sizeof(rwlock->name), "tw:%s:%d", file, line); } return res; @@ -397,60 +425,151 @@ static inline int dt_pthread_cond_wait(pthread_cond_t *cond, dt_pthread_mutex_t #endif } +/* As the rwlock related functions are not timing critical and are always worth to be checked for + valid results there is no check for MUTEX_REPORTING +*/ + #define dt_pthread_rwlock_t pthread_rwlock_t -#define dt_pthread_rwlock_init pthread_rwlock_init -#define dt_pthread_rwlock_destroy pthread_rwlock_destroy -#define dt_pthread_rwlock_unlock pthread_rwlock_unlock -#define dt_pthread_rwlock_rdlock pthread_rwlock_rdlock -#define dt_pthread_rwlock_wrlock pthread_rwlock_wrlock -#define dt_pthread_rwlock_tryrdlock pthread_rwlock_tryrdlock -#define dt_pthread_rwlock_trywrlock pthread_rwlock_trywrlock - -#define dt_pthread_rwlock_rdlock_with_caller(A,B,C) pthread_rwlock_rdlock(A) -#define dt_pthread_rwlock_wrlock_with_caller(A,B,C) pthread_rwlock_wrlock(A) -#define dt_pthread_rwlock_tryrdlock_with_caller(A,B,C) pthread_rwlock_tryrdlock(A) -#define dt_pthread_rwlock_trywrlock_with_caller(A,B,C) pthread_rwlock_trywrlock(A) +static inline int dt_pthread_rwlock_init(dt_pthread_rwlock_t *lock, + const pthread_rwlockattr_t *attr) +{ + const int res = pthread_rwlock_init(lock, attr); +#ifdef MUTEX_REPORTING + if(res) _retrw_error(res, "init"); +#endif + return res; +} + +static inline int dt_pthread_rwlock_destroy(dt_pthread_rwlock_t *lock) +{ + const int res = pthread_rwlock_destroy(lock); +#ifdef MUTEX_REPORTING + if(res) _retrw_error(res, "destroy"); +#endif + return res; +} + +static inline int dt_pthread_rwlock_unlock(dt_pthread_rwlock_t *rwlock) +{ + const int res = pthread_rwlock_unlock(rwlock); +#ifdef MUTEX_REPORTING + if(res) _retrw_error(res, "unlock"); +#endif + return res; +} +static inline int dt_pthread_rwlock_rdlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) +{ + const int res = pthread_rwlock_rdlock(rwlock); +#ifdef MUTEX_REPORTING + if(res) + { + char name[256]; + snprintf(name, sizeof(name), "rdlock u:%s:%d", file ? file : "???", line); + _retrw_error(res, name); + } #endif + return res; +} + +static inline int dt_pthread_rwlock_wrlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) +{ + const int res = pthread_rwlock_wrlock(rwlock); +#ifdef MUTEX_REPORTING + if(res) + { + char name[256]; + snprintf(name, sizeof(name), "wrlock u:%s:%d", file ? file : "???", line); + _retrw_error(res, name); + } + #endif + return res; +} + +static inline int dt_pthread_rwlock_tryrdlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) +{ + const int res = pthread_rwlock_tryrdlock(rwlock); +#ifdef MUTEX_REPORTING + if(res && (res != EBUSY)) + { + char name[256]; + snprintf(name, sizeof(name), "tryrdlock u:%s:%d", file ? file : "???", line); + _retrw_error(res, name); + } +#endif + return res; +} + +static inline int dt_pthread_rwlock_tryrdlock(dt_pthread_rwlock_t *rwlock) +{ + const int res = pthread_rwlock_tryrdlock(rwlock); +#ifdef MUTEX_REPORTING + if(res && (res != EBUSY)) + _retrw_error(res, "tryrdlock"); +#endif + return res; +} + +static inline int dt_pthread_rwlock_trywrlock_with_caller(dt_pthread_rwlock_t *rwlock, const char *file, int line) +{ + const int res = pthread_rwlock_trywrlock(rwlock); +#ifdef MUTEX_REPORTING + if(res && (res != EBUSY)) + { + char name[256]; + snprintf(name, sizeof(name), "trywrlock u:%s:%d", file ? file : "???", line); + _retrw_error(res, name); + } +#endif + return res; +} + +static inline int dt_pthread_rwlock_trywrlock(dt_pthread_rwlock_t *rwlock) +{ + const int res = pthread_rwlock_trywrlock(rwlock); +#ifdef MUTEX_REPORTING + if(res && (res != EBUSY)) _retrw_error(res, "trywrlock"); +#endif + return res; +} + +#endif // endof release specific inlines /* shared for _DEBUG and release builds */ // if at all possible, do NOT use. static inline int dt_pthread_mutex_BAD_lock(dt_pthread_mutex_t *mutex) { -#ifdef MUTEX_REPORTING const int ret = pthread_mutex_lock(&mutex->mutex); +#ifdef MUTEX_REPORTING if(ret) _ret_error(ret, "BAD_lock"); - return ret; -#else - return pthread_mutex_lock(&mutex->mutex); #endif + assert(ret == 0); + return ret; } static inline int dt_pthread_mutex_BAD_trylock(dt_pthread_mutex_t *mutex) { -#ifdef MUTEX_REPORTING const int ret = pthread_mutex_trylock(&mutex->mutex); +#ifdef MUTEX_REPORTING if(ret && (ret != EBUSY)) _ret_error(ret, "BAD_trylock"); - return ret; -#else - return pthread_mutex_trylock(&mutex->mutex); #endif + assert((ret == 0) || (ret == EBUSY)); + return ret; } static inline int dt_pthread_mutex_BAD_unlock(dt_pthread_mutex_t *mutex) { -#ifdef MUTEX_REPORTING const int ret = pthread_mutex_unlock(&mutex->mutex); +#ifdef MUTEX_REPORTING if(ret) _ret_error(ret, "BAD_unlock"); - return ret; -#else - return pthread_mutex_unlock(&mutex->mutex); #endif + assert(ret == 0); + return ret; } int dt_pthread_create(pthread_t *thread, void *(*start_routine)(void *), void *arg); - +int dt_pthread_join(pthread_t thread); void dt_pthread_setname(const char *name); // clang-format off diff --git a/src/control/control.c b/src/control/control.c index 8fd9d5fa265c..a6e88b4f9901 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -333,7 +333,7 @@ void dt_control_shutdown(dt_control_t *s) int err = 0; // collect all joining errors /* first wait for gphoto device updater */ #ifdef HAVE_GPHOTO2 - err = pthread_join(s->update_gphoto_thread, NULL); + err = dt_pthread_join(s->update_gphoto_thread); #endif if(!cleanup) @@ -342,18 +342,18 @@ void dt_control_shutdown(dt_control_t *s) dt_print(DT_DEBUG_CONTROL, "[dt_control_shutdown] closing control threads"); /* then wait for kick_on_workers_thread */ - err = pthread_join(s->kick_on_workers_thread, NULL); + err = dt_pthread_join(s->kick_on_workers_thread); dt_print(DT_DEBUG_CONTROL, "[dt_control_shutdown] joined kicker%s", err ? ", error" : ""); for(int k = 0; k < s->num_threads-1; k++) { - err = pthread_join(s->thread[k], NULL); + err = dt_pthread_join(s->thread[k]); dt_print(DT_DEBUG_CONTROL, "[dt_control_shutdown] joined num_thread %i%s", k, err ? ", error" : ""); } for(int k = 0; k < DT_CTL_WORKER_RESERVED; k++) { - err = pthread_join(s->thread_res[k], NULL); + err = dt_pthread_join(s->thread_res[k]); dt_print(DT_DEBUG_CONTROL, "[dt_control_shutdown] joined worker %i%s", k, err ? ", error" : ""); } }