1 /*------------------------------------------------------------------------------
2  *
3  * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4  * The YADIFA TM software product is provided under the BSD 3-clause license:
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  *        * Redistributions of source code must retain the above copyright
11  *          notice, this list of conditions and the following disclaimer.
12  *        * Redistributions in binary form must reproduce the above copyright
13  *          notice, this list of conditions and the following disclaimer in the
14  *          documentation and/or other materials provided with the distribution.
15  *        * Neither the name of EURid nor the names of its contributors may be
16  *          used to endorse or promote products derived from this software
17  *          without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  *------------------------------------------------------------------------------
32  *
33  */
34 
35 /** @defgroup threading mutexes, ...
36  *  @ingroup dnscore
37  *  @brief
38  *
39  * @{
40  *
41  *----------------------------------------------------------------------------*/
42 
43 // CentOS 5.9 requires this to have PTHREAD_MUTEX_RECURSIVE
44 #define  _GNU_SOURCE 1
45 
46 #include "dnscore/dnscore-config.h"
47 #include "dnscore/thread.h"
48 
49 #include <sys/types.h>
50 #include <unistd.h>
51 #include "dnscore/ptr_set.h"
52 #include "dnscore/timems.h"
53 #include "dnscore/bytezarray_output_stream.h"
54 #include "dnscore/format.h"
55 #include "dnscore/thread-tag.h"
56 #include "dnscore/process.h"
57 
58 #if __OpenBSD__
59 #error "OpenBSD doesn't handle PTHREAD_PROCESS_SHARED"
60 #endif
61 
62 #include "dnscore/logger.h"
63 
64 #include "dnscore/mutex.h"
65 
66 // export TSAN_OPTIONS=detect_deadlocks=1:second_deadlock_stack=1
67 // -sanitize=thread
68 
69 #define MTXCOBJ_TAG 0x4a424f4358544d
70 #define MTXCPT_TAG 0x54504358544d
71 #define MTXCTHRD_TAG 0x445248544358544d
72 #define MTXCMON_TAG 0x4e4f4d5358544d
73 
74 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
75 static void
mutex_debug_logger_handle_msg(const void * handle,u32 level,const char * fmt,...)76 mutex_debug_logger_handle_msg(const void* handle, u32 level, const char* fmt, ...)
77 {
78     (void)handle;
79     (void)level;
80 
81     format("%llT | %i | %p | ", timeus(), getpid(), thread_self());
82 
83     output_stream baos;
84     bytezarray_output_stream_context baos_context;
85 
86     va_list args;
87     va_start(args, fmt);
88     u8 text_buffer[512];
89     bytezarray_output_stream_init_ex_static(&baos, text_buffer, sizeof(text_buffer), 0, &baos_context);
90 
91     if(FAIL(vosformat(&baos, fmt, args)))
92     {
93         bytezarray_output_stream_reset(&baos);
94         osprint(&baos, "*** ERROR : MESSAGE FORMATTING FAILED ***");
95     }
96 
97     //output_stream_write_u8(&baos, 0);
98     output_stream_write(termout, bytezarray_output_stream_buffer(&baos), bytezarray_output_stream_buffer_offset(&baos));
99     output_stream_write_u8(termout, (u8)'\n');
100 }
101 #endif
102 
103 void
mutex_debug_stacktrace_log(void * handle,u32 level,stacktrace trace)104 mutex_debug_stacktrace_log(void* handle, u32 level, stacktrace trace)
105 {
106     (void)handle;
107     (void)level;
108     debug_stacktrace_print(termout, trace);
109     output_stream_write_u8(termout, (u8)'\n');
110 }
111 
112 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
113 static void
mutex_debug_log_stacktrace(void * handle,u32 level,const char * prefix)114 mutex_debug_log_stacktrace(void* handle, u32 level, const char *prefix)
115 {
116     println(prefix);
117     stacktrace trace = debug_stacktrace_get();
118     mutex_debug_stacktrace_log(handle, level, trace);
119 }
120 
121 #define logger_handle_msg mutex_debug_logger_handle_msg
122 #define debug_stacktrace_log mutex_debug_stacktrace_log
123 #define debug_log_stacktrace mutex_debug_log_stacktrace
124 #define logger_flush flushout
125 
126 #define MODULE_MSG_HANDLE NULL
127 #define LOG_TEXT_PREFIX ""
128 #define MSG_DEBUG7 0
129 #define MSG_DEBUG6 0
130 #define MSG_DEBUG5 0
131 #define MSG_DEBUG4 0
132 #define MSG_DEBUG3 0
133 #define MSG_DEBUG2 0
134 #define MSG_DEBUG1 0
135 #define MSG_DEBUG 0
136 #define MSG_WARNING 0
137 #define MSG_ERR 0
138 
139 #define log_debug7(...) logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
140 #define log_debug6(...) logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
141 #define log_debug5(...) logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
142 #define log_debug4(...) logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
143 #define log_debug3(...) logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
144 #define log_debug2(...) logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
145 #define log_debug1(...) logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
146 #define log_debug(...)  logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
147 #define log_notice(...) logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
148 #define log_info(...)   logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
149 #define log_warn(...)   logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
150 #define log_err(...)    logger_handle_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
151 
152 #define log_try_debug7(...) logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
153 #define log_try_debug6(...) logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
154 #define log_try_debug5(...) logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
155 #define log_try_debug4(...) logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
156 #define log_try_debug3(...) logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
157 #define log_try_debug2(...) logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
158 #define log_try_debug1(...) logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
159 #define log_try_debug(...)  logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
160 #define log_try_notice(...) logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
161 #define log_try_info(...)   logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
162 #define log_try_warn(...)   logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
163 #define log_try_err(...)    logger_handle_try_msg(0,0,LOG_TEXT_PREFIX __VA_ARGS__)
164 #endif
165 
166 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
167 
168 volatile bool mutex_ultraverbose = FALSE;
169 //volatile bool mutex_ultraverbose = TRUE;
170 
171 #if MUTEX_CONTENTION_MONITOR
172 
173 struct mutex_contention_point_s
174 {
175     stacktrace st;  // stack trace to that point
176     s64 lock_wait;  // time spent waiting for the lock (but successfully acquired)
177     s64 lock_loops; // loops made before acquiring the lock
178     s64 owner_time; // time spent owning that mutex on that stack trace
179     s64 use_count;
180     s64 lock_fail;  // try-locks that failed
181 };
182 
183 typedef struct mutex_contention_point_s mutex_contention_point_t;
184 
185 struct mutex_contention_object_s
186 {
187     void *mutex;
188     ptr_set threads;    // set of mutex_contention_monitor
189     bool recursive;
190 };
191 
192 typedef struct mutex_contention_object_s mutex_contention_object_t;
193 
194 typedef ptr_set mutex_contention_thread_t;
195 
196 struct mutex_contention_monitor_s
197 {
198     thread_t owning_thread;
199     mutex_contention_point_t *contention_point;
200     //
201     mutex_contention_object_t *mutex_object;
202     mutex_contention_thread_t *mutex_thread;
203     const char *type_name;
204     s64 lock_begin_timestamp;
205     s64 lock_wait_loops;
206     s64 lock_end_timestamp;         // 0 until the mutex is acquired
207     s32 multi_lock_count;
208     pid_t pid;
209     bool used_by_condition;         // true means not really locked
210 };
211 
212 typedef struct mutex_contention_monitor_s mutex_contention_monitor_t;
213 
mutex_contention_monitor_lock_count_inc(mutex_contention_monitor_t * mcm)214 void mutex_contention_monitor_lock_count_inc(mutex_contention_monitor_t *mcm)
215 {
216     //log_info("mutex_contention_monitor_lock_count_inc(%p) (%i -> %i)", mcm, mcm->multi_lock_count, mcm->multi_lock_count + 1);
217     ++mcm->multi_lock_count;
218 }
219 
mutex_contention_monitor_lock_count_dec(mutex_contention_monitor_t * mcm)220 bool mutex_contention_monitor_lock_count_dec(mutex_contention_monitor_t *mcm)
221 {
222     //log_info("mutex_contention_monitor_lock_count_dec(%p) (%i -> %i)", mcm, mcm->multi_lock_count, mcm->multi_lock_count - 1);
223     assert(mcm->multi_lock_count > 0);
224 
225     return (--mcm->multi_lock_count == 0);
226 }
227 
228 static pthread_mutex_t mutex_contention_mtx = PTHREAD_MUTEX_INITIALIZER;
229 static ptr_set mutex_contention_mutex_to_threads_set = PTR_SET_PTR_EMPTY;           // mutex -> mutex_contention_object_t
230 static ptr_set mutex_contention_stacktrace_to_point_set = PTR_SET_EMPTY;            // stacktrace -> mutex_contention_point_t
231 static ptr_set mutex_contention_thread_to_monitor_set = PTR_SET_EMPTY;
232 // thread -> mutex_contention_monitor_t
233 
234 static const char *mutex_type_name = "mutex_lock";
235 static const char *group_mutex_type_name = "group_mutex_lock";
236 static const char *shared_group_mutex_type_name = "shared_group_mutex_lock";
237 
mutex_contention_object_create(void * mutex_ptr,bool recursive)238 void mutex_contention_object_create(void *mutex_ptr, bool recursive)
239 {
240     mutex_contention_object_t *mcu;
241     pthread_mutex_lock(&mutex_contention_mtx);
242     ptr_node *mutex_node = ptr_set_insert(&mutex_contention_mutex_to_threads_set, mutex_ptr);
243     if(mutex_node->value == NULL)
244     {
245         MALLOC_OBJECT_OR_DIE(mcu, mutex_contention_object_t, MTXCOBJ_TAG);
246         mcu->mutex = mutex_ptr;
247         ptr_set_init(&mcu->threads);
248         mcu->threads.compare = ptr_set_ptr_node_compare;
249         mcu->recursive = recursive;
250     }
251     else
252     {
253         logger_flush();
254         abort(); // already exists
255     }
256     pthread_mutex_unlock(&mutex_contention_mtx);
257 }
258 
mutex_contention_object_destroy(void * mutex_ptr)259 void mutex_contention_object_destroy(void *mutex_ptr)
260 {
261     pthread_mutex_lock(&mutex_contention_mtx);
262     ptr_node *mutex_node = ptr_set_find(&mutex_contention_mutex_to_threads_set, mutex_ptr);
263     if(mutex_node != NULL)
264     {
265         if(mutex_node->value != NULL)
266         {
267             mutex_contention_object_t *mcu = (mutex_contention_object_t*)mutex_node->value;
268             s64 now = timeus();
269 
270             if(!ptr_set_isempty(&mcu->threads))
271             {
272                 ptr_set_iterator iter;
273                 ptr_set_iterator_init(&mcu->threads, &iter);
274                 while(ptr_set_iterator_hasnext(&iter))
275                 {
276                     ptr_node *node = ptr_set_iterator_next_node(&iter);
277                     mutex_contention_monitor_t* other_mcm = (mutex_contention_monitor_t*)node->value;
278 
279                     if(other_mcm->lock_end_timestamp == 0)
280                     {
281                         s64 other_wait_time = (now - other_mcm->lock_begin_timestamp);
282                         log_err("mutex-monitor: unsafe destruction: thread %p has also been waiting for mutex @%p for %llius (looped %lli)",
283                                  other_mcm->owning_thread, other_mcm->mutex_object->mutex, other_wait_time, other_mcm->lock_wait_loops);
284                         mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_WARNING, other_mcm->contention_point->st);
285                     }
286                     else
287                     {
288                         s64 other_wait_time = (now - other_mcm->lock_end_timestamp);
289                         log_err("mutex-monitor: unsafe destruction: thread %p has been owning the mutex @%p for %llius (a)",
290                                  other_mcm->owning_thread, other_mcm->mutex_object->mutex, other_wait_time, other_mcm->lock_wait_loops);
291                         mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_WARNING, other_mcm->contention_point->st);
292                     }
293                 }
294 
295                 logger_flush();
296                 abort();
297             }
298         }
299 
300         ptr_set_delete(&mutex_contention_mutex_to_threads_set, mutex_ptr);
301     }
302     else
303     {
304         abort(); // already exists
305     }
306     pthread_mutex_unlock(&mutex_contention_mtx);
307 }
308 
309 mutex_contention_monitor_t *
mutex_contention_lock_begin(thread_t thread,void * mutex_ptr,stacktrace st,const char * type_name)310 mutex_contention_lock_begin(thread_t thread, void *mutex_ptr, stacktrace st, const char *type_name)
311 {
312     mutex_contention_object_t *mco;
313     mutex_contention_point_t *mcp;
314     mutex_contention_thread_t *mct;
315     mutex_contention_monitor_t *mcm;
316 
317     pthread_mutex_lock(&mutex_contention_mtx);
318 
319     // insert/find mutex contention users
320     // mutex->currently locking threads
321 
322     ptr_node *mutex_node = ptr_set_insert(&mutex_contention_mutex_to_threads_set, mutex_ptr);
323     if(mutex_node->value != NULL)
324     {
325         mco = (mutex_contention_object_t*)mutex_node->value;
326     }
327     else
328     {
329         log_info("mutex monitor: %p has not been passed to mutex_contention_object_create(void*,bool): static?", mutex_ptr);
330 
331         MALLOC_OBJECT_OR_DIE(mco, mutex_contention_object_t, MTXCOBJ_TAG);
332         mco->mutex = mutex_ptr;
333         ptr_set_init(&mco->threads);
334         mco->threads.compare = ptr_set_ptr_node_compare;
335         mco->recursive = FALSE;
336         mutex_node->value = mco;
337     }
338 
339     // insert/find mutex contention points
340     // stacktrace->lock statistics
341 
342     ptr_node *contention_node = ptr_set_insert(&mutex_contention_stacktrace_to_point_set, st);
343     if(contention_node->value != NULL)
344     {
345         mcp = (mutex_contention_point_t*)contention_node->value;
346     }
347     else
348     {
349         MALLOC_OBJECT_OR_DIE(mcp, mutex_contention_point_t, MTXCPT_TAG);
350         mcp->st = st;
351         mcp->lock_wait = 0;
352         mcp->lock_loops = 0;
353         mcp->owner_time = 0;
354         mcp->use_count = 0;
355         mcp->lock_fail = 0;
356         contention_node->value = mcp;
357     }
358 
359     ++mcp->use_count;
360 
361     // insert/find threads using mutexes
362     // thread->monitored mutexes set
363 
364     ptr_node *thread_node = ptr_set_insert(&mutex_contention_thread_to_monitor_set, (void*)thread);
365 
366     if(thread_node->value != NULL)
367     {
368         mct = (mutex_contention_thread_t*)thread_node->value;
369     }
370     else
371     {
372         MALLOC_OBJECT_OR_DIE(mct, mutex_contention_thread_t, MTXCTHRD_TAG);
373         ptr_set_init(mct);
374         mct->compare = ptr_set_ptr_node_compare;
375         thread_node->value = mct;
376     }
377 
378     // insert/find mutexes in the thread monitored set
379 
380     ptr_node *monitor_node = ptr_set_insert(mct, st);
381     if(monitor_node->value != NULL)
382     {
383         mcm = (mutex_contention_monitor_t*)monitor_node->value;
384         // double lock ... ?
385         mutex_contention_monitor_lock_count_inc(mcm);
386     }
387     else
388     {
389         MALLOC_OBJECT_OR_DIE(mcm, mutex_contention_monitor_t, MTXCMON_TAG);
390         mcm->owning_thread = thread;
391         mcm->contention_point = mcp;
392         mcm->mutex_object = mco;
393         mcm->mutex_thread = mct;
394         mcm->type_name = type_name;
395         mcm->lock_begin_timestamp = timeus();
396         mcm->lock_end_timestamp = 0;
397         mcm->lock_wait_loops = 0;
398         mcm->multi_lock_count = 1;
399         mcm->pid = getpid_ex();
400         monitor_node->value = mcm;
401         mcm->used_by_condition = FALSE;
402     }
403 
404     ptr_node *user_thread_node = ptr_set_insert(&mco->threads, (void*)thread);
405     if(user_thread_node->value == NULL)
406     {
407         user_thread_node->value = mcm;
408     }
409     else
410     {
411         mutex_contention_monitor_t *old_mcm = (mutex_contention_monitor_t*)user_thread_node->value;
412         log_err("lock monitor: mutex %p already locked by %p (old@%p now@%p)", mutex_ptr, old_mcm->lock_begin_timestamp, old_mcm, mcm);
413         log_err("lock monitor: locked by");
414         mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_ERR, old_mcm->contention_point->st);
415         log_err("lock monitor: failed by");
416         mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_ERR, mcm->contention_point->st);
417 
418         debug_stacktrace_print(termout, old_mcm->contention_point->st);
419         debug_stacktrace_print(termout, mcm->contention_point->st);
420         logger_flush();
421         flushout();
422         assert(user_thread_node->value == mcm);
423     }
424 
425     pthread_mutex_unlock(&mutex_contention_mtx);
426 
427     return mcm;
428 }
429 
mutex_contention_lock_wait(mutex_contention_monitor_t * mcm)430 void mutex_contention_lock_wait(mutex_contention_monitor_t *mcm)
431 {
432     // insert/find mutex_contention_users
433 
434     pthread_mutex_lock(&mutex_contention_mtx);
435     ++mcm->lock_end_timestamp;
436 
437     if(mutex_contention_monitor_lock_count_dec(mcm))
438     {
439         mcm->contention_point->owner_time += timeus() - mcm->lock_end_timestamp;
440 
441         mcm->used_by_condition = TRUE;
442     }
443 
444     pthread_mutex_unlock(&mutex_contention_mtx);
445 }
446 
mutex_contention_lock_wait_with_mutex(thread_t thread,void * mutex_ptr)447 void mutex_contention_lock_wait_with_mutex(thread_t thread, void *mutex_ptr)
448 {
449     mutex_contention_object_t *mco;
450     mutex_contention_monitor_t *mcm;
451 
452     pthread_mutex_lock(&mutex_contention_mtx);
453 
454     ptr_node *mutex_node = ptr_set_find(&mutex_contention_mutex_to_threads_set, mutex_ptr);
455     if(mutex_node == NULL)
456     {
457         log_err("mutex monitor: can't wait on %p as it has not been passed to mutex_contention_object_create(void*,bool)");
458         abort();
459     }
460 
461     mco = (mutex_contention_object_t*)mutex_node->value;
462 
463     ptr_node *monitor_node = ptr_set_find(&mco->threads, (void*)thread);
464     if(monitor_node == NULL)
465     {
466         log_err("mutex monitor: can't wait on %p as it's not used by thread %p", thread);
467         abort();
468     }
469 
470     mcm = (mutex_contention_monitor_t*)monitor_node->value;
471 
472     if(mutex_contention_monitor_lock_count_dec(mcm))
473     {
474         mcm->contention_point->owner_time += timeus() - mcm->lock_end_timestamp;
475     }
476 
477     mcm->used_by_condition = TRUE;
478 
479     pthread_mutex_unlock(&mutex_contention_mtx);
480 }
481 
mutex_contention_lock_resume(mutex_contention_monitor_t * mcm)482 void mutex_contention_lock_resume(mutex_contention_monitor_t *mcm)
483 {
484     // insert/find mutex_contention_users
485 
486     pthread_mutex_lock(&mutex_contention_mtx);
487     ++mcm->lock_end_timestamp;
488     mutex_contention_monitor_lock_count_inc(mcm);
489     mcm->used_by_condition = FALSE;
490     pthread_mutex_unlock(&mutex_contention_mtx);
491 }
492 
mutex_contention_lock_resume_with_mutex(thread_t thread,void * mutex_ptr)493 void mutex_contention_lock_resume_with_mutex(thread_t thread, void *mutex_ptr)
494 {
495     mutex_contention_object_t *mco;
496     mutex_contention_monitor_t *mcm;
497 
498     pthread_mutex_lock(&mutex_contention_mtx);
499 
500     ptr_node *mutex_node = ptr_set_find(&mutex_contention_mutex_to_threads_set, mutex_ptr);
501     if(mutex_node == NULL)
502     {
503         log_err("mutex monitor: can't wait on %p as it has not been passed to mutex_contention_object_create(void*,bool)");
504         abort();
505     }
506 
507     mco = (mutex_contention_object_t*)mutex_node->value;
508 
509     ptr_node *monitor_node = ptr_set_find(&mco->threads, (void*)thread);
510     if(monitor_node == NULL)
511     {
512         log_err("mutex monitor: can't wait on %p as it's not used by thread %p", thread);
513         abort();
514     }
515 
516     mcm = (mutex_contention_monitor_t*)monitor_node->value;
517 
518     assert(mcm->used_by_condition);
519 
520     mutex_contention_monitor_lock_count_inc(mcm);
521     mcm->used_by_condition = FALSE;
522 
523     pthread_mutex_unlock(&mutex_contention_mtx);
524 }
525 
mutex_contention_lock_end(mutex_contention_monitor_t * mcm)526 void mutex_contention_lock_end(mutex_contention_monitor_t *mcm)
527 {
528     pthread_mutex_lock(&mutex_contention_mtx);
529 
530     mcm->lock_end_timestamp = timeus();
531 
532     s64 wait_time = mcm->lock_end_timestamp - mcm->lock_begin_timestamp;
533 
534     mcm->contention_point->lock_wait += wait_time;
535     mcm->contention_point->lock_loops += mcm->lock_wait_loops;
536 
537     pthread_mutex_unlock(&mutex_contention_mtx);
538 }
539 
mutex_contention_lock_fail(mutex_contention_monitor_t * mcm)540 void mutex_contention_lock_fail(mutex_contention_monitor_t *mcm)
541 {
542     pthread_mutex_lock(&mutex_contention_mtx);
543 
544     mcm->lock_end_timestamp = timeus();
545 
546     s64 wait_time = mcm->lock_end_timestamp - mcm->lock_begin_timestamp;
547 
548     mcm->contention_point->lock_wait += wait_time;
549     mcm->contention_point->lock_loops += mcm->lock_wait_loops;
550     ++mcm->contention_point->lock_fail;
551 
552     pthread_mutex_unlock(&mutex_contention_mtx);
553 }
554 
mutex_contention_unlock(thread_t thread,void * mutex_ptr)555 void mutex_contention_unlock(thread_t thread, void *mutex_ptr)
556 {
557     mutex_contention_object_t *mco;
558     mutex_contention_monitor_t *mcm;
559 
560     pthread_mutex_lock(&mutex_contention_mtx);
561 
562     ptr_node *mutex_node = ptr_set_find(&mutex_contention_mutex_to_threads_set, mutex_ptr);
563     if(mutex_node == NULL)
564     {
565         log_err("mutex monitor: can't unlock %p as it has not been passed to mutex_contention_object_create(void*,bool)");
566         abort();
567     }
568 
569     mco = (mutex_contention_object_t*)mutex_node->value;
570 
571     ptr_node *monitor_node = ptr_set_find(&mco->threads, (void*)thread);
572     if(monitor_node == NULL)
573     {
574         log_err("mutex monitor: can't unlock %p as it's not used by thread %p", thread);
575         abort();
576     }
577 
578     mcm = (mutex_contention_monitor_t*)monitor_node->value;
579 
580     if(mutex_contention_monitor_lock_count_dec(mcm))
581     {
582         mcm->contention_point->owner_time += timeus() - mcm->lock_end_timestamp;
583 
584         ptr_set_delete(&mco->threads, (void*)mcm->owning_thread);
585         ptr_set_delete(mcm->mutex_thread, mcm->contention_point->st);
586 
587         free(mcm);
588     }
589     else
590     {
591         log_info("mutex monitor: mcm@%p multi_lock_count = %i", mcm, mcm->multi_lock_count);
592         logger_flush();
593         flushout();
594     }
595 
596     pthread_mutex_unlock(&mutex_contention_mtx);
597 }
598 
mutex_contention_unlock_with_monitor(mutex_contention_monitor_t * mcm)599 void mutex_contention_unlock_with_monitor(mutex_contention_monitor_t *mcm)
600 {
601     pthread_mutex_lock(&mutex_contention_mtx);
602 
603     thread_t tid = thread_self();
604 
605     if(mcm->owning_thread != tid)
606     {
607     log_err("mutex-monitor: locked with %p, unlocked with %p", mcm->owning_thread, tid);
608     }
609 
610     if(mutex_contention_monitor_lock_count_dec(mcm))
611     {
612         mcm->contention_point->owner_time += timeus() - mcm->lock_end_timestamp;
613 
614         ptr_set_delete(&mcm->mutex_object->threads, (void*)mcm->owning_thread);
615 
616         ptr_set_delete(mcm->mutex_thread, mcm->contention_point->st);
617 
618         free(mcm);
619     }
620 
621     pthread_mutex_unlock(&mutex_contention_mtx);
622 }
623 
624 static smp_int mutex_contention_monitor_thread_should_stop = SMP_INT_INITIALIZER_AT(0);
625 static pthread_mutex_t mutex_contention_monitor_thread_mtx = PTHREAD_MUTEX_INITIALIZER;
626 static thread_t mutex_contention_monitor_thread_id = 0;
627 
628 static void*
mutex_contention_monitor_thread(void * args_)629 mutex_contention_monitor_thread(void* args_)
630 {
631     (void)args_;
632 
633     while(smp_int_get(&mutex_contention_monitor_thread_should_stop) == 0)
634     {
635         pthread_mutex_lock(&mutex_contention_mtx);
636 
637         ptr_set_iterator thread_iter;
638         ptr_set_iterator monitor_iter;
639         ptr_set_iterator other_iter;
640         ptr_set_iterator_init(&mutex_contention_thread_to_monitor_set, &thread_iter);
641 
642         log_info("mutex-monitor: tick");
643 
644         while(ptr_set_iterator_hasnext(&thread_iter))
645         {
646             s64 now = timeus();
647 
648             ptr_node *thread_node = ptr_set_iterator_next_node(&thread_iter);
649             mutex_contention_thread_t *mct = (mutex_contention_thread_t*)thread_node->value;
650 
651             ptr_set_iterator_init(mct, &monitor_iter);
652             while(ptr_set_iterator_hasnext(&monitor_iter))
653             {
654                 ptr_node *monitor_node = ptr_set_iterator_next_node(&monitor_iter);
655                 mutex_contention_monitor_t *mcm = (mutex_contention_monitor_t*)monitor_node->value;
656 
657                 if(mcm->lock_end_timestamp == 0)
658                 {
659                     s64 wait_time = (now - mcm->lock_begin_timestamp);
660 
661                     if(wait_time >= MUTEX_LOCKED_TOO_MUCH_TIME_US)
662                     {
663                         // not fine at all
664                         // tell this thread has been waiting for this node for quite some time
665                         // tell who is owning the mutex and since when
666                         log_warn("mutex-monitor: thread %p (%s) has been waiting for mutex @%p for %llius (looped %lli)",
667                                 mcm->owning_thread, thread_get_tag_with_pid_and_tid(getpid_ex(), mcm->owning_thread),
668                                 mcm->mutex_object->mutex, wait_time, mcm->lock_wait_loops);
669                         mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_WARNING, mcm->contention_point->st);
670 
671                         ptr_set_iterator_init(&mcm->mutex_object->threads, &other_iter);
672                         while(ptr_set_iterator_hasnext(&other_iter))
673                         {
674                             ptr_node *other_node = ptr_set_iterator_next_node(&other_iter);
675                             mutex_contention_monitor_t* other_mcm = (mutex_contention_monitor_t*)other_node->value;
676 
677                             if(other_mcm->lock_end_timestamp == 0)
678                             {
679                                 s64 other_wait_time = (now - other_mcm->lock_begin_timestamp);
680                                 log_warn("mutex-monitor: thread %p (%s) has also been waiting for mutex @%p for %llius (looped %lli)",
681                                         other_mcm->owning_thread, thread_get_tag_with_pid_and_tid(getpid_ex(), other_mcm->owning_thread),
682                                         other_mcm->mutex_object->mutex, other_wait_time, other_mcm->lock_wait_loops);
683                                 mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_WARNING, other_mcm->contention_point->st);
684                             }
685                             else
686                             {
687                                 s64 other_wait_time = (now - other_mcm->lock_end_timestamp);
688                                 log_warn("mutex-monitor: thread %p (%s) has been owning the mutex @%p for %llius (b)",
689                                         other_mcm->owning_thread, thread_get_tag_with_pid_and_tid(getpid_ex(), other_mcm->owning_thread),
690                                         other_mcm->mutex_object->mutex, other_wait_time, other_mcm->lock_wait_loops);
691                                 mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_WARNING, other_mcm->contention_point->st);
692                             }
693                         }
694                     }
695                 }
696                 else
697                 {
698                     if(!mcm->used_by_condition)
699                     {
700                         s64 lock_time = (now - mcm->lock_end_timestamp);
701 
702                         if(lock_time >= MUTEX_LOCKED_TOO_MUCH_TIME_US)
703                         {
704                             log_warn("mutex-monitor: thread %p (%s) has been owning the mutex @%p for %llius (looped %lli) (a long time)",
705                                      mcm->owning_thread, thread_get_tag_with_pid_and_tid(getpid_ex(), mcm->owning_thread),
706                                      mcm->mutex_object->mutex, lock_time, mcm->lock_wait_loops);
707                             mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_WARNING, mcm->contention_point->st);
708                         }
709                     }
710                     else
711                     {
712                         // mutex is used by a condition
713 
714                         s64 wait_time = (now - mcm->lock_end_timestamp);
715 
716                         if(wait_time >= MUTEX_WAITED_TOO_MUCH_TIME_US)
717                         {
718                             log_warn("mutex-monitor: thread %p (%s) has been waiting for %llius (looped %lli) (a long time)",
719                                      mcm->owning_thread, thread_get_tag_with_pid_and_tid(getpid_ex(), mcm->owning_thread),
720                                      wait_time, mcm->lock_wait_loops);
721                             mutex_debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_WARNING, mcm->contention_point->st);
722                         }
723                     }
724                 }
725             }
726         }
727 
728         pthread_mutex_unlock(&mutex_contention_mtx);
729 
730         sleep(1);
731     }
732 
733     return NULL;
734 }
735 
mutex_contention_monitor_start()736 void mutex_contention_monitor_start()
737 {
738     pthread_mutex_lock(&mutex_contention_monitor_thread_mtx);
739     if(mutex_contention_monitor_thread_id == 0)
740     {
741         thread_t tid;
742         int ret = thread_create(&tid, mutex_contention_monitor_thread, NULL);
743         if(ret == 0)
744         {
745             mutex_contention_monitor_thread_id = tid;
746         }
747     }
748     pthread_mutex_unlock(&mutex_contention_monitor_thread_mtx);
749 }
750 
mutex_contention_monitor_stop()751 void mutex_contention_monitor_stop()
752 {
753     pthread_mutex_lock(&mutex_contention_monitor_thread_mtx);
754     if(mutex_contention_monitor_thread_id != 0)
755     {
756         smp_int_add(&mutex_contention_monitor_thread_should_stop, 1);
757 
758         thread_join(mutex_contention_monitor_thread_id, NULL);
759 
760         smp_int_sub(&mutex_contention_monitor_thread_should_stop, 1);
761         mutex_contention_monitor_thread_id = 0;
762     }
763     pthread_mutex_unlock(&mutex_contention_monitor_thread_mtx);
764 }
765 
766 #endif // !MUTEX_CONTENTION_MONITOR
767 
768 #endif
769 
cond_init_process_shared(cond_t * cond)770 int cond_init_process_shared(cond_t *cond)
771 {
772     int ret;
773     pthread_condattr_t attr;
774     if((ret = pthread_condattr_init(&attr)) == 0)
775     {
776         if((ret = pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED)) == 0)
777         {
778             ret = pthread_cond_init(cond, &attr);
779 
780             if(ret != 0)
781             {
782                 ret = MAKE_ERRNO_ERROR(ret);
783             }
784         }
785         else
786         {
787             ret = MAKE_ERRNO_ERROR(ret);
788         }
789 
790         pthread_condattr_destroy(&attr);
791     }
792     else
793     {
794         ret = MAKE_ERRNO_ERROR(ret);
795     }
796     return ret;
797 }
798 
799 
800 /*
801  * Group mutex lock
802  */
803 
804 void
group_mutex_init(group_mutex_t * mtx)805 group_mutex_init(group_mutex_t* mtx)
806 {
807 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
808 #ifdef MODULE_MSG_HANDLE
809     log_debug7("group_mutex: init mutex@%p", mtx);
810 #endif
811 #endif
812 
813     mutex_init(&mtx->mutex);
814     cond_init(&mtx->cond);
815 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
816 #if MUTEX_CONTENTION_MONITOR
817     mutex_contention_object_create(mtx, FALSE);
818 #endif
819 #endif
820     mtx->count = 0;
821     mtx->owner = GROUP_MUTEX_NOBODY;
822     mtx->reserved_owner = GROUP_MUTEX_NOBODY;
823 }
824 
825 bool
group_mutex_islocked(group_mutex_t * mtx)826 group_mutex_islocked(group_mutex_t *mtx)
827 {
828     mutex_lock(&mtx->mutex);
829     bool r = mtx->owner != 0;
830     mutex_unlock(&mtx->mutex);
831     return r;
832 }
833 
834 void
group_mutex_lock(group_mutex_t * mtx,u8 owner)835 group_mutex_lock(group_mutex_t *mtx, u8 owner)
836 {
837 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
838 #if MUTEX_CONTENTION_MONITOR
839     mutex_contention_monitor_t *mcm = mutex_contention_lock_begin(thread_self(), mtx, debug_stacktrace_get(), group_mutex_type_name);
840 #endif
841 #endif
842     mutex_lock(&mtx->mutex);
843 
844     for(;;)
845     {
846 		/*
847 			A simple way to ensure that a lock can be shared
848 			by similar entities or not.
849 			Sharable entities have their msb off.
850 		*/
851 
852         u8 co = mtx->owner & GROUP_MUTEX_LOCKMASK_FLAG;
853 
854         if(co == GROUP_MUTEX_NOBODY || co == owner)
855         {
856             yassert(mtx->count != MAX_S32);
857 
858             mtx->owner = owner & GROUP_MUTEX_LOCKMASK_FLAG;
859             mtx->count++;
860 
861             break;
862         }
863 /*
864 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
865 #if MUTEX_CONTENTION_MONITOR
866         mutex_contention_lock_wait(mcm); // counts the loops
867 #endif
868 #endif
869 */
870         cond_wait(&mtx->cond, &mtx->mutex);
871     }
872 
873 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
874 #if MUTEX_CONTENTION_MONITOR
875     mutex_contention_lock_end(mcm);
876 #endif
877 #endif
878 
879     mutex_unlock(&mtx->mutex);
880 
881 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
882 #ifdef MODULE_MSG_HANDLE
883     log_debug7("group_mutex: locked mutex@%p for %x", mtx, owner);
884 #endif
885 #endif
886 }
887 
888 bool
group_mutex_trylock(group_mutex_t * mtx,u8 owner)889 group_mutex_trylock(group_mutex_t *mtx, u8 owner)
890 {
891 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
892 #ifdef MODULE_MSG_HANDLE
893     log_debug7("group_mutex: trying to lock mutex@%p for %x", mtx, owner);
894 #endif
895 #endif
896 
897 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
898 #if MUTEX_CONTENTION_MONITOR
899     mutex_contention_monitor_t *mcm = mutex_contention_lock_begin(thread_self(), mtx, debug_stacktrace_get(), group_mutex_type_name);
900 #endif
901 #endif
902     mutex_lock(&mtx->mutex);
903 
904     u8 co = mtx->owner & GROUP_MUTEX_LOCKMASK_FLAG;
905 
906     if(co == GROUP_MUTEX_NOBODY || co == owner)
907     {
908         yassert(mtx->count != MAX_S32);
909 
910         mtx->owner = owner & GROUP_MUTEX_LOCKMASK_FLAG;
911         mtx->count++;
912 
913 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
914 #if MUTEX_CONTENTION_MONITOR
915         mutex_contention_lock_end(mcm);
916 #endif
917 #endif
918 
919         mutex_unlock(&mtx->mutex);
920 
921 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
922 #ifdef MODULE_MSG_HANDLE
923         log_debug7("group_mutex: locked mutex@%p for %x", mtx, owner);
924 #endif
925 #endif
926         return TRUE;
927     }
928     else
929     {
930 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
931 #if MUTEX_CONTENTION_MONITOR
932         mutex_contention_lock_fail(mcm);
933 #endif
934 #endif
935         mutex_unlock(&mtx->mutex);
936 
937 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
938 #ifdef MODULE_MSG_HANDLE
939         log_debug7("group_mutex: failed to lock mutex@%p for %x", mtx, owner);
940 #endif
941 #endif
942         return FALSE;
943     }
944 }
945 
946 void
group_mutex_unlock(group_mutex_t * mtx,u8 owner)947 group_mutex_unlock(group_mutex_t *mtx, u8 owner)
948 {
949 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
950 #ifdef MODULE_MSG_HANDLE
951     log_debug7("group_mutex: unlocking mutex@%p for %x (owned by %x)", mtx, owner, mtx->owner);
952 #endif
953 #endif
954     mutex_lock(&mtx->mutex);
955 
956     yassert(mtx->owner == (owner & GROUP_MUTEX_LOCKMASK_FLAG));
957     yassert(mtx->count != 0);
958 
959     (void)owner;
960 
961     mtx->count--;
962 
963     if(mtx->count == 0)
964     {
965         mtx->owner = GROUP_MUTEX_NOBODY;
966 
967         // wake up all the ones that were waiting for a clean ownership
968 
969         cond_notify(&mtx->cond);
970     }
971 
972 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
973 #if MUTEX_CONTENTION_MONITOR
974     mutex_contention_unlock(thread_self(), mtx);
975 #endif
976 #endif
977 
978     mutex_unlock(&mtx->mutex);
979 }
980 
981 void
group_mutex_double_lock(group_mutex_t * mtx,u8 owner,u8 secondary_owner)982 group_mutex_double_lock(group_mutex_t *mtx, u8 owner, u8 secondary_owner)
983 {
984     yassert(owner == GROUP_MUTEX_READ);
985 
986 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
987 
988 #ifdef MODULE_MSG_HANDLE
989     log_debug7("group_mutex: double-locking mutex@%p for %x", mtx, secondary_owner);
990 #endif
991 
992 #endif
993 
994 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
995 #if MUTEX_CONTENTION_MONITOR
996     mutex_contention_monitor_t *mcm = mutex_contention_lock_begin(thread_self(), mtx, debug_stacktrace_get(), group_mutex_type_name);
997 #endif
998 #endif
999     mutex_lock(&mtx->mutex);
1000 
1001     for(;;)
1002     {
1003         /*
1004          * A simple way to ensure that a lock can be shared
1005          * by similar entities or not.
1006          * Sharable entities have their msb off.
1007          */
1008 
1009         u8 so = mtx->reserved_owner & GROUP_MUTEX_LOCKMASK_FLAG;
1010 
1011         if(so == GROUP_MUTEX_NOBODY || so == secondary_owner)
1012         {
1013             u8 co = mtx->owner & GROUP_MUTEX_LOCKMASK_FLAG;
1014 
1015             if(co == GROUP_MUTEX_NOBODY || co == owner)
1016             {
1017                 yassert(!SIGNED_VAR_VALUE_IS_MAX(mtx->count));
1018 
1019                 mtx->owner = owner & GROUP_MUTEX_LOCKMASK_FLAG;
1020                 mtx->count++;
1021                 mtx->reserved_owner = secondary_owner & GROUP_MUTEX_LOCKMASK_FLAG;
1022 
1023                 break;
1024             }
1025         }
1026         else
1027         {
1028             // the secondary owner is already taken
1029         }
1030 
1031 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1032 #if MUTEX_CONTENTION_MONITOR
1033         mutex_contention_lock_wait(mcm);
1034 #endif
1035 #endif
1036         cond_wait(&mtx->cond, &mtx->mutex);
1037 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1038 #if MUTEX_CONTENTION_MONITOR
1039         mutex_contention_lock_resume(mcm);
1040 #endif
1041 #endif
1042     }
1043 
1044 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1045 #if MUTEX_CONTENTION_MONITOR
1046     mutex_contention_lock_end(mcm);
1047 #endif
1048 #endif
1049 
1050     mutex_unlock(&mtx->mutex);
1051 
1052 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1053 #ifdef MODULE_MSG_HANDLE
1054     log_debug7("group_mutex: double-locked mutex@%p for %x", mtx, secondary_owner);
1055 #endif
1056 #endif
1057 }
1058 
1059 void
group_mutex_double_unlock(group_mutex_t * mtx,u8 owner,u8 secondary_owner)1060 group_mutex_double_unlock(group_mutex_t *mtx, u8 owner, u8 secondary_owner)
1061 {
1062 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1063 #ifdef MODULE_MSG_HANDLE
1064     log_debug7("group_mutex: double-unlocking mutex@%p for %x (owned by %x)", mtx, secondary_owner, mtx->reserved_owner);
1065 #endif
1066 #endif
1067 
1068     yassert(owner == GROUP_MUTEX_READ);
1069 
1070     mutex_lock(&mtx->mutex);
1071 
1072     yassert(mtx->owner == (owner & GROUP_MUTEX_LOCKMASK_FLAG));
1073     yassert(mtx->reserved_owner == (secondary_owner & GROUP_MUTEX_LOCKMASK_FLAG));
1074     yassert(mtx->count != 0);
1075 
1076     (void)owner;
1077     (void)secondary_owner;
1078 
1079     mtx->reserved_owner = GROUP_MUTEX_NOBODY;
1080 
1081     --mtx->count;
1082 
1083 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1084 #ifdef MODULE_MSG_HANDLE
1085     log_debug7("group_mutex: double-unlocked mutex@%p for %x,%x", mtx, owner, secondary_owner);
1086 #endif
1087 #endif
1088 
1089     yassert((mtx->owner & 0xc0) == 0);
1090 
1091     if(mtx->count == 0)
1092     {
1093         mtx->owner = GROUP_MUTEX_NOBODY;
1094         cond_notify(&mtx->cond);
1095     }
1096 
1097 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1098 #if MUTEX_CONTENTION_MONITOR
1099     mutex_contention_unlock(thread_self(), mtx);
1100 #endif
1101 #endif
1102 
1103     mutex_unlock(&mtx->mutex);
1104 }
1105 
1106 void
group_mutex_exchange_locks(group_mutex_t * mtx,u8 owner,u8 secondary_owner)1107 group_mutex_exchange_locks(group_mutex_t *mtx, u8 owner, u8 secondary_owner)
1108 {
1109 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1110 #ifdef MODULE_MSG_HANDLE
1111     log_debug7("group_mutex: exchanging-locks of mutex@%p %x,%x (", mtx, owner, secondary_owner, mtx->owner, mtx->reserved_owner);
1112 #endif
1113 #endif
1114 
1115     yassert(owner == GROUP_MUTEX_READ || secondary_owner == GROUP_MUTEX_READ);
1116 
1117 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1118 #ifdef MODULE_MSG_HANDLE
1119     s64 start = timeus();
1120 #endif
1121 
1122     mutex_lock(&mtx->mutex);
1123 
1124     if((mtx->owner != (owner & GROUP_MUTEX_LOCKMASK_FLAG)) || (mtx->reserved_owner != (secondary_owner & GROUP_MUTEX_LOCKMASK_FLAG)) || (mtx->count == 0))
1125     {
1126 #ifdef MODULE_MSG_HANDLE
1127         debug_log_stacktrace(g_system_logger, MSG_ERR, "group_mutex_exchange_locks");
1128 #endif
1129         abort();
1130     }
1131 #else
1132     mutex_lock(&mtx->mutex);
1133 
1134     yassert(mtx->owner == (owner & GROUP_MUTEX_LOCKMASK_FLAG));
1135     yassert(mtx->reserved_owner == (secondary_owner & GROUP_MUTEX_LOCKMASK_FLAG));
1136     yassert(mtx->count != 0);
1137 #endif
1138 
1139 #if DEBUG
1140     if((mtx->owner != (owner & GROUP_MUTEX_LOCKMASK_FLAG)) || (mtx->count == 0))
1141     {
1142         mutex_unlock(&mtx->mutex);
1143         yassert(mtx->owner == (owner & GROUP_MUTEX_LOCKMASK_FLAG));
1144         yassert(mtx->count != 0);
1145         abort(); // unreachable
1146     }
1147 
1148     if(mtx->reserved_owner != (secondary_owner & GROUP_MUTEX_LOCKMASK_FLAG))
1149     {
1150         mutex_unlock(&mtx->mutex);
1151         yassert(mtx->reserved_owner != (secondary_owner & GROUP_MUTEX_LOCKMASK_FLAG));
1152         abort(); // unreachable
1153     }
1154 #endif
1155 
1156     // wait to be the last one
1157 
1158     while(mtx->count != 1)
1159     {
1160 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1161 #ifdef MODULE_MSG_HANDLE
1162         s64 d = timeus() - start;
1163         if(d > MUTEX_WAITED_TOO_MUCH_TIME_US)
1164         {
1165             log_warn("group_mutex_exchange_locks(%p,%x,%x) : waited for %llius already ...", mtx, owner, secondary_owner, d);
1166             debug_log_stacktrace(MODULE_MSG_HANDLE, MSG_WARNING, "group_mutex_exchange_locks:");
1167         }
1168 #endif
1169 #endif
1170         cond_timedwait(&mtx->cond, &mtx->mutex, 100);
1171     }
1172 
1173     mtx->owner = secondary_owner & GROUP_MUTEX_LOCKMASK_FLAG;
1174     mtx->reserved_owner = owner & GROUP_MUTEX_LOCKMASK_FLAG;
1175 
1176 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1177 #ifdef MODULE_MSG_HANDLE
1178     log_debug7("group_mutex: exchanged locks of mutex@%p to %x, %x", mtx, secondary_owner, owner);
1179 #endif
1180 #endif
1181 
1182     if((secondary_owner & GROUP_MUTEX_EXCLUSIVE_FLAG) == 0)
1183     {
1184         cond_notify(&mtx->cond);
1185     }
1186 
1187     mutex_unlock(&mtx->mutex);
1188 }
1189 
1190 void
group_mutex_destroy(group_mutex_t * mtx)1191 group_mutex_destroy(group_mutex_t* mtx)
1192 {
1193 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1194 #ifdef MODULE_MSG_HANDLE
1195     log_debug7("group_mutex: destroy mutex@%p", mtx);
1196 #endif
1197 #endif
1198 
1199     mutex_lock(&mtx->mutex);
1200     yassert(mtx->count == 0);
1201 
1202     mutex_unlock(&mtx->mutex);
1203 
1204     group_mutex_lock(mtx, GROUP_MUTEX_DESTROY);
1205     group_mutex_unlock(mtx, GROUP_MUTEX_DESTROY);
1206 
1207     cond_notify(&mtx->cond);
1208     cond_finalize(&mtx->cond);
1209     mutex_destroy(&mtx->mutex);
1210 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1211 #if MUTEX_CONTENTION_MONITOR
1212     mutex_contention_object_destroy(mtx);
1213 #endif
1214 #endif
1215 }
1216 
1217 void
mutex_init_recursive(mutex_t * mtx)1218 mutex_init_recursive(mutex_t *mtx)
1219 {
1220     int err;
1221 
1222     ZEROMEMORY(mtx, sizeof(mutex_t));
1223 
1224 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1225 #if MUTEX_CONTENTION_MONITOR
1226     mutex_contention_object_create(mtx, FALSE);
1227 #endif
1228 #endif
1229 
1230     pthread_mutexattr_t   mta;
1231 
1232     err = pthread_mutexattr_init(&mta);
1233 
1234     if(err != 0)
1235     {
1236 #ifdef MODULE_MSG_HANDLE
1237         logger_handle_msg(g_system_logger,MSG_ERR, "mutex_init_recursive: attr %r", MAKE_ERRNO_ERROR(err));
1238 #endif
1239     }
1240 
1241     err = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE);
1242 
1243     if(err != 0)
1244     {
1245 #ifdef MODULE_MSG_HANDLE
1246         logger_handle_msg(g_system_logger,MSG_ERR, "mutex_init_recursive: set %r", MAKE_ERRNO_ERROR(err));
1247 #endif
1248     }
1249 
1250     err = pthread_mutex_init(mtx, &mta);
1251 
1252     if(err != 0)
1253     {
1254 #ifdef MODULE_MSG_HANDLE
1255         logger_handle_msg(g_system_logger,MSG_ERR, "mutex_init_recursive: %r", MAKE_ERRNO_ERROR(err));
1256 #endif
1257     }
1258 
1259 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1260     if(mutex_ultraverbose)
1261     {
1262         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_init(%p)", mtx);
1263     }
1264 #endif
1265 
1266     pthread_mutexattr_destroy(&mta);
1267 }
1268 
1269 int
mutex_init_process_shared(mutex_t * mtx)1270 mutex_init_process_shared(mutex_t *mtx)
1271 {
1272     int ret;
1273 
1274 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1275 #if MUTEX_CONTENTION_MONITOR
1276     mutex_contention_object_create(mtx, FALSE);
1277 #endif
1278 #endif
1279 
1280     pthread_mutexattr_t attr;
1281     if((ret = pthread_mutexattr_init(&attr)) == 0)
1282     {
1283         ret = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
1284 
1285         if(ret == 0)
1286         {
1287             if((ret = pthread_mutex_init(mtx, &attr)) != 0)
1288             {
1289                 ret = MAKE_ERRNO_ERROR(ret);
1290             }
1291         }
1292         else
1293         {
1294             ret = MAKE_ERRNO_ERROR(ret);
1295         }
1296 
1297         pthread_mutexattr_destroy(&attr);
1298     }
1299     else
1300     {
1301         ret = MAKE_ERRNO_ERROR(ret);
1302     }
1303 
1304     return ret;
1305 }
1306 
1307 void
mutex_init(mutex_t * mtx)1308 mutex_init(mutex_t *mtx)
1309 {
1310 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1311 #if MUTEX_CONTENTION_MONITOR
1312     mutex_contention_object_create(mtx, FALSE);
1313 #endif
1314 #endif
1315     int err = pthread_mutex_init(mtx, NULL);
1316 
1317     if(err != 0)
1318     {
1319         logger_handle_msg(g_system_logger,MSG_ERR, "mutex_init: %r", MAKE_ERRNO_ERROR(err));
1320     }
1321 }
1322 
1323 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1324 
1325 void
mutex_lock(mutex_t * mtx)1326 mutex_lock(mutex_t *mtx)
1327 {
1328     if(mutex_ultraverbose)
1329     {
1330 #ifdef MODULE_MSG_HANDLE
1331         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_lock(%p)", mtx);
1332 #endif
1333     }
1334 
1335 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1336 #if MUTEX_CONTENTION_MONITOR
1337     mutex_contention_monitor_t *mcm = mutex_contention_lock_begin(thread_self(), mtx, debug_stacktrace_get(), mutex_type_name);
1338 #endif
1339 #endif
1340 
1341     pthread_mutex_lock(mtx);
1342 
1343 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1344 #if MUTEX_CONTENTION_MONITOR
1345     mutex_contention_lock_end(mcm);
1346 #endif
1347 #endif
1348 
1349     if(mutex_ultraverbose)
1350     {
1351 #ifdef MODULE_MSG_HANDLE
1352         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_lock(%p): locked", mtx);
1353 #endif
1354     }
1355 }
1356 
1357 bool
mutex_trylock(mutex_t * mtx)1358 mutex_trylock(mutex_t *mtx)
1359 {
1360     if(mutex_ultraverbose)
1361     {
1362         logger_handle_msg(g_system_logger, MSG_DEBUG7, "mutex_trylock(%p)", mtx);
1363     }
1364 
1365 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1366 #if MUTEX_CONTENTION_MONITOR
1367     mutex_contention_monitor_t *mcm = mutex_contention_lock_begin(thread_self(), mtx, debug_stacktrace_get(), mutex_type_name);
1368 #endif
1369 #endif
1370 
1371     int err = pthread_mutex_trylock(mtx);
1372 
1373     if((err != 0) && (err != EBUSY))
1374     {
1375         logger_handle_msg(g_system_logger,MSG_ERR, "mutex_trylock(%p): %r", mtx, MAKE_ERRNO_ERROR(err));
1376         logger_flush();
1377         abort();
1378     }
1379 
1380     if(err == 0)
1381     {
1382 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1383 #if MUTEX_CONTENTION_MONITOR
1384         mutex_contention_lock_end(mcm);
1385 #endif
1386 #endif
1387     }
1388 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1389 #if MUTEX_CONTENTION_MONITOR
1390     else
1391     {
1392         mutex_contention_lock_fail(mcm);
1393     }
1394 #endif
1395 #endif
1396 
1397     if(mutex_ultraverbose)
1398     {
1399         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_trylock(%p): %s", mtx, (err == 0)?"locked":"failed");
1400     }
1401 
1402     return err == 0;
1403 }
1404 
1405 void
mutex_unlock(mutex_t * mtx)1406 mutex_unlock(mutex_t *mtx)
1407 {
1408     if(mutex_ultraverbose)
1409     {
1410         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_unlock(%p)", mtx);
1411     }
1412 
1413 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1414 #if MUTEX_CONTENTION_MONITOR
1415     mutex_contention_unlock(thread_self(), mtx);
1416 #endif
1417 #endif
1418 
1419     int err = pthread_mutex_unlock(mtx);
1420 
1421     if(err != 0)
1422     {
1423         logger_handle_msg(g_system_logger, MSG_ERR, "mutex_unlock(%p) self=%p: %r", mtx, (intptr)thread_self(), MAKE_ERRNO_ERROR(err));
1424         debug_stacktrace_log(g_system_logger, MSG_ERR, debug_stacktrace_get());
1425         logger_flush();
1426         abort();
1427     }
1428 }
1429 
1430 int
mutex_lock_unchecked(mutex_t * mtx)1431 mutex_lock_unchecked(mutex_t *mtx)
1432 {
1433     if(mutex_ultraverbose)
1434     {
1435 #ifdef MODULE_MSG_HANDLE
1436         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_lock(%p)", mtx);
1437 #endif
1438     }
1439 
1440 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1441 #if MUTEX_CONTENTION_MONITOR
1442     mutex_contention_monitor_t *mcm = mutex_contention_lock_begin(thread_self(), mtx, debug_stacktrace_get(), mutex_type_name);
1443 #endif
1444 #endif
1445 
1446     int ret = pthread_mutex_lock(mtx);
1447 
1448 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1449 #if MUTEX_CONTENTION_MONITOR
1450     mutex_contention_lock_end(mcm);
1451 #endif
1452 #endif
1453 
1454     if(mutex_ultraverbose)
1455     {
1456 #ifdef MODULE_MSG_HANDLE
1457         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_lock(%p): locked", mtx);
1458 #endif
1459     }
1460 
1461     return ret;
1462 }
1463 
1464 int
mutex_unlock_unchecked(mutex_t * mtx)1465 mutex_unlock_unchecked(mutex_t *mtx)
1466 {
1467     if(mutex_ultraverbose)
1468     {
1469         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_unlock(%p)", mtx);
1470     }
1471 
1472 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1473 #if MUTEX_CONTENTION_MONITOR
1474     mutex_contention_unlock(thread_self(), mtx);
1475 #endif
1476 #endif
1477 
1478     int ret = pthread_mutex_unlock(mtx);
1479 
1480     if(ret != 0)
1481     {
1482         logger_handle_msg(g_system_logger, MSG_ERR, "mutex_unlock(%p) self=%p: %r", mtx, (intptr)thread_self(), MAKE_ERRNO_ERROR(ret));
1483         debug_stacktrace_log(g_system_logger, MSG_ERR, debug_stacktrace_get());
1484         logger_flush();
1485     }
1486 
1487     return ret;
1488 }
1489 
1490 #endif
1491 
1492 void
mutex_destroy(mutex_t * mtx)1493 mutex_destroy(mutex_t *mtx)
1494 {
1495 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1496     int ebusy_count = 0;
1497 
1498     if(mutex_ultraverbose)
1499     {
1500 #ifdef MODULE_MSG_HANDLE
1501         logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_destroy(%p)", mtx);
1502 #endif
1503     }
1504 #endif
1505 
1506     for(;;)
1507     {
1508         int err = pthread_mutex_destroy(mtx);
1509 
1510         switch(err)
1511         {
1512             case 0:
1513             {
1514 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1515 #if MUTEX_CONTENTION_MONITOR
1516                 mutex_contention_object_destroy(mtx);
1517 #endif
1518 #endif
1519 
1520 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1521                 if(ebusy_count > 0)
1522                 {
1523                     logger_handle_msg(g_system_logger,MSG_DEBUG7, "mutex_destroy: EBUSY #%i", ebusy_count);
1524                 }
1525 #endif
1526                 return;
1527             }
1528             case EBUSY:
1529             {
1530                 usleep(1000);
1531 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1532                 ebusy_count++;
1533 
1534 #ifdef MODULE_MSG_HANDLE
1535                 if((ebusy_count & 0xfffff) == 0)
1536                 {
1537                     debug_stacktrace_log(g_system_logger, MSG_DEBUG7,  debug_stacktrace_get());
1538                 }
1539 
1540                 if((ebusy_count & 0xfff) == 0)
1541                 {
1542                     logger_handle_msg(g_system_logger,MSG_ERR, "mutex_destroy: EBUSY #%i", ebusy_count);
1543                 }
1544 #endif
1545 #endif
1546                 break;
1547             }
1548             default:
1549             {
1550 #ifdef MODULE_MSG_HANDLE
1551                 logger_handle_msg(g_system_logger,MSG_ERR, "mutex_destroy: %r", MAKE_ERRNO_ERROR(err));
1552                 logger_flush();
1553 #endif
1554                 abort();
1555             }
1556         }
1557     }
1558 }
1559 
1560 ///////////////////////////////////////////////////////////////////////////////
1561 
1562 /*
1563  * Group mutex lock
1564  */
1565 
1566 void
shared_group_shared_mutex_init(shared_group_shared_mutex_t * smtx)1567 shared_group_shared_mutex_init(shared_group_shared_mutex_t* smtx)
1568 {
1569     mutex_init(&smtx->mutex);
1570     cond_init(&smtx->cond);
1571     smtx->rc = 0;
1572 
1573 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1574 #if MUTEX_CONTENTION_MONITOR
1575     mutex_contention_object_create(smtx, FALSE);
1576 #endif
1577 #endif
1578 }
1579 
1580 void
shared_group_shared_mutex_init_recursive(shared_group_shared_mutex_t * smtx)1581 shared_group_shared_mutex_init_recursive(shared_group_shared_mutex_t* smtx)
1582 {
1583     mutex_init_recursive(&smtx->mutex);
1584     cond_init(&smtx->cond);
1585     smtx->rc = 0;
1586 
1587 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1588 #if MUTEX_CONTENTION_MONITOR
1589     mutex_contention_object_create(smtx, TRUE);
1590 #endif
1591 #endif
1592 }
1593 
1594 void
shared_group_shared_mutex_destroy(shared_group_shared_mutex_t * smtx)1595 shared_group_shared_mutex_destroy(shared_group_shared_mutex_t* smtx)
1596 {
1597     yassert(smtx->rc == 0);
1598 
1599     cond_finalize(&smtx->cond);
1600     mutex_destroy(&smtx->mutex);
1601 
1602 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1603 #if MUTEX_CONTENTION_MONITOR
1604     mutex_contention_object_destroy(smtx);
1605 #endif
1606 #endif
1607 }
1608 
1609 void
shared_group_mutex_init(shared_group_mutex_t * mtx,shared_group_shared_mutex_t * smtx,const char * name)1610 shared_group_mutex_init(shared_group_mutex_t* mtx, shared_group_shared_mutex_t* smtx, const char *name)
1611 {
1612 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1613 #ifdef MODULE_MSG_HANDLE
1614     log_debug7("shared_group_mutex: init mutex@%p+%p '%s'", mtx, smtx, name);
1615 #endif
1616 #else
1617     (void)name;
1618 #endif
1619 
1620     mutex_lock(&smtx->mutex);
1621     smtx->rc++;
1622     mutex_unlock(&smtx->mutex);
1623     mtx->shared_mutex = smtx;
1624 
1625     mtx->count = 0;
1626     mtx->owner = GROUP_MUTEX_NOBODY;
1627 
1628 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1629 #if MUTEX_CONTENTION_MONITOR
1630     mutex_contention_object_create(mtx, TRUE);
1631 #endif
1632 #endif
1633 }
1634 
1635 bool
shared_group_mutex_islocked(shared_group_mutex_t * mtx)1636 shared_group_mutex_islocked(shared_group_mutex_t *mtx)
1637 {
1638     mutex_lock(&mtx->shared_mutex->mutex);
1639     bool r = mtx->owner != 0;
1640     mutex_unlock(&mtx->shared_mutex->mutex);
1641     return r;
1642 }
1643 
1644 bool
shared_group_mutex_islocked_by(shared_group_mutex_t * mtx,u8 owner)1645 shared_group_mutex_islocked_by(shared_group_mutex_t *mtx, u8 owner)
1646 {
1647     mutex_lock(&mtx->shared_mutex->mutex);
1648     bool r = mtx->owner == (owner & GROUP_MUTEX_LOCKMASK_FLAG);
1649     mutex_unlock(&mtx->shared_mutex->mutex);
1650     return r;
1651 }
1652 
1653 void
shared_group_mutex_lock(shared_group_mutex_t * mtx,u8 owner)1654 shared_group_mutex_lock(shared_group_mutex_t *mtx, u8 owner)
1655 {
1656 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1657 #ifdef MODULE_MSG_HANDLE
1658     log_debug7("shared_group_mutex: locking mutex@%p for %x", mtx, owner);
1659 #endif
1660 #endif
1661 
1662 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1663 #if MUTEX_CONTENTION_MONITOR
1664     mutex_contention_monitor_t *mcm = mutex_contention_lock_begin(thread_self(), mtx, debug_stacktrace_get(), shared_group_mutex_type_name);
1665 #endif
1666 #endif
1667 
1668     mutex_lock(&mtx->shared_mutex->mutex);
1669 
1670     for(;;)
1671     {
1672 		/*
1673 			A simple way to ensure that a lock can be shared
1674 			by similar entities or not.
1675 			Sharable entities have their msb off.
1676 		*/
1677 
1678         u8 co = mtx->owner & GROUP_MUTEX_LOCKMASK_FLAG;
1679 
1680         if(co == GROUP_MUTEX_NOBODY || co == owner)
1681         {
1682             yassert(mtx->count != MAX_S32);
1683 
1684             mtx->owner = owner & GROUP_MUTEX_LOCKMASK_FLAG;
1685             mtx->count++;
1686             break;
1687         }
1688 
1689 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1690 #if MUTEX_CONTENTION_MONITOR
1691         mutex_contention_lock_wait(mcm);
1692 #endif
1693 #endif
1694         cond_wait(&mtx->shared_mutex->cond, &mtx->shared_mutex->mutex);
1695 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1696 #if MUTEX_CONTENTION_MONITOR
1697         mutex_contention_lock_resume(mcm);
1698 #endif
1699 #endif
1700     }
1701 
1702 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1703 #if MUTEX_CONTENTION_MONITOR
1704     mutex_contention_lock_end(mcm);
1705 #endif
1706 #endif
1707 
1708     mutex_unlock(&mtx->shared_mutex->mutex);
1709 
1710 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1711 #ifdef MODULE_MSG_HANDLE
1712     log_debug7("shared_group_mutex: locked mutex@%p for %x", mtx, owner);
1713 #endif
1714 #endif
1715 }
1716 
1717 bool
shared_group_mutex_trylock(shared_group_mutex_t * mtx,u8 owner)1718 shared_group_mutex_trylock(shared_group_mutex_t *mtx, u8 owner)
1719 {
1720 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1721 #ifdef MODULE_MSG_HANDLE
1722     log_debug7("shared_group_mutex: trying to lock mutex@%p for %x", mtx, owner);
1723 #endif
1724 #endif
1725 
1726 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1727 #if MUTEX_CONTENTION_MONITOR
1728     mutex_contention_monitor_t *mcm = mutex_contention_lock_begin(thread_self(), mtx, debug_stacktrace_get(), shared_group_mutex_type_name);
1729 #endif
1730 #endif
1731 
1732     mutex_lock(&mtx->shared_mutex->mutex);
1733 
1734     u8 co = mtx->owner & GROUP_MUTEX_LOCKMASK_FLAG;
1735 
1736     if(co == GROUP_MUTEX_NOBODY || co == owner)
1737     {
1738         yassert(mtx->count != MAX_S32);
1739 
1740         mtx->owner = owner & GROUP_MUTEX_LOCKMASK_FLAG;
1741         mtx->count++;
1742 
1743 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1744 #if MUTEX_CONTENTION_MONITOR
1745         mutex_contention_lock_end(mcm);
1746 #endif
1747 #endif
1748         mutex_unlock(&mtx->shared_mutex->mutex);
1749 
1750 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1751 #ifdef MODULE_MSG_HANDLE
1752         log_debug7("shared_group_mutex: locked mutex@%p for %x", mtx, owner);
1753 #endif
1754 #endif
1755         return TRUE;
1756     }
1757     else
1758     {
1759 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1760 #if MUTEX_CONTENTION_MONITOR
1761         mutex_contention_lock_fail(mcm);
1762 #endif
1763 #endif
1764         mutex_unlock(&mtx->shared_mutex->mutex);
1765 
1766 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1767 #ifdef MODULE_MSG_HANDLE
1768         log_debug7("shared_group_mutex: failed to lock mutex@%p for %x", mtx, owner);
1769 #endif
1770 #endif
1771 
1772         return FALSE;
1773     }
1774 }
1775 
1776 void
shared_group_mutex_unlock(shared_group_mutex_t * mtx,u8 owner)1777 shared_group_mutex_unlock(shared_group_mutex_t *mtx, u8 owner)
1778 {
1779 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1780 #ifdef MODULE_MSG_HANDLE
1781     log_debug7("shared_group_mutex: unlocking mutex@%p for %x (owned by %x)", mtx, owner, mtx->owner);
1782 #endif
1783 #endif
1784 
1785     mutex_lock(&mtx->shared_mutex->mutex);
1786 
1787     yassert(mtx->owner == (owner & GROUP_MUTEX_LOCKMASK_FLAG));
1788     yassert(mtx->count != 0);
1789 
1790     (void)owner;
1791 
1792     mtx->count--;
1793 
1794     if(mtx->count == 0)
1795     {
1796         mtx->owner = GROUP_MUTEX_NOBODY;
1797 
1798         // wake up all the ones that were waiting for a clean ownership
1799 
1800         cond_notify(&mtx->shared_mutex->cond);
1801     }
1802 
1803 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1804 #if MUTEX_CONTENTION_MONITOR
1805     mutex_contention_unlock(thread_self(), mtx);
1806 #endif
1807 #endif
1808 
1809     mutex_unlock(&mtx->shared_mutex->mutex);
1810 }
1811 
1812 bool
shared_group_mutex_transferlock(shared_group_mutex_t * mtx,u8 owner,u8 newowner)1813 shared_group_mutex_transferlock(shared_group_mutex_t *mtx, u8 owner, u8 newowner)
1814 {
1815     bool r;
1816 
1817 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1818 #ifdef MODULE_MSG_HANDLE
1819     log_debug7("shared_group_mutex: transferring ownership of mutex@%p from %x to %x (owned by %x)", mtx, owner, newowner, mtx->owner);
1820 #endif
1821 #endif
1822 
1823     mutex_lock(&mtx->shared_mutex->mutex);
1824 
1825     u8 co = mtx->owner & GROUP_MUTEX_LOCKMASK_FLAG;
1826 
1827     if((r = (co == owner)))
1828     {
1829         mtx->owner = newowner;
1830     }
1831 
1832     mutex_unlock(&mtx->shared_mutex->mutex);
1833 
1834     return r;
1835 }
1836 
1837 void
shared_group_mutex_destroy(shared_group_mutex_t * mtx)1838 shared_group_mutex_destroy(shared_group_mutex_t* mtx)
1839 {
1840 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT > 1
1841 #ifdef MODULE_MSG_HANDLE
1842     log_debug7("shared_group_mutex: destroy mutex@%p", mtx);
1843 #endif
1844 #endif
1845 
1846 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
1847 #if MUTEX_CONTENTION_MONITOR
1848     mutex_contention_object_destroy(mtx);
1849 #endif
1850 #endif
1851 
1852     mutex_lock(&mtx->shared_mutex->mutex);
1853     mtx->shared_mutex->rc--;
1854     mutex_unlock(&mtx->shared_mutex->mutex);
1855 }
1856 
1857 
1858 /** @} */
1859