/** * Call this function when thread tid stops to exist, such that the * "last owner" field can be cleared if it still refers to that thread. */ static void mutex_delete_thread(struct mutex_info* p, const DrdThreadId tid) { tl_assert(p); if (p->owner == tid && p->recursion_count > 0) { MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), p->a1, p->recursion_count, p->owner }; VG_(maybe_record_error)(VG_(get_running_tid)(), MutexErr, VG_(get_IP)(VG_(get_running_tid)()), "Mutex still locked at thread exit", &MEI); p->owner = VG_INVALID_THREADID; } }
/** * Initialize the memory 'vc' points at as a vector clock with size 'size'. * If the pointer 'vcelem' is not null, it is assumed to be an array with * 'size' elements and it becomes the initial value of the vector clock. */ void DRD_(vc_init)(VectorClock* const vc, const VCElem* const vcelem, const unsigned size) { tl_assert(vc); vc->size = 0; vc->capacity = 0; vc->vc = 0; DRD_(vc_reserve)(vc, size); tl_assert(size == 0 || vc->vc != 0); if (vcelem) { VG_(memcpy)(vc->vc, vcelem, size * sizeof(vcelem[0])); vc->size = size; } }
/** Called before pthread_mutex_lock() is invoked. If a data structure for * the client-side object was not yet created, do this now. Also check whether * an attempt is made to lock recursively a synchronization object that must * not be locked recursively. */ void DRD_(mutex_pre_lock)(const Addr mutex, MutexT mutex_type, const Bool trylock) { struct mutex_info* p; p = DRD_(mutex_get_or_allocate)(mutex, mutex_type); if (mutex_type == mutex_type_unknown) mutex_type = p->mutex_type; if (s_trace_mutex) { VG_(message)(Vg_UserMsg, "[%d] %s %s 0x%lx rc %d owner %d\n", DRD_(thread_get_running_tid)(), trylock ? "pre_mutex_lock " : "mutex_trylock ", p ? DRD_(mutex_get_typename)(p) : "(?)", mutex, p ? p->recursion_count : -1, p ? p->owner : DRD_INVALID_THREADID); } if (p == 0) { DRD_(not_a_mutex)(mutex); return; } tl_assert(p); if (mutex_type == mutex_type_invalid_mutex) { DRD_(not_a_mutex)(mutex); return; } if (! trylock && p->owner == DRD_(thread_get_running_tid)() && p->recursion_count >= 1 && mutex_type != mutex_type_recursive_mutex) { MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), p->a1, p->recursion_count, p->owner }; VG_(maybe_record_error)(VG_(get_running_tid)(), MutexErr, VG_(get_IP)(VG_(get_running_tid)()), "Recursive locking not allowed", &MEI); } }
void DRD_(stop_tracing_address_range)(const Addr a1, const Addr a2) { tl_assert(a1 < a2); DRD_(bm_clear_load)(DRD_(s_suppressed), a1, a2); if (DRD_(g_any_address_traced)) { DRD_(g_any_address_traced) = DRD_(bm_has_any_load)(DRD_(s_suppressed), 0, ~(Addr)0); } }
static __inline__ void drd_start_using_mem(const Addr a1, const SizeT len, const Bool is_stack_mem) { const Addr a2 = a1 + len; tl_assert(a1 <= a2); if (!is_stack_mem && s_trace_alloc) DRD_(trace_msg)("Started using memory range 0x%lx + %ld%s", a1, len, DRD_(running_thread_inside_pthread_create)() ? " (inside pthread_create())" : ""); #if 0 if (!is_stack_mem && DRD_(g_free_is_write)) DRD_(thread_stop_using_mem)(a1, a2); #else /* * Sometimes it happens that a client starts using a memory range that has * been accessed before but for which drd_stop_using_mem() has not been * called for the entire range. It is not yet clear whether this is an * out-of-range access by the client, an issue in the Valgrind core or an * issue in DRD. Avoid that this issue triggers false positive reports by * always clearing accesses for newly allocated memory ranges. See also * http://bugs.kde.org/show_bug.cgi?id=297147. */ DRD_(thread_stop_using_mem)(a1, a2); #endif if (UNLIKELY(DRD_(any_address_is_traced)())) { DRD_(trace_mem_access)(a1, len, eStart, 0, 0); } if (UNLIKELY(DRD_(running_thread_inside_pthread_create)())) { DRD_(start_suppression)(a1, a2, "pthread_create()"); } }
/** * Called after sem_wait() finished. * @note Do not rely on the value of 'waited' -- some glibc versions do * not set it correctly. */ void DRD_(semaphore_post_wait)(const DrdThreadId tid, const Addr semaphore, const Bool waited) { struct semaphore_info* p; Segment* sg; p = DRD_(semaphore_get)(semaphore); if (s_trace_semaphore) { VG_(message)(Vg_UserMsg, "[%d/%d] semaphore_wait 0x%lx value %u -> %u", VG_(get_running_tid)(), DRD_(thread_get_running_tid)(), semaphore, p ? p->value : 0, p ? p->value - 1 : 0); } tl_assert(p); tl_assert(p->waiters > 0); p->waiters--; tl_assert((int)p->waiters >= 0); tl_assert((int)p->value >= 0); if (p->value == 0) { SemaphoreErrInfo sei = { semaphore }; VG_(maybe_record_error)(VG_(get_running_tid)(), SemaphoreErr, VG_(get_IP)(VG_(get_running_tid)()), "Invalid semaphore", &sei); return; } p->value--; tl_assert((int)p->value >= 0); if (p->waits_to_skip > 0) p->waits_to_skip--; else { sg = DRD_(segment_pop)(p); tl_assert(sg); if (sg) { if (p->last_sem_post_tid != tid && p->last_sem_post_tid != DRD_INVALID_THREADID) { DRD_(thread_combine_vc2)(tid, &sg->vc); } DRD_(sg_put)(sg); DRD_(thread_new_segment)(tid); s_semaphore_segment_creation_count++; } } }
/** * Initialize the memory 'sg' points at. * * @note The creator and created thread ID's may be equal. * @note This function copies the vector clock of thread 'creator', a technique * also known as clock snooping. This will only work reliably if the thread * that called pthread_create() waits until the created thread has copied * the vector clock. */ static void sg_init(Segment* const sg, const DrdThreadId creator, const DrdThreadId created) { Segment* creator_sg; ThreadId vg_created = DRD_(DrdThreadIdToVgThreadId)(created); tl_assert(sg); tl_assert(creator == DRD_INVALID_THREADID || DRD_(IsValidDrdThreadId)(creator)); creator_sg = (creator != DRD_INVALID_THREADID ? DRD_(thread_get_segment)(creator) : 0); sg->g_next = NULL; sg->g_prev = NULL; sg->thr_next = NULL; sg->thr_prev = NULL; sg->tid = created; sg->refcnt = 1; if (vg_created != VG_INVALID_THREADID && VG_(get_SP)(vg_created) != 0) sg->stacktrace = VG_(record_ExeContext)(vg_created, 0); else sg->stacktrace = 0; if (creator_sg) DRD_(vc_copy)(&sg->vc, &creator_sg->vc); else DRD_(vc_init)(&sg->vc, 0, 0); DRD_(vc_increment)(&sg->vc, created); DRD_(bm_init)(&sg->bm); if (s_trace_segment) { HChar* vc; vc = DRD_(vc_aprint)(&sg->vc); VG_(message)(Vg_DebugMsg, "New segment for thread %u with vc %s\n", created, vc); VG_(free)(vc); } }
/** Called when thread tid stops to exist. */ static void barrier_delete_thread(struct barrier_info* const p, const DrdThreadId tid) { struct barrier_thread_info* q; const UWord word_tid = tid; q = VG_(OSetGen_Remove)(p->oset, &word_tid); /* * q is only non-zero if the barrier object has been used by thread tid * after the barrier_init() call and before the thread finished. */ if (q) { DRD_(barrier_thread_destroy)(q); VG_(OSetGen_FreeNode)(p->oset, q); } }
static __always_inline int pthread_mutex_init_intercept(pthread_mutex_t *mutex, const pthread_mutexattr_t* attr) { int ret; OrigFn fn; int mt; VALGRIND_GET_ORIG_FN(fn); mt = PTHREAD_MUTEX_DEFAULT; if (attr) pthread_mutexattr_gettype(attr, &mt); VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PRE_MUTEX_INIT, mutex, DRD_(pthread_to_drd_mutex_type)(mt), 0, 0, 0); CALL_FN_W_WW(ret, fn, mutex, attr); VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__POST_MUTEX_INIT, mutex, 0, 0, 0, 0); return ret; }
static void DRD_(print_usage)(void) { VG_(printf)( " --check-stack-var=yes|no Whether or not to report data races on\n" " stack variables [no].\n" " --exclusive-threshold=<n> Print an error message if any mutex or\n" " writer lock is held longer than the specified time (in milliseconds).\n" " --first-race-only=yes|no Only report the first data race that occurs on\n" " a memory location instead of all races [no].\n" " --report-signal-unlocked=yes|no Whether to report calls to\n" " pthread_cond_signal() where the mutex associated\n" " with the signal via pthread_cond_wait() is not\n" " locked at the time the signal is sent [yes].\n" " --segment-merging=yes|no Controls segment merging [yes].\n" " Segment merging is an algorithm to limit memory usage of the\n" " data race detection algorithm. Disabling segment merging may\n" " improve the accuracy of the so-called 'other segments' displayed\n" " in race reports but can also trigger an out of memory error.\n" " --segment-merging-interval=<n> Perform segment merging every time n new\n" " segments have been created. Default: %d.\n" " --shared-threshold=<n> Print an error message if a reader lock\n" " is held longer than the specified time (in milliseconds).\n" " --show-confl-seg=yes|no Show conflicting segments in race reports [yes].\n" " --show-stack-usage=yes|no Print stack usage at thread exit time [no].\n" " --var-info=yes|no Display the names of global, static and\n" " stack variables when a race is reported on such a variable. This\n" " information is by default not displayed since for big programs\n" " reading in all debug information at once may cause an out of\n" " memory error [no].\n" "\n" " drd options for monitoring process behavior:\n" " --trace-addr=<address> Trace all load and store activity for the.\n" " specified address [off].\n" " --trace-barrier=yes|no Trace all barrier activity [no].\n" " --trace-cond=yes|no Trace all condition variable activity [no].\n" " --trace-fork-join=yes|no Trace all thread fork/join activity [no].\n" " --trace-mutex=yes|no Trace all mutex activity [no].\n" " --trace-rwlock=yes|no Trace all reader-writer lock activity[no].\n" " --trace-semaphore=yes|no Trace all semaphore activity [no].\n", DRD_(thread_get_segment_merge_interval)() ); VG_(replacement_malloc_print_usage)(); }
/** * Remove the information that was stored about the client object p. * * @note The order of operations below is important. The client object is * removed from the client object set after the cleanup function has been * called such that if the cleanup function can still use the function * DRD_(clientobj_get_any)(). This happens e.g. in the function * first_observed() in drd_error.c. */ static Bool clientobj_remove_obj(DrdClientobj* const p) { tl_assert(p); if (s_trace_clientobj) { DRD_(trace_msg)("Removing client object 0x%lx of type %d", p->any.a1, p->any.type); #if 0 VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), VG_(clo_backtrace_size)); #endif } tl_assert(p->any.cleanup); (*p->any.cleanup)(p); VG_(OSetGen_Remove)(s_clientobj_set, &p->any.a1); VG_(OSetGen_FreeNode)(s_clientobj_set, p); return True; }
void bm_test1(void) { struct bitmap* bm; struct bitmap* bm2; unsigned i, j; bm = DRD_(bm_new)(); for (i = 0; i < sizeof(s_test1_args)/sizeof(s_test1_args[0]); i++) { DRD_(bm_access_range)(bm, s_test1_args[i].address, s_test1_args[i].address + s_test1_args[i].size, s_test1_args[i].access_type); } for (i = 0; i < sizeof(s_test1_args)/sizeof(s_test1_args[0]); i++) { for (j = 0; first_address_with_higher_lsb(j) <= s_test1_args[i].size; j = first_address_with_higher_lsb(j)) { tl_assert(DRD_(bm_has_1)(bm, s_test1_args[i].address + j, s_test1_args[i].access_type)); } } bm2 = DRD_(bm_new)(); DRD_(bm_merge2)(bm2, bm); DRD_(bm_merge2)(bm2, bm); assert(bm_equal_print_diffs(bm2, bm)); if (s_verbose) VG_(printf)("Deleting bitmap bm\n"); DRD_(bm_delete)(bm); if (s_verbose) VG_(printf)("Deleting bitmap bm2\n"); DRD_(bm_delete)(bm2); }
static void drd_pre_mem_read_asciiz(const CorePart part, const ThreadId tid, Char* const s, const Addr a) { const char* p = (void*)a; SizeT size = 0; /* Note: the expression '*p' reads client memory and may crash if the */ /* client provided an invalid pointer ! */ while (*p) { p++; size++; } if (size > 0) { DRD_(trace_load)(a, size); } }
/** * Combine the vector clock corresponding to the last unlock operation of * reader-writer lock p into the vector clock of thread 'tid'. */ static void DRD_(rwlock_combine_other_vc)(struct rwlock_info* const p, const DrdThreadId tid, const Bool readers_too) { struct rwlock_thread_info* q; VectorClock old_vc; DRD_(vc_copy)(&old_vc, DRD_(thread_get_vc)(tid)); VG_(OSetGen_ResetIter)(p->thread_info); for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; ) { if (q->tid != tid) { if (q->latest_wrlocked_segment) DRD_(vc_combine)(DRD_(thread_get_vc)(tid), &q->latest_wrlocked_segment->vc); if (readers_too && q->latest_rdlocked_segment) DRD_(vc_combine)(DRD_(thread_get_vc)(tid), &q->latest_rdlocked_segment->vc); } } DRD_(thread_update_conflict_set)(tid, &old_vc); DRD_(vc_cleanup)(&old_vc); }
// pthread_mutex_init PTH_FUNC(int, pthreadZumutexZuinit, pthread_mutex_t *mutex, const pthread_mutexattr_t* attr) { int ret; int res; OrigFn fn; int mt; VALGRIND_GET_ORIG_FN(fn); mt = PTHREAD_MUTEX_DEFAULT; if (attr) pthread_mutexattr_gettype(attr, &mt); VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_MUTEX_INIT, mutex, DRD_(pthread_to_drd_mutex_type)(mt), 0, 0, 0); CALL_FN_W_WW(ret, fn, mutex, attr); VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_INIT, mutex, 0, 0, 0, 0); return ret; }
static void drd_pre_thread_create(const ThreadId creator, const ThreadId created) { const DrdThreadId drd_creator = DRD_(VgThreadIdToDrdThreadId)(creator); tl_assert(created != VG_INVALID_THREADID); DRD_(thread_pre_create)(drd_creator, created); if (DRD_(IsValidDrdThreadId)(drd_creator)) { DRD_(thread_new_segment)(drd_creator); } if (DRD_(thread_get_trace_fork_join)()) { DRD_(trace_msg)("drd_pre_thread_create creator = %d, created = %d", drd_creator, created); } }
static void drd_pre_mem_read_asciiz(const CorePart part, const ThreadId tid, Char* const s, const Addr a) { const char* p = (void*)a; SizeT size = 0; /* Note: the expression '*p' reads client memory and may crash if the */ /* client provided an invalid pointer ! */ while (*p) { p++; size++; } // To do: find out what a reasonable upper limit on 'size' is. tl_assert(size < 4096); if (size > 0) { DRD_(trace_load)(a, size); } }
/** * Clean up all client objects p for which their start address p->any.a1 fits * inside the address range [ a1, a2 [. * * @note The implementation of this function relies on the fact that the * data in s_clientobj_set is sorted on the start address of client objects. */ void DRD_(clientobj_stop_using_mem)(const Addr a1, const Addr a2) { Addr removed_at; DrdClientobj* p; tl_assert(s_clientobj_set); if (! DRD_(range_contains_suppression_or_hbvar)(a1, a2)) return; VG_(OSetGen_ResetIterAt)(s_clientobj_set, &a1); for ( ; (p = VG_(OSetGen_Next)(s_clientobj_set)) != 0 && p->any.a1 < a2; ) { tl_assert(a1 <= p->any.a1); removed_at = p->any.a1; clientobj_remove_obj(p); /* * The above call removes an element from the oset and hence * invalidates the iterator. Restore the iterator. */ VG_(OSetGen_ResetIterAt)(s_clientobj_set, &removed_at); } }
static __always_inline int pthread_create_intercept(pthread_t* thread, const pthread_attr_t* attr, void* (*start)(void*), void* arg) { int ret; OrigFn fn; DrdSema wrapper_started; DrdPosixThreadArgs thread_args; VALGRIND_GET_ORIG_FN(fn); DRD_(sema_init)(&wrapper_started); thread_args.start = start; thread_args.arg = arg; thread_args.wrapper_started = &wrapper_started; /* * Find out whether the thread will be started as a joinable thread * or as a detached thread. If no thread attributes have been specified, * this means that the new thread will be started as a joinable thread. */ thread_args.detachstate = PTHREAD_CREATE_JOINABLE; if (attr) { if (pthread_attr_getdetachstate(attr, &thread_args.detachstate) != 0) assert(0); } assert(thread_args.detachstate == PTHREAD_CREATE_JOINABLE || thread_args.detachstate == PTHREAD_CREATE_DETACHED); DRD_(entering_pthread_create)(); CALL_FN_W_WWWW(ret, fn, thread, attr, DRD_(thread_wrapper), &thread_args); DRD_(left_pthread_create)(); if (ret == 0) { /* Wait until the thread wrapper started. */ DRD_(sema_down)(&wrapper_started); } DRD_(sema_destroy)(&wrapper_started); VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_START_NEW_SEGMENT, pthread_self(), 0, 0, 0, 0); return ret; }
/* with arguments (0,1). */ static void drd_post_thread_create(const ThreadId vg_created) { DrdThreadId drd_created; tl_assert(vg_created != VG_INVALID_THREADID); drd_created = DRD_(thread_post_create)(vg_created); if (DRD_(thread_get_trace_fork_join)()) { VG_(message)(Vg_DebugMsg, "drd_post_thread_create created = %d/%d", vg_created, drd_created); } if (! DRD_(get_check_stack_accesses)()) { DRD_(start_suppression)(DRD_(thread_get_stack_max)(drd_created) - DRD_(thread_get_stack_size)(drd_created), DRD_(thread_get_stack_max)(drd_created), "stack"); } }
/** * Stop and print an error message in case a non-supported threading * library implementation (LinuxThreads) has been detected. */ static void DRD_(check_threading_library)(void) { if (DRD_(detected_linuxthreads)()) { if (getenv("LD_ASSUME_KERNEL")) { fprintf(stderr, "Detected the LinuxThreads threading library. Sorry, but DRD only supports\n" "the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n" "after having unset the environment variable LD_ASSUME_KERNEL. Giving up.\n" ); } else { fprintf(stderr, "Detected the LinuxThreads threading library. Sorry, but DRD only supports\n" "the newer NPTL (Native POSIX Threads Library). Please try to rerun DRD\n" "after having upgraded to a newer version of your Linux distribution.\n" "Giving up.\n" ); } abort(); } }
/** * Compare the type of the rwlock specified at initialization time with * the type passed as an argument, and complain if these two types do not * match. */ static Bool drd_rwlock_check_type(struct rwlock_info* const p, const RwLockT rwlock_type) { tl_assert(p); /* The code below has to be updated if additional rwlock types are added. */ tl_assert(rwlock_type == pthread_rwlock || rwlock_type == user_rwlock); tl_assert(p->rwlock_type == pthread_rwlock || p->rwlock_type == user_rwlock); if (p->rwlock_type == rwlock_type) return True; { RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 }; VG_(maybe_record_error) (VG_(get_running_tid)(), RwlockErr, VG_(get_IP)(VG_(get_running_tid)()), rwlock_type == pthread_rwlock ? "Attempt to use a user-defined rwlock as a POSIX rwlock" : "Attempt to use a POSIX rwlock as a user-defined rwlock", &REI); } return False; }
static __inline__ void drd_stop_using_mem(const Addr a1, const SizeT len, const Bool is_stack_mem) { const Addr a2 = a1 + len; tl_assert(a1 < a2); if (UNLIKELY(DRD_(any_address_is_traced)())) { DRD_(trace_mem_access)(a1, len, eEnd); } if (! is_stack_mem || DRD_(get_check_stack_accesses)()) { DRD_(thread_stop_using_mem)(a1, a2); DRD_(clientobj_stop_using_mem)(a1, a2); DRD_(suppression_stop_using_mem)(a1, a2); } }
static __inline__ void drd_stop_using_mem(const Addr a1, const SizeT len, const Bool is_stack_mem) { const Addr a2 = a1 + len; tl_assert(a1 <= a2); if (UNLIKELY(DRD_(any_address_is_traced)())) DRD_(trace_mem_access)(a1, len, eEnd); if (!is_stack_mem && s_trace_alloc) VG_(message)(Vg_UserMsg, "Stopped using memory range 0x%lx + %ld\n", a1, len); if (!is_stack_mem || DRD_(get_check_stack_accesses)()) { DRD_(thread_stop_using_mem)(a1, a2, !is_stack_mem && s_free_is_write); DRD_(clientobj_stop_using_mem)(a1, a2); DRD_(suppression_stop_using_mem)(a1, a2); } if (!is_stack_mem && s_free_is_write) DRD_(trace_store)(a1, len); }
/** * Deallocate the memory owned by the struct barrier_info object and also * all the nodes in the OSet p->oset. * * Called by clientobj_destroy(). */ static void barrier_cleanup(struct barrier_info* p) { struct barrier_thread_info* q; Segment* latest_sg = 0; OSet* oset; int i; tl_assert(p); DRD_(thread_get_latest_segment)(&latest_sg, DRD_(thread_get_running_tid)()); tl_assert(latest_sg); if (p->pre_waiters_left != p->count) { BarrierErrInfo bei = { DRD_(thread_get_running_tid)(), p->a1, 0, 0 }; VG_(maybe_record_error)(VG_(get_running_tid)(), BarrierErr, VG_(get_IP)(VG_(get_running_tid)()), "Destruction of barrier that is being waited" " upon", &bei); } else { oset = p->oset[1 - (p->pre_iteration & 1)]; VG_(OSetGen_ResetIter)(oset); for ( ; (q = VG_(OSetGen_Next)(oset)) != 0; ) { if (q->post_wait_sg && !DRD_(vc_lte)(&q->post_wait_sg->vc, &latest_sg->vc)) { barrier_report_wait_delete_race(p, q); } DRD_(barrier_thread_destroy)(q); } } for (i = 0; i < 2; i++) { VG_(OSetGen_Destroy)(p->oset[i]); p->oset[i] = NULL; } DRD_(sg_put)(latest_sg); }
/** Called before pthread_mutex_init(). */ struct mutex_info* DRD_(mutex_init)(const Addr mutex, const MutexT mutex_type) { struct mutex_info* p; if (s_trace_mutex) { VG_(message)(Vg_UserMsg, "[%d] mutex_init %s 0x%lx\n", DRD_(thread_get_running_tid)(), DRD_(mutex_type_name)(mutex_type), mutex); } if (mutex_type == mutex_type_invalid_mutex) { DRD_(not_a_mutex)(mutex); return 0; } p = DRD_(mutex_get)(mutex); if (p) { const ThreadId vg_tid = VG_(get_running_tid)(); MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), p->a1, p->recursion_count, p->owner }; VG_(maybe_record_error)(vg_tid, MutexErr, VG_(get_IP)(vg_tid), "Mutex reinitialization", &MEI); p->mutex_type = mutex_type; return p; } p = DRD_(mutex_get_or_allocate)(mutex, mutex_type); return p; }
void DRD_(cond_set_trace)(const Bool trace_cond) { DRD_(s_trace_cond) = trace_cond; }
void DRD_(cond_set_report_signal_unlocked)(const Bool r) { DRD_(s_report_signal_unlocked) = r; }
q ? q->owner : DRD_INVALID_THREADID }; VG_(maybe_record_error)(VG_(get_running_tid)(), CondDestrErr, VG_(get_IP)(VG_(get_running_tid)()), "Destroying condition variable that is being" " waited upon", &cde); } } } static void wrong_type(const Addr addr) { GenericErrInfo gei = { .tid = DRD_(thread_get_running_tid)(), .addr = addr, }; VG_(maybe_record_error)(VG_(get_running_tid)(), GenericErr, VG_(get_IP)(VG_(get_running_tid)()), "wrong type of synchronization object", &gei); } static struct cond_info* cond_get_or_allocate(const Addr cond) { struct cond_info *p; tl_assert(offsetof(DrdClientobj, cond) == 0); p = &(DRD_(clientobj_get)(cond, ClientCondvar)->cond);
/** * Deallocate the memory that is owned by members of * struct barrier_thread_info. */ static void DRD_(barrier_thread_destroy)(struct barrier_thread_info* const p) { tl_assert(p); DRD_(sg_put)(p->sg); DRD_(sg_put)(p->post_wait_sg); }