locks.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "../libnetdata.h"
  3. #ifdef NETDATA_TRACE_RWLOCKS
  4. #ifndef NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC
  5. #define NETDATA_TRACE_RWLOCKS_WAIT_TIME_TO_IGNORE_USEC 10
  6. #endif
  7. #ifndef NETDATA_TRACE_RWLOCKS_HOLD_TIME_TO_IGNORE_USEC
  8. #define NETDATA_TRACE_RWLOCKS_HOLD_TIME_TO_IGNORE_USEC 10000
  9. #endif
  10. #ifndef NETDATA_THREAD_LOCKS_ARRAY_SIZE
  11. #define NETDATA_THREAD_LOCKS_ARRAY_SIZE 10
  12. #endif
  13. #endif // NETDATA_TRACE_RWLOCKS
  14. // ----------------------------------------------------------------------------
  15. // automatic thread cancelability management, based on locks
  16. static __thread int netdata_thread_first_cancelability = 0;
  17. static __thread int netdata_thread_nested_disables = 0;
  18. static __thread size_t netdata_locks_acquired_rwlocks = 0;
  19. static __thread size_t netdata_locks_acquired_mutexes = 0;
  20. inline void netdata_thread_disable_cancelability(void) {
  21. int old;
  22. int ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
  23. if(ret != 0)
  24. error("THREAD_CANCELABILITY: pthread_setcancelstate() on thread %s returned error %d", netdata_thread_tag(), ret);
  25. else {
  26. if(!netdata_thread_nested_disables)
  27. netdata_thread_first_cancelability = old;
  28. netdata_thread_nested_disables++;
  29. }
  30. }
  31. inline void netdata_thread_enable_cancelability(void) {
  32. if(netdata_thread_nested_disables < 1) {
  33. error("THREAD_CANCELABILITY: netdata_thread_enable_cancelability(): invalid thread cancelability count %d on thread %s - results will be undefined - please report this!",
  34. netdata_thread_nested_disables, netdata_thread_tag());
  35. }
  36. else if(netdata_thread_nested_disables == 1) {
  37. int old = 1;
  38. int ret = pthread_setcancelstate(netdata_thread_first_cancelability, &old);
  39. if(ret != 0)
  40. error("THREAD_CANCELABILITY: pthread_setcancelstate() on thread %s returned error %d", netdata_thread_tag(), ret);
  41. else {
  42. if(old != PTHREAD_CANCEL_DISABLE)
  43. error("THREAD_CANCELABILITY: netdata_thread_enable_cancelability(): old thread cancelability on thread %s was changed, expected DISABLED (%d), found %s (%d) - please report this!", netdata_thread_tag(), PTHREAD_CANCEL_DISABLE, (old == PTHREAD_CANCEL_ENABLE)?"ENABLED":"UNKNOWN", old);
  44. }
  45. netdata_thread_nested_disables = 0;
  46. }
  47. else
  48. netdata_thread_nested_disables--;
  49. }
  50. // ----------------------------------------------------------------------------
  51. // mutex
  52. int __netdata_mutex_init(netdata_mutex_t *mutex) {
  53. int ret = pthread_mutex_init(mutex, NULL);
  54. if(unlikely(ret != 0))
  55. error("MUTEX_LOCK: failed to initialize (code %d).", ret);
  56. return ret;
  57. }
  58. int __netdata_mutex_destroy(netdata_mutex_t *mutex) {
  59. int ret = pthread_mutex_destroy(mutex);
  60. if(unlikely(ret != 0))
  61. error("MUTEX_LOCK: failed to destroy (code %d).", ret);
  62. return ret;
  63. }
  64. int __netdata_mutex_lock(netdata_mutex_t *mutex) {
  65. netdata_thread_disable_cancelability();
  66. int ret = pthread_mutex_lock(mutex);
  67. if(unlikely(ret != 0)) {
  68. netdata_thread_enable_cancelability();
  69. error("MUTEX_LOCK: failed to get lock (code %d)", ret);
  70. }
  71. else
  72. netdata_locks_acquired_mutexes++;
  73. return ret;
  74. }
  75. int __netdata_mutex_trylock(netdata_mutex_t *mutex) {
  76. netdata_thread_disable_cancelability();
  77. int ret = pthread_mutex_trylock(mutex);
  78. if(ret != 0)
  79. netdata_thread_enable_cancelability();
  80. else
  81. netdata_locks_acquired_mutexes++;
  82. return ret;
  83. }
  84. int __netdata_mutex_unlock(netdata_mutex_t *mutex) {
  85. int ret = pthread_mutex_unlock(mutex);
  86. if(unlikely(ret != 0))
  87. error("MUTEX_LOCK: failed to unlock (code %d).", ret);
  88. else {
  89. netdata_locks_acquired_mutexes--;
  90. netdata_thread_enable_cancelability();
  91. }
  92. return ret;
  93. }
  94. #ifdef NETDATA_TRACE_RWLOCKS
  95. int netdata_mutex_init_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  96. const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
  97. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_init(%p) from %lu@%s, %s()", mutex, line, file, function);
  98. int ret = __netdata_mutex_init(mutex);
  99. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_init(%p) = %d, from %lu@%s, %s()", mutex, ret, line, file, function);
  100. return ret;
  101. }
  102. int netdata_mutex_destroy_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  103. const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
  104. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_destroy(%p) from %lu@%s, %s()", mutex, line, file, function);
  105. int ret = __netdata_mutex_destroy(mutex);
  106. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_destroy(%p) = %d, from %lu@%s, %s()", mutex, ret, line, file, function);
  107. return ret;
  108. }
  109. int netdata_mutex_lock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  110. const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
  111. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_lock(%p) from %lu@%s, %s()", mutex, line, file, function);
  112. usec_t start_s = now_monotonic_high_precision_usec();
  113. int ret = __netdata_mutex_lock(mutex);
  114. usec_t end_s = now_monotonic_high_precision_usec();
  115. // remove compiler unused variables warning
  116. (void)start_s;
  117. (void)end_s;
  118. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_lock(%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, end_s - start_s, line, file, function);
  119. return ret;
  120. }
  121. int netdata_mutex_trylock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  122. const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
  123. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_trylock(%p) from %lu@%s, %s()", mutex, line, file, function);
  124. usec_t start_s = now_monotonic_high_precision_usec();
  125. int ret = __netdata_mutex_trylock(mutex);
  126. usec_t end_s = now_monotonic_high_precision_usec();
  127. // remove compiler unused variables warning
  128. (void)start_s;
  129. (void)end_s;
  130. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_trylock(%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, end_s - start_s, line, file, function);
  131. return ret;
  132. }
  133. int netdata_mutex_unlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  134. const unsigned long line __maybe_unused, netdata_mutex_t *mutex) {
  135. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_unlock(%p) from %lu@%s, %s()", mutex, line, file, function);
  136. usec_t start_s = now_monotonic_high_precision_usec();
  137. int ret = __netdata_mutex_unlock(mutex);
  138. usec_t end_s = now_monotonic_high_precision_usec();
  139. // remove compiler unused variables warning
  140. (void)start_s;
  141. (void)end_s;
  142. debug(D_LOCKS, "MUTEX_LOCK: netdata_mutex_unlock(%p) = %d in %llu usec, from %lu@%s, %s()", mutex, ret, end_s - start_s, line, file, function);
  143. return ret;
  144. }
  145. #endif // NETDATA_TRACE_RWLOCKS
  146. // ----------------------------------------------------------------------------
  147. // rwlock
  148. int __netdata_rwlock_destroy(netdata_rwlock_t *rwlock) {
  149. int ret = pthread_rwlock_destroy(&rwlock->rwlock_t);
  150. if(unlikely(ret != 0))
  151. error("RW_LOCK: failed to destroy lock (code %d)", ret);
  152. return ret;
  153. }
  154. int __netdata_rwlock_init(netdata_rwlock_t *rwlock) {
  155. int ret = pthread_rwlock_init(&rwlock->rwlock_t, NULL);
  156. if(unlikely(ret != 0))
  157. error("RW_LOCK: failed to initialize lock (code %d)", ret);
  158. return ret;
  159. }
  160. int __netdata_rwlock_rdlock(netdata_rwlock_t *rwlock) {
  161. netdata_thread_disable_cancelability();
  162. int ret = pthread_rwlock_rdlock(&rwlock->rwlock_t);
  163. if(unlikely(ret != 0)) {
  164. netdata_thread_enable_cancelability();
  165. error("RW_LOCK: failed to obtain read lock (code %d)", ret);
  166. }
  167. else
  168. netdata_locks_acquired_rwlocks++;
  169. return ret;
  170. }
  171. int __netdata_rwlock_wrlock(netdata_rwlock_t *rwlock) {
  172. netdata_thread_disable_cancelability();
  173. int ret = pthread_rwlock_wrlock(&rwlock->rwlock_t);
  174. if(unlikely(ret != 0)) {
  175. error("RW_LOCK: failed to obtain write lock (code %d)", ret);
  176. netdata_thread_enable_cancelability();
  177. }
  178. else
  179. netdata_locks_acquired_rwlocks++;
  180. return ret;
  181. }
  182. int __netdata_rwlock_unlock(netdata_rwlock_t *rwlock) {
  183. int ret = pthread_rwlock_unlock(&rwlock->rwlock_t);
  184. if(unlikely(ret != 0))
  185. error("RW_LOCK: failed to release lock (code %d)", ret);
  186. else {
  187. netdata_thread_enable_cancelability();
  188. netdata_locks_acquired_rwlocks--;
  189. }
  190. return ret;
  191. }
  192. int __netdata_rwlock_tryrdlock(netdata_rwlock_t *rwlock) {
  193. netdata_thread_disable_cancelability();
  194. int ret = pthread_rwlock_tryrdlock(&rwlock->rwlock_t);
  195. if(ret != 0)
  196. netdata_thread_enable_cancelability();
  197. else
  198. netdata_locks_acquired_rwlocks++;
  199. return ret;
  200. }
  201. int __netdata_rwlock_trywrlock(netdata_rwlock_t *rwlock) {
  202. netdata_thread_disable_cancelability();
  203. int ret = pthread_rwlock_trywrlock(&rwlock->rwlock_t);
  204. if(ret != 0)
  205. netdata_thread_enable_cancelability();
  206. else
  207. netdata_locks_acquired_rwlocks++;
  208. return ret;
  209. }
  210. // ----------------------------------------------------------------------------
  211. // spinlock implementation
  212. // https://www.youtube.com/watch?v=rmGJc9PXpuE&t=41s
  213. void netdata_spinlock_init(SPINLOCK *spinlock) {
  214. memset(spinlock, 0, sizeof(SPINLOCK));
  215. }
  216. void netdata_spinlock_lock(SPINLOCK *spinlock) {
  217. static const struct timespec ns = { .tv_sec = 0, .tv_nsec = 1 };
  218. #ifdef NETDATA_INTERNAL_CHECKS
  219. size_t spins = 0;
  220. #endif
  221. netdata_thread_disable_cancelability();
  222. for(int i = 1;
  223. __atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) ||
  224. __atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE)
  225. ; i++
  226. ) {
  227. #ifdef NETDATA_INTERNAL_CHECKS
  228. spins++;
  229. #endif
  230. if(unlikely(i == 8)) {
  231. i = 0;
  232. nanosleep(&ns, NULL);
  233. }
  234. }
  235. // we have the lock
  236. #ifdef NETDATA_INTERNAL_CHECKS
  237. spinlock->spins += spins;
  238. spinlock->locker_pid = gettid();
  239. #endif
  240. }
  241. void netdata_spinlock_unlock(SPINLOCK *spinlock) {
  242. #ifdef NETDATA_INTERNAL_CHECKS
  243. spinlock->locker_pid = 0;
  244. #endif
  245. __atomic_clear(&spinlock->locked, __ATOMIC_RELEASE);
  246. netdata_thread_enable_cancelability();
  247. }
  248. bool netdata_spinlock_trylock(SPINLOCK *spinlock) {
  249. netdata_thread_disable_cancelability();
  250. if(!__atomic_load_n(&spinlock->locked, __ATOMIC_RELAXED) &&
  251. !__atomic_test_and_set(&spinlock->locked, __ATOMIC_ACQUIRE))
  252. // we got the lock
  253. return true;
  254. // we didn't get the lock
  255. return false;
  256. }
  257. #ifdef NETDATA_TRACE_RWLOCKS
  258. // ----------------------------------------------------------------------------
  259. // lockers list
  260. static netdata_rwlock_locker *find_rwlock_locker(const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
  261. pid_t pid = gettid();
  262. netdata_rwlock_locker *locker = NULL;
  263. __netdata_mutex_lock(&rwlock->lockers_mutex);
  264. Pvoid_t *PValue = JudyLGet(rwlock->lockers_pid_JudyL, pid, PJE0);
  265. if(PValue && *PValue)
  266. locker = *PValue;
  267. __netdata_mutex_unlock(&rwlock->lockers_mutex);
  268. return locker;
  269. }
  270. static netdata_rwlock_locker *add_rwlock_locker(const char *file, const char *function, const unsigned long line, netdata_rwlock_t *rwlock, LOCKER_REQUEST lock_type) {
  271. netdata_rwlock_locker *locker;
  272. locker = find_rwlock_locker(file, function, line, rwlock);
  273. if(locker) {
  274. locker->lock |= lock_type;
  275. locker->refcount++;
  276. }
  277. else {
  278. locker = mallocz(sizeof(netdata_rwlock_locker));
  279. locker->pid = gettid();
  280. locker->tag = netdata_thread_tag();
  281. locker->refcount = 1;
  282. locker->lock = lock_type;
  283. locker->got_it = false;
  284. locker->file = file;
  285. locker->function = function;
  286. locker->line = line;
  287. __netdata_mutex_lock(&rwlock->lockers_mutex);
  288. DOUBLE_LINKED_LIST_APPEND_UNSAFE(rwlock->lockers, locker, prev, next);
  289. Pvoid_t *PValue = JudyLIns(&rwlock->lockers_pid_JudyL, locker->pid, PJE0);
  290. *PValue = locker;
  291. if (lock_type == RWLOCK_REQUEST_READ || lock_type == RWLOCK_REQUEST_TRYREAD) rwlock->readers++;
  292. if (lock_type == RWLOCK_REQUEST_WRITE || lock_type == RWLOCK_REQUEST_TRYWRITE) rwlock->writers++;
  293. __netdata_mutex_unlock(&rwlock->lockers_mutex);
  294. }
  295. return locker;
  296. }
  297. static void remove_rwlock_locker(const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock, netdata_rwlock_locker *locker) {
  298. __netdata_mutex_lock(&rwlock->lockers_mutex);
  299. locker->refcount--;
  300. if(!locker->refcount) {
  301. DOUBLE_LINKED_LIST_REMOVE_UNSAFE(rwlock->lockers, locker, prev, next);
  302. JudyLDel(&rwlock->lockers_pid_JudyL, locker->pid, PJE0);
  303. if (locker->lock == RWLOCK_REQUEST_READ || locker->lock == RWLOCK_REQUEST_TRYREAD) rwlock->readers--;
  304. else if (locker->lock == RWLOCK_REQUEST_WRITE || locker->lock == RWLOCK_REQUEST_TRYWRITE) rwlock->writers--;
  305. freez(locker);
  306. }
  307. __netdata_mutex_unlock(&rwlock->lockers_mutex);
  308. }
  309. // ----------------------------------------------------------------------------
  310. // debug versions of rwlock
  311. int netdata_rwlock_destroy_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  312. const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
  313. int ret = __netdata_rwlock_destroy(rwlock);
  314. if(!ret) {
  315. while (rwlock->lockers)
  316. remove_rwlock_locker(file, function, line, rwlock, rwlock->lockers);
  317. }
  318. return ret;
  319. }
  320. int netdata_rwlock_init_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  321. const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
  322. int ret = __netdata_rwlock_init(rwlock);
  323. if(!ret) {
  324. __netdata_mutex_init(&rwlock->lockers_mutex);
  325. rwlock->lockers_pid_JudyL = NULL;
  326. rwlock->lockers = NULL;
  327. rwlock->readers = 0;
  328. rwlock->writers = 0;
  329. }
  330. return ret;
  331. }
  332. int netdata_rwlock_rdlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  333. const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
  334. netdata_rwlock_locker *locker = add_rwlock_locker(file, function, line, rwlock, RWLOCK_REQUEST_READ);
  335. int ret = __netdata_rwlock_rdlock(rwlock);
  336. if(!ret)
  337. locker->got_it = true;
  338. else
  339. remove_rwlock_locker(file, function, line, rwlock, locker);
  340. return ret;
  341. }
  342. int netdata_rwlock_wrlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  343. const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
  344. netdata_rwlock_locker *locker = add_rwlock_locker(file, function, line, rwlock, RWLOCK_REQUEST_WRITE);
  345. int ret = __netdata_rwlock_wrlock(rwlock);
  346. if(!ret)
  347. locker->got_it = true;
  348. else
  349. remove_rwlock_locker(file, function, line, rwlock, locker);
  350. return ret;
  351. }
  352. int netdata_rwlock_unlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  353. const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
  354. netdata_rwlock_locker *locker = find_rwlock_locker(file, function, line, rwlock);
  355. if(unlikely(!locker))
  356. fatal("UNLOCK WITHOUT LOCK");
  357. int ret = __netdata_rwlock_unlock(rwlock);
  358. if(likely(!ret))
  359. remove_rwlock_locker(file, function, line, rwlock, locker);
  360. return ret;
  361. }
  362. int netdata_rwlock_tryrdlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  363. const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
  364. netdata_rwlock_locker *locker = add_rwlock_locker(file, function, line, rwlock, RWLOCK_REQUEST_TRYREAD);
  365. int ret = __netdata_rwlock_tryrdlock(rwlock);
  366. if(!ret)
  367. locker->got_it = true;
  368. else
  369. remove_rwlock_locker(file, function, line, rwlock, locker);
  370. return ret;
  371. }
  372. int netdata_rwlock_trywrlock_debug(const char *file __maybe_unused, const char *function __maybe_unused,
  373. const unsigned long line __maybe_unused, netdata_rwlock_t *rwlock) {
  374. netdata_rwlock_locker *locker = add_rwlock_locker(file, function, line, rwlock, RWLOCK_REQUEST_TRYWRITE);
  375. int ret = __netdata_rwlock_trywrlock(rwlock);
  376. if(!ret)
  377. locker->got_it = true;
  378. else
  379. remove_rwlock_locker(file, function, line, rwlock, locker);
  380. return ret;
  381. }
  382. #endif // NETDATA_TRACE_RWLOCKS