1 /* -*- mode: C; c-basic-offset: 3; -*- */ 2 /* 3 This file is part of drd, a thread error detector. 4 5 Copyright (C) 2006-2010 Bart Van Assche <bvanassche (at) acm.org>. 6 7 This program is free software; you can redistribute it and/or 8 modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the 10 License, or (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 20 02111-1307, USA. 21 22 The GNU General Public License is contained in the file COPYING. 23 */ 24 25 26 #include "drd_clientobj.h" 27 #include "drd_cond.h" 28 #include "drd_error.h" 29 #include "drd_mutex.h" 30 #include "pub_tool_errormgr.h" /* VG_(maybe_record_error)() */ 31 #include "pub_tool_libcassert.h" /* tl_assert() */ 32 #include "pub_tool_libcbase.h" /* VG_(memcmp)() */ 33 #include "pub_tool_libcprint.h" /* VG_(printf)() */ 34 #include "pub_tool_machine.h" /* VG_(get_IP)() */ 35 #include "pub_tool_threadstate.h" /* VG_(get_running_tid)() */ 36 37 38 /* Local functions. */ 39 40 static void DRD_(cond_cleanup)(struct cond_info* p); 41 42 43 /* Local variables. */ 44 45 static Bool DRD_(s_report_signal_unlocked) = True; 46 static Bool DRD_(s_trace_cond); 47 48 49 /* Global variables. */ 50 51 Addr DRD_(pthread_cond_initializer); 52 int DRD_(pthread_cond_initializer_size); 53 54 55 /* Function definitions. */ 56 57 void DRD_(cond_set_report_signal_unlocked)(const Bool r) 58 { 59 DRD_(s_report_signal_unlocked) = r; 60 } 61 62 void DRD_(cond_set_trace)(const Bool trace_cond) 63 { 64 DRD_(s_trace_cond) = trace_cond; 65 } 66 67 static 68 void DRD_(cond_initialize)(struct cond_info* const p, const Addr cond) 69 { 70 tl_assert(cond != 0); 71 tl_assert(p->a1 == cond); 72 tl_assert(p->type == ClientCondvar); 73 74 p->cleanup = (void(*)(DrdClientobj*))(DRD_(cond_cleanup)); 75 p->delete_thread = 0; 76 p->waiter_count = 0; 77 p->mutex = 0; 78 } 79 80 /** 81 * Free the memory that was allocated by cond_initialize(). Called by 82 * DRD_(clientobj_remove)(). 83 */ 84 static void DRD_(cond_cleanup)(struct cond_info* p) 85 { 86 tl_assert(p); 87 if (p->mutex) 88 { 89 struct mutex_info* q; 90 q = &(DRD_(clientobj_get)(p->mutex, ClientMutex)->mutex); 91 { 92 CondDestrErrInfo cde = { 93 DRD_(thread_get_running_tid)(), 94 p->a1, 95 q ? q->a1 : 0, 96 q ? q->owner : DRD_INVALID_THREADID 97 }; 98 VG_(maybe_record_error)(VG_(get_running_tid)(), 99 CondDestrErr, 100 VG_(get_IP)(VG_(get_running_tid)()), 101 "Destroying condition variable that is being" 102 " waited upon", 103 &cde); 104 } 105 } 106 } 107 108 /** 109 * Report that the synchronization object at address 'addr' is of the 110 * wrong type. 111 */ 112 static void wrong_type(const Addr addr) 113 { 114 GenericErrInfo gei = { 115 .tid = DRD_(thread_get_running_tid)(), 116 .addr = addr, 117 }; 118 VG_(maybe_record_error)(VG_(get_running_tid)(), 119 GenericErr, 120 VG_(get_IP)(VG_(get_running_tid)()), 121 "wrong type of synchronization object", 122 &gei); 123 } 124 125 static struct cond_info* cond_get_or_allocate(const Addr cond) 126 { 127 struct cond_info *p; 128 129 tl_assert(offsetof(DrdClientobj, cond) == 0); 130 p = &(DRD_(clientobj_get)(cond, ClientCondvar)->cond); 131 if (p) 132 return p; 133 134 if (DRD_(clientobj_present)(cond, cond + 1)) 135 { 136 wrong_type(cond); 137 return 0; 138 } 139 140 p = &(DRD_(clientobj_add)(cond, ClientCondvar)->cond); 141 DRD_(cond_initialize)(p, cond); 142 return p; 143 } 144 145 struct cond_info* DRD_(cond_get)(const Addr cond) 146 { 147 tl_assert(offsetof(DrdClientobj, cond) == 0); 148 return &(DRD_(clientobj_get)(cond, ClientCondvar)->cond); 149 } 150 151 /** Called before pthread_cond_init(). */ 152 void DRD_(cond_pre_init)(const Addr cond) 153 { 154 struct cond_info* p; 155 156 if (DRD_(s_trace_cond)) 157 { 158 VG_(message)(Vg_UserMsg, 159 "[%d] cond_init cond 0x%lx\n", 160 DRD_(thread_get_running_tid)(), 161 cond); 162 } 163 164 p = DRD_(cond_get)(cond); 165 166 if (p) 167 { 168 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 169 VG_(maybe_record_error)(VG_(get_running_tid)(), 170 CondErr, 171 VG_(get_IP)(VG_(get_running_tid)()), 172 "initialized twice", 173 &cei); 174 } 175 176 p = cond_get_or_allocate(cond); 177 } 178 179 /** Called after pthread_cond_destroy(). */ 180 void DRD_(cond_post_destroy)(const Addr cond) 181 { 182 struct cond_info* p; 183 184 if (DRD_(s_trace_cond)) 185 { 186 VG_(message)(Vg_UserMsg, 187 "[%d] cond_destroy cond 0x%lx\n", 188 DRD_(thread_get_running_tid)(), 189 cond); 190 } 191 192 p = DRD_(cond_get)(cond); 193 if (p == 0) 194 { 195 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 196 VG_(maybe_record_error)(VG_(get_running_tid)(), 197 CondErr, 198 VG_(get_IP)(VG_(get_running_tid)()), 199 "not a condition variable", 200 &cei); 201 return; 202 } 203 204 if (p->waiter_count != 0) 205 { 206 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 207 VG_(maybe_record_error)(VG_(get_running_tid)(), 208 CondErr, 209 VG_(get_IP)(VG_(get_running_tid)()), 210 "destruction of condition variable being waited" 211 " upon", 212 &cei); 213 } 214 215 DRD_(clientobj_remove)(p->a1, ClientCondvar); 216 } 217 218 /** 219 * Called before pthread_cond_wait(). Note: before this function is called, 220 * mutex_unlock() has already been called from drd_clientreq.c. 221 */ 222 void DRD_(cond_pre_wait)(const Addr cond, const Addr mutex) 223 { 224 struct cond_info* p; 225 struct mutex_info* q; 226 227 if (DRD_(s_trace_cond)) 228 { 229 VG_(message)(Vg_UserMsg, 230 "[%d] cond_pre_wait cond 0x%lx\n", 231 DRD_(thread_get_running_tid)(), 232 cond); 233 } 234 235 p = cond_get_or_allocate(cond); 236 if (!p) 237 { 238 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 239 VG_(maybe_record_error)(VG_(get_running_tid)(), 240 CondErr, 241 VG_(get_IP)(VG_(get_running_tid)()), 242 "not a condition variable", 243 &cei); 244 return; 245 } 246 247 if (p->waiter_count == 0) 248 { 249 p->mutex = mutex; 250 } 251 else if (p->mutex != mutex) 252 { 253 CondWaitErrInfo cwei 254 = { .tid = DRD_(thread_get_running_tid)(), 255 .cond = cond, .mutex1 = p->mutex, .mutex2 = mutex }; 256 VG_(maybe_record_error)(VG_(get_running_tid)(), 257 CondWaitErr, 258 VG_(get_IP)(VG_(get_running_tid)()), 259 "Inconsistent association of condition variable" 260 " and mutex", 261 &cwei); 262 } 263 tl_assert(p->mutex); 264 q = DRD_(mutex_get)(p->mutex); 265 if (q 266 && q->owner == DRD_(thread_get_running_tid)() && q->recursion_count > 0) 267 { 268 const ThreadId vg_tid = VG_(get_running_tid)(); 269 MutexErrInfo MEI = { DRD_(thread_get_running_tid)(), 270 q->a1, q->recursion_count, q->owner }; 271 VG_(maybe_record_error)(vg_tid, 272 MutexErr, 273 VG_(get_IP)(vg_tid), 274 "Mutex locked recursively", 275 &MEI); 276 } 277 else if (q == 0) 278 { 279 DRD_(not_a_mutex)(p->mutex); 280 } 281 282 ++p->waiter_count; 283 } 284 285 /** 286 * Called after pthread_cond_wait(). 287 */ 288 void DRD_(cond_post_wait)(const Addr cond) 289 { 290 struct cond_info* p; 291 292 if (DRD_(s_trace_cond)) 293 { 294 VG_(message)(Vg_UserMsg, 295 "[%d] cond_post_wait cond 0x%lx\n", 296 DRD_(thread_get_running_tid)(), 297 cond); 298 } 299 300 p = DRD_(cond_get)(cond); 301 if (!p) 302 { 303 struct mutex_info* q; 304 q = &(DRD_(clientobj_get)(p->mutex, ClientMutex)->mutex); 305 { 306 CondDestrErrInfo cde = { 307 DRD_(thread_get_running_tid)(), 308 p->a1, 309 q ? q->a1 : 0, 310 q ? q->owner : DRD_INVALID_THREADID 311 }; 312 VG_(maybe_record_error)(VG_(get_running_tid)(), 313 CondDestrErr, 314 VG_(get_IP)(VG_(get_running_tid)()), 315 "condition variable has been destroyed while" 316 " being waited upon", 317 &cde); 318 } 319 return; 320 } 321 322 if (p->waiter_count > 0) 323 { 324 --p->waiter_count; 325 if (p->waiter_count == 0) 326 { 327 p->mutex = 0; 328 } 329 } 330 } 331 332 static void cond_signal(const DrdThreadId tid, struct cond_info* const cond_p) 333 { 334 const ThreadId vg_tid = VG_(get_running_tid)(); 335 const DrdThreadId drd_tid = DRD_(VgThreadIdToDrdThreadId)(vg_tid); 336 337 tl_assert(cond_p); 338 339 if (cond_p->waiter_count > 0) 340 { 341 if (DRD_(s_report_signal_unlocked) 342 && ! DRD_(mutex_is_locked_by)(cond_p->mutex, drd_tid)) 343 { 344 /* 345 * A signal is sent while the associated mutex has not been locked. 346 * This can indicate but is not necessarily a race condition. 347 */ 348 CondRaceErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), 349 .cond = cond_p->a1, 350 .mutex = cond_p->mutex, 351 }; 352 VG_(maybe_record_error)(vg_tid, 353 CondRaceErr, 354 VG_(get_IP)(vg_tid), 355 "CondErr", 356 &cei); 357 } 358 } 359 else 360 { 361 /* 362 * No other thread is waiting for the signal, hence the signal will 363 * be lost. This is normal in a POSIX threads application. 364 */ 365 } 366 } 367 368 static void not_initialized(Addr const cond) 369 { 370 CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond }; 371 VG_(maybe_record_error)(VG_(get_running_tid)(), 372 CondErr, 373 VG_(get_IP)(VG_(get_running_tid)()), 374 "condition variable has not been initialized", 375 &cei); 376 } 377 378 /** Called before pthread_cond_signal(). */ 379 void DRD_(cond_pre_signal)(Addr const cond) 380 { 381 struct cond_info* p; 382 383 p = DRD_(cond_get)(cond); 384 if (DRD_(s_trace_cond)) 385 { 386 VG_(message)(Vg_UserMsg, 387 "[%d] cond_signal cond 0x%lx\n", 388 DRD_(thread_get_running_tid)(), 389 cond); 390 } 391 392 tl_assert(DRD_(pthread_cond_initializer)); 393 if (!p && VG_(memcmp)((void*)cond, (void*)DRD_(pthread_cond_initializer), 394 DRD_(pthread_cond_initializer_size)) != 0) 395 { 396 not_initialized(cond); 397 return; 398 } 399 400 if (!p) 401 p = cond_get_or_allocate(cond); 402 403 cond_signal(DRD_(thread_get_running_tid)(), p); 404 } 405 406 /** Called before pthread_cond_broadcast(). */ 407 void DRD_(cond_pre_broadcast)(Addr const cond) 408 { 409 struct cond_info* p; 410 411 if (DRD_(s_trace_cond)) 412 { 413 VG_(message)(Vg_UserMsg, 414 "[%d] cond_broadcast cond 0x%lx\n", 415 DRD_(thread_get_running_tid)(), 416 cond); 417 } 418 419 p = DRD_(cond_get)(cond); 420 tl_assert(DRD_(pthread_cond_initializer)); 421 if (!p && VG_(memcmp)((void*)cond, (void*)DRD_(pthread_cond_initializer), 422 DRD_(pthread_cond_initializer_size)) != 0) 423 { 424 not_initialized(cond); 425 return; 426 } 427 428 if (!p) 429 p = cond_get_or_allocate(cond); 430 431 cond_signal(DRD_(thread_get_running_tid)(), p); 432 } 433