sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame^] | 1 | |
| 2 | /*--------------------------------------------------------------------*/ |
| 3 | /*--- Thread modelling. vg_threadmodel.c ---*/ |
| 4 | /*--------------------------------------------------------------------*/ |
| 5 | |
| 6 | /* |
| 7 | This file is part of Valgrind, a dynamic binary instrumentation |
| 8 | framework. |
| 9 | |
| 10 | Copyright (C) 2005 Jeremy Fitzhardinge |
| 11 | jeremy@goop.org |
| 12 | |
| 13 | This program is free software; you can redistribute it and/or |
| 14 | modify it under the terms of the GNU General Public License as |
| 15 | published by the Free Software Foundation; either version 2 of the |
| 16 | License, or (at your option) any later version. |
| 17 | |
| 18 | This program is distributed in the hope that it will be useful, but |
| 19 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 21 | General Public License for more details. |
| 22 | |
| 23 | You should have received a copy of the GNU General Public License |
| 24 | along with this program; if not, write to the Free Software |
| 25 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 26 | 02111-1307, USA. |
| 27 | |
| 28 | The GNU General Public License is contained in the file COPYING. |
| 29 | */ |
| 30 | |
| 31 | /* |
| 32 | This file implements an abstract thread model, by which a client's |
| 33 | thread usage is validated. The model the file implements is |
| 34 | intended to be enough to contain pthreads, or perhaps a superset, |
| 35 | but it is not pthread-specific; that's done in in vg_pthreadmodel.c |
| 36 | |
| 37 | While the primary client of this file is vg_pthreadmodel.c, it is |
| 38 | also intended that clients can make direct use of this file for |
| 39 | home-grown threading libraries. It is therefore useful for both |
| 40 | validating the library itself as well as the users of the library. |
| 41 | |
| 42 | A note on terminology: |
| 43 | |
| 44 | The states referred to in this file ("blocked state", "zombie |
| 45 | state") are specific to this threads model, and have nothing do to |
| 46 | with the scheduler status for a thread. For example, a thread |
| 47 | could be "blocked" in a lock but be in VgTs_Runnable status, |
| 48 | because the lock is actually a spinlock. |
| 49 | |
| 50 | "Fails" means "reports an error" and possibly means that the model |
| 51 | is getting out of sync with the actual implementation. This model |
| 52 | only reports problems to the user, and doesn't attempt to actually |
| 53 | change the behaviour of the implementation. |
| 54 | |
| 55 | NB: |
| 56 | |
| 57 | This file assumes there's a 1:1 relationship between application |
| 58 | threads and Valgrind threads, which means that 1:N and M:N thread |
| 59 | models are not (yet) supported. At some point we may need to |
| 60 | introduce a separate notion of a "thread" for modelling purposes. |
| 61 | */ |
| 62 | |
| 63 | //:: |
| 64 | //:: #include "core.h" |
| 65 | //:: |
| 66 | //:: struct thread; |
| 67 | //:: struct mutex; |
| 68 | //:: struct condvar; |
| 69 | //:: |
| 70 | //:: static const Bool debug_thread = False; |
| 71 | //:: static const Bool debug_mutex = False; |
| 72 | //:: |
| 73 | //:: /* -------------------------------------------------- |
| 74 | //:: Thread lifetime |
| 75 | //:: |
| 76 | //:: Threads are all expressed in terms of internal ThreadIds. The |
| 77 | //:: thread library interface needs to map from the library's identifers |
| 78 | //:: to ThreadIds. |
| 79 | //:: -------------------------------------------------- */ |
| 80 | //:: |
| 81 | //:: /* Per-thread state. We maintain our own here rather than hanging it |
| 82 | //:: off ThreadState, so that we have the option of not having a 1:1 |
| 83 | //:: relationship between modelled threads and Valgrind threads. */ |
| 84 | //:: struct thread |
| 85 | //:: { |
| 86 | //:: ThreadId tid; |
| 87 | //:: ThreadId creator; |
| 88 | //:: |
| 89 | //:: Bool detached; /* thread is detached */ |
| 90 | //:: |
| 91 | //:: enum thread_state { |
| 92 | //:: TS_Alive, /* alive */ |
| 93 | //:: TS_Zombie, /* waiting to be joined on (detached is False) */ |
| 94 | //:: TS_Dead, /* all dead */ |
| 95 | //:: |
| 96 | //:: TS_Running, /* running */ |
| 97 | //:: TS_MutexBlocked, /* blocked on mutex */ |
| 98 | //:: TS_CVBlocked, /* blocked on condvar */ |
| 99 | //:: TS_JoinBlocked, /* blocked in join */ |
| 100 | //:: } state; |
| 101 | //:: |
| 102 | //:: struct mutex *mx_blocked; /* mutex we're blocked on (state==TS_MutexBlocked) */ |
| 103 | //:: struct condvar *cv_blocked; /* condvar we're blocked on (state==TS_CVBlocked) */ |
| 104 | //:: struct thread *th_blocked; /* thread we're blocked on (state==TS_JoinBlocked) */ |
| 105 | //:: |
| 106 | //:: ExeContext *ec_created; /* where created */ |
| 107 | //:: ExeContext *ec_blocked; /* where blocked/unblocked */ |
| 108 | //:: }; |
| 109 | //:: |
| 110 | //:: enum thread_error |
| 111 | //:: { |
| 112 | //:: THE_NotExist, /* thread doesn't exist */ |
| 113 | //:: THE_NotAlive, /* thread isn't alive (use after death) */ |
| 114 | //:: THE_Rebirth, /* thread already alive */ |
| 115 | //:: THE_Blocked, /* thread not supposed to be blocked */ |
| 116 | //:: THE_NotBlocked, /* thread supposed to be blocked */ |
| 117 | //:: THE_Detached, /* thread is detached */ |
| 118 | //:: }; |
| 119 | //:: |
| 120 | //:: struct thread_error_data |
| 121 | //:: { |
| 122 | //:: enum thread_error err; |
| 123 | //:: struct thread *th; |
| 124 | //:: const Char *action; |
| 125 | //:: }; |
| 126 | //:: |
| 127 | //:: static const Char *pp_threadstate(const struct thread *th) |
| 128 | //:: { |
| 129 | //:: if (th == NULL) |
| 130 | //:: return "non-existent"; |
| 131 | //:: |
| 132 | //:: switch(th->state) { |
| 133 | //:: case TS_Alive: return "alive"; |
| 134 | //:: case TS_Zombie: return "zombie"; |
| 135 | //:: case TS_Dead: return "dead"; |
| 136 | //:: case TS_Running: return "running"; |
| 137 | //:: case TS_MutexBlocked:return "mutex-blocked"; |
| 138 | //:: case TS_CVBlocked: return "cv-blocked"; |
| 139 | //:: case TS_JoinBlocked: return "join-blocked"; |
| 140 | //:: default: return "???"; |
| 141 | //:: } |
| 142 | //:: } |
| 143 | //:: |
| 144 | //:: static void thread_validate(struct thread *th) |
| 145 | //:: { |
| 146 | //:: switch(th->state) { |
| 147 | //:: case TS_Alive: |
| 148 | //:: case TS_Running: |
| 149 | //:: case TS_Dead: |
| 150 | //:: case TS_Zombie: |
| 151 | //:: vg_assert(th->mx_blocked == NULL); |
| 152 | //:: vg_assert(th->cv_blocked == NULL); |
| 153 | //:: vg_assert(th->th_blocked == NULL); |
| 154 | //:: break; |
| 155 | //:: |
| 156 | //:: case TS_MutexBlocked: |
| 157 | //:: vg_assert(th->mx_blocked != NULL); |
| 158 | //:: vg_assert(th->cv_blocked == NULL); |
| 159 | //:: vg_assert(th->th_blocked == NULL); |
| 160 | //:: break; |
| 161 | //:: |
| 162 | //:: case TS_CVBlocked: |
| 163 | //:: vg_assert(th->mx_blocked == NULL); |
| 164 | //:: vg_assert(th->cv_blocked != NULL); |
| 165 | //:: vg_assert(th->th_blocked == NULL); |
| 166 | //:: break; |
| 167 | //:: |
| 168 | //:: case TS_JoinBlocked: |
| 169 | //:: vg_assert(th->mx_blocked == NULL); |
| 170 | //:: vg_assert(th->cv_blocked == NULL); |
| 171 | //:: vg_assert(th->th_blocked != NULL); |
| 172 | //:: break; |
| 173 | //:: } |
| 174 | //:: } |
| 175 | //:: |
| 176 | //:: static void thread_setstate(struct thread *th, enum thread_state state) |
| 177 | //:: { |
| 178 | //:: ExeContext *ec; |
| 179 | //:: |
| 180 | //:: if (th->state == state) |
| 181 | //:: return; |
| 182 | //:: |
| 183 | //:: ec = VG_(get_ExeContext)(th->tid); |
| 184 | //:: |
| 185 | //:: switch(state) { |
| 186 | //:: case TS_Alive: |
| 187 | //:: case TS_Dead: |
| 188 | //:: th->ec_created = ec; |
| 189 | //:: break; |
| 190 | //:: |
| 191 | //:: case TS_Running: |
| 192 | //:: case TS_MutexBlocked: |
| 193 | //:: case TS_CVBlocked: |
| 194 | //:: case TS_JoinBlocked: |
| 195 | //:: case TS_Zombie: |
| 196 | //:: th->ec_blocked = ec; |
| 197 | //:: } |
| 198 | //:: |
| 199 | //:: th->state = state; |
| 200 | //:: if (debug_thread) |
| 201 | //:: VG_(printf)("setting thread(%d) -> %s\n", th->tid, pp_threadstate(th)); |
| 202 | //:: thread_validate(th); |
| 203 | //:: } |
| 204 | //:: |
| 205 | //:: static void do_thread_run(struct thread *th) |
| 206 | //:: { |
| 207 | //:: th->mx_blocked = NULL; |
| 208 | //:: th->cv_blocked = NULL; |
| 209 | //:: th->th_blocked = NULL; |
| 210 | //:: thread_setstate(th, TS_Running); |
| 211 | //:: } |
| 212 | //:: |
| 213 | //:: static void do_thread_block_mutex(struct thread *th, struct mutex *mx) |
| 214 | //:: { |
| 215 | //:: th->mx_blocked = mx; |
| 216 | //:: th->cv_blocked = NULL; |
| 217 | //:: th->th_blocked = NULL; |
| 218 | //:: thread_setstate(th, TS_MutexBlocked); |
| 219 | //:: } |
| 220 | //:: |
| 221 | //:: static void do_thread_block_condvar(struct thread *th, struct condvar *cv) |
| 222 | //:: { |
| 223 | //:: th->mx_blocked = NULL; |
| 224 | //:: th->cv_blocked = cv; |
| 225 | //:: th->th_blocked = NULL; |
| 226 | //:: thread_setstate(th, TS_CVBlocked); |
| 227 | //:: } |
| 228 | //:: |
| 229 | //:: static void do_thread_block_join(struct thread *th, struct thread *joinee) |
| 230 | //:: { |
| 231 | //:: th->mx_blocked = NULL; |
| 232 | //:: th->cv_blocked = NULL; |
| 233 | //:: th->th_blocked = joinee; |
| 234 | //:: thread_setstate(th, TS_JoinBlocked); |
| 235 | //:: } |
| 236 | //:: |
| 237 | //:: static void do_thread_block_zombie(struct thread *th) |
| 238 | //:: { |
| 239 | //:: th->mx_blocked = NULL; |
| 240 | //:: th->cv_blocked = NULL; |
| 241 | //:: th->th_blocked = NULL; |
| 242 | //:: thread_setstate(th, TS_Zombie); |
| 243 | //:: } |
| 244 | //:: |
| 245 | //:: static void do_thread_dead(struct thread *th) |
| 246 | //:: { |
| 247 | //:: th->mx_blocked = NULL; |
| 248 | //:: th->cv_blocked = NULL; |
| 249 | //:: th->th_blocked = NULL; |
| 250 | //:: thread_setstate(th, TS_Dead); |
| 251 | //:: } |
| 252 | //:: |
| 253 | //:: static SkipList sk_threads = SKIPLIST_INIT(struct thread, tid, VG_(cmp_UInt), NULL, VG_AR_CORE); |
| 254 | //:: |
| 255 | //:: static struct thread *thread_get(ThreadId tid) |
| 256 | //:: { |
| 257 | //:: return VG_(SkipList_Find_Exact)(&sk_threads, &tid); |
| 258 | //:: } |
| 259 | //:: |
| 260 | //:: static void thread_report(ThreadId tid, enum thread_error err, const Char *action) |
| 261 | //:: { |
| 262 | //:: Char *errstr = "?"; |
| 263 | //:: struct thread *th = thread_get(tid); |
| 264 | //:: struct thread_error_data errdata; |
| 265 | //:: |
| 266 | //:: switch(err) { |
| 267 | //:: case THE_NotExist: errstr = "non existent"; break; |
| 268 | //:: case THE_NotAlive: errstr = "not alive"; break; |
| 269 | //:: case THE_Rebirth: errstr = "re-born"; break; |
| 270 | //:: case THE_Blocked: errstr = "blocked"; break; |
| 271 | //:: case THE_NotBlocked: errstr = "not blocked"; break; |
| 272 | //:: case THE_Detached: errstr = "detached"; break; |
| 273 | //:: } |
| 274 | //:: |
| 275 | //:: errdata.err = err; |
| 276 | //:: errdata.th = th; |
| 277 | //:: errdata.action = action; |
| 278 | //:: |
| 279 | //:: VG_(maybe_record_error)(VG_(get_running_tid)(), ThreadErr, 0, errstr, &errdata); |
| 280 | //:: } |
| 281 | //:: |
| 282 | //:: static void pp_thread_error(Error *err) |
| 283 | //:: { |
| 284 | //:: struct thread_error_data *errdata = VG_(get_error_extra)(err); |
| 285 | //:: struct thread *th = errdata->th; |
| 286 | //:: Char *errstr = VG_(get_error_string)(err); |
| 287 | //:: |
| 288 | //:: VG_(message)(Vg_UserMsg, "Found %s thread in state %s while %s", |
| 289 | //:: errstr, pp_threadstate(th), errdata->action); |
| 290 | //:: VG_(pp_ExeContext)(VG_(get_error_where)(err)); |
| 291 | //:: |
| 292 | //:: if (th) { |
| 293 | //:: VG_(message)(Vg_UserMsg, " Thread %d was %s", |
| 294 | //:: th->tid, th->state == TS_Dead ? "destroyed" : "created"); |
| 295 | //:: VG_(pp_ExeContext)(th->ec_created); |
| 296 | //:: } |
| 297 | //:: } |
| 298 | //:: |
| 299 | //:: /* Thread creation */ |
| 300 | //:: void VG_(tm_thread_create)(ThreadId creator, ThreadId tid, Bool detached) |
| 301 | //:: { |
| 302 | //:: struct thread *th = thread_get(tid); |
| 303 | //:: |
| 304 | //:: if (debug_thread) |
| 305 | //:: VG_(printf)("thread %d creates %d %s\n", creator, tid, detached ? "detached" : ""); |
| 306 | //:: if (th != NULL) { |
| 307 | //:: if (th->state != TS_Dead) |
| 308 | //:: thread_report(tid, THE_Rebirth, "creating"); |
| 309 | //:: } else { |
| 310 | //:: th = VG_(SkipNode_Alloc)(&sk_threads); |
| 311 | //:: th->tid = tid; |
| 312 | //:: VG_(SkipList_Insert)(&sk_threads, th); |
| 313 | //:: } |
| 314 | //:: |
| 315 | //:: th->creator = creator; |
| 316 | //:: th->detached = detached; |
| 317 | //:: th->mx_blocked = NULL; |
| 318 | //:: th->cv_blocked = NULL; |
| 319 | //:: th->th_blocked = NULL; |
| 320 | //:: |
| 321 | //:: thread_setstate(th, TS_Alive); |
| 322 | //:: do_thread_run(th); |
| 323 | //:: } |
| 324 | //:: |
| 325 | //:: Bool VG_(tm_thread_exists)(ThreadId tid) |
| 326 | //:: { |
| 327 | //:: struct thread *th = thread_get(tid); |
| 328 | //:: |
| 329 | //:: return th && th->state != TS_Dead; |
| 330 | //:: } |
| 331 | //:: |
| 332 | //:: /* A thread is terminating itself |
| 333 | //:: - fails if tid has already terminated |
| 334 | //:: - if detached, tid becomes invalid for all further operations |
| 335 | //:: - if not detached, the thread remains in a Zombie state until |
| 336 | //:: someone joins on it |
| 337 | //:: */ |
| 338 | //:: void VG_(tm_thread_exit)(ThreadId tid) |
| 339 | //:: { |
| 340 | //:: struct thread *th = thread_get(tid); |
| 341 | //:: |
| 342 | //:: if (th == NULL) |
| 343 | //:: thread_report(tid, THE_NotExist, "exiting"); |
| 344 | //:: else { |
| 345 | //:: struct thread *joiner; |
| 346 | //:: |
| 347 | //:: switch(th->state) { |
| 348 | //:: case TS_Dead: |
| 349 | //:: case TS_Zombie: /* already exited once */ |
| 350 | //:: thread_report(tid, THE_NotAlive, "exiting"); |
| 351 | //:: break; |
| 352 | //:: |
| 353 | //:: case TS_MutexBlocked: |
| 354 | //:: case TS_CVBlocked: |
| 355 | //:: case TS_JoinBlocked: |
| 356 | //:: thread_report(tid, THE_Blocked, "exiting"); |
| 357 | //:: break; |
| 358 | //:: |
| 359 | //:: case TS_Alive: |
| 360 | //:: case TS_Running: |
| 361 | //:: /* OK */ |
| 362 | //:: break; |
| 363 | //:: } |
| 364 | //:: |
| 365 | //:: /* ugly - walk all threads to find people joining with us */ |
| 366 | //:: /* In pthreads its an error to have multiple joiners, but that |
| 367 | //:: seems a bit specific to implement here; there should a way |
| 368 | //:: for the thread library binding to handle this. */ |
| 369 | //:: for(joiner = VG_(SkipNode_First)(&sk_threads); |
| 370 | //:: joiner != NULL; |
| 371 | //:: joiner = VG_(SkipNode_Next)(&sk_threads, joiner)) { |
| 372 | //:: if (joiner->state == TS_JoinBlocked && joiner->th_blocked == th) { |
| 373 | //:: /* found someone - wake them up */ |
| 374 | //:: do_thread_run(joiner); |
| 375 | //:: |
| 376 | //:: /* we're dead */ |
| 377 | //:: do_thread_dead(th); |
| 378 | //:: } |
| 379 | //:: } |
| 380 | //:: |
| 381 | //:: if (th->state != TS_Dead) |
| 382 | //:: do_thread_block_zombie(th); |
| 383 | //:: } |
| 384 | //:: } |
| 385 | //:: |
| 386 | //:: void VG_(tm_thread_detach)(ThreadId tid) |
| 387 | //:: { |
| 388 | //:: struct thread *th = thread_get(tid); |
| 389 | //:: |
| 390 | //:: if (th == NULL) |
| 391 | //:: thread_report(tid, THE_NotExist, "detaching"); |
| 392 | //:: else { |
| 393 | //:: if (th->detached) |
| 394 | //:: thread_report(tid, THE_Detached, "detaching"); |
| 395 | //:: else { |
| 396 | //:: /* XXX look for waiters */ |
| 397 | //:: th->detached = True; |
| 398 | //:: } |
| 399 | //:: } |
| 400 | //:: } |
| 401 | //:: |
| 402 | //:: /* One thread blocks until another has terminated |
| 403 | //:: - fails if joinee is detached |
| 404 | //:: - fails if joinee doesn't exist |
| 405 | //:: - once the join completes, joinee is dead |
| 406 | //:: */ |
| 407 | //:: void VG_(tm_thread_join)(ThreadId joinerid, ThreadId joineeid) |
| 408 | //:: { |
| 409 | //:: struct thread *joiner = thread_get(joinerid); |
| 410 | //:: struct thread *joinee = thread_get(joineeid); |
| 411 | //:: |
| 412 | //:: /* First, check the joinee thread's state */ |
| 413 | //:: if (joinee == NULL) |
| 414 | //:: thread_report(joineeid, THE_NotExist, "joining as joinee"); |
| 415 | //:: else { |
| 416 | //:: switch(joinee->state) { |
| 417 | //:: case TS_Alive: /* really shouldn't see them in this state... */ |
| 418 | //:: case TS_Running: |
| 419 | //:: case TS_Zombie: |
| 420 | //:: case TS_MutexBlocked: |
| 421 | //:: case TS_CVBlocked: |
| 422 | //:: case TS_JoinBlocked: |
| 423 | //:: /* OK */ |
| 424 | //:: break; |
| 425 | //:: |
| 426 | //:: case TS_Dead: |
| 427 | //:: thread_report(joineeid, THE_NotAlive, "joining as joinee"); |
| 428 | //:: break; |
| 429 | //:: } |
| 430 | //:: } |
| 431 | //:: |
| 432 | //:: /* now the joiner... */ |
| 433 | //:: if (joiner == NULL) |
| 434 | //:: thread_report(joineeid, THE_NotExist, "joining as joiner"); |
| 435 | //:: else { |
| 436 | //:: switch(joiner->state) { |
| 437 | //:: case TS_Alive: /* ? */ |
| 438 | //:: case TS_Running: /* OK */ |
| 439 | //:: break; |
| 440 | //:: |
| 441 | //:: case TS_Zombie: /* back from the dead */ |
| 442 | //:: case TS_Dead: |
| 443 | //:: thread_report(joineeid, THE_NotAlive, "joining as joiner"); |
| 444 | //:: break; |
| 445 | //:: |
| 446 | //:: case TS_MutexBlocked: |
| 447 | //:: case TS_CVBlocked: |
| 448 | //:: case TS_JoinBlocked: |
| 449 | //:: thread_report(joineeid, THE_Blocked, "joining as joiner"); |
| 450 | //:: break; |
| 451 | //:: } |
| 452 | //:: |
| 453 | //:: if (joinee->detached) |
| 454 | //:: thread_report(joineeid, THE_Detached, "joining as joiner"); |
| 455 | //:: else { |
| 456 | //:: /* block if the joinee hasn't exited yet */ |
| 457 | //:: if (joinee) { |
| 458 | //:: switch(joinee->state) { |
| 459 | //:: case TS_Dead: |
| 460 | //:: break; |
| 461 | //:: |
| 462 | //:: default: |
| 463 | //:: if (joinee->state == TS_Zombie) |
| 464 | //:: do_thread_dead(joinee); |
| 465 | //:: else |
| 466 | //:: do_thread_block_join(joiner, joinee); |
| 467 | //:: } |
| 468 | //:: } |
| 469 | //:: } |
| 470 | //:: } |
| 471 | //:: } |
| 472 | //:: |
| 473 | //:: /* Context switch to a new thread */ |
| 474 | //:: void VG_(tm_thread_switchto)(ThreadId tid) |
| 475 | //:: { |
| 476 | //:: VG_TRACK( thread_run, tid ); |
| 477 | //:: } |
| 478 | //:: |
| 479 | //:: static void thread_block_mutex(ThreadId tid, struct mutex *mx) |
| 480 | //:: { |
| 481 | //:: struct thread *th = thread_get(tid); |
| 482 | //:: |
| 483 | //:: if (th == NULL) { |
| 484 | //:: /* should an unknown thread doing something make it spring to life? */ |
| 485 | //:: thread_report(tid, THE_NotExist, "blocking on mutex"); |
| 486 | //:: return; |
| 487 | //:: } |
| 488 | //:: switch(th->state) { |
| 489 | //:: case TS_Dead: |
| 490 | //:: case TS_Zombie: |
| 491 | //:: thread_report(th->tid, THE_NotAlive, "blocking on mutex"); |
| 492 | //:: break; |
| 493 | //:: |
| 494 | //:: case TS_MutexBlocked: |
| 495 | //:: case TS_CVBlocked: |
| 496 | //:: case TS_JoinBlocked: |
| 497 | //:: thread_report(th->tid, THE_Blocked, "blocking on mutex"); |
| 498 | //:: break; |
| 499 | //:: |
| 500 | //:: case TS_Alive: |
| 501 | //:: case TS_Running: /* OK */ |
| 502 | //:: break; |
| 503 | //:: } |
| 504 | //:: |
| 505 | //:: do_thread_block_mutex(th, mx); |
| 506 | //:: } |
| 507 | //:: |
| 508 | //:: static void thread_unblock_mutex(ThreadId tid, struct mutex *mx, const Char *action) |
| 509 | //:: { |
| 510 | //:: struct thread *th = thread_get(tid); |
| 511 | //:: |
| 512 | //:: if (th == NULL) { |
| 513 | //:: /* should an unknown thread doing something make it spring to life? */ |
| 514 | //:: thread_report(tid, THE_NotExist, "giving up on mutex"); |
| 515 | //:: return; |
| 516 | //:: } |
| 517 | //:: |
| 518 | //:: switch(th->state) { |
| 519 | //:: case TS_MutexBlocked: /* OK */ |
| 520 | //:: break; |
| 521 | //:: |
| 522 | //:: case TS_Alive: |
| 523 | //:: case TS_Running: |
| 524 | //:: thread_report(tid, THE_NotBlocked, action); |
| 525 | //:: break; |
| 526 | //:: |
| 527 | //:: case TS_CVBlocked: |
| 528 | //:: case TS_JoinBlocked: |
| 529 | //:: thread_report(tid, THE_Blocked, action); |
| 530 | //:: break; |
| 531 | //:: |
| 532 | //:: case TS_Dead: |
| 533 | //:: case TS_Zombie: |
| 534 | //:: thread_report(tid, THE_NotAlive, action); |
| 535 | //:: break; |
| 536 | //:: } |
| 537 | //:: |
| 538 | //:: do_thread_run(th); |
| 539 | //:: } |
| 540 | //:: |
| 541 | //:: /* -------------------------------------------------- |
| 542 | //:: Mutexes |
| 543 | //:: |
| 544 | //:: This models simple, non-recursive mutexes. |
| 545 | //:: -------------------------------------------------- */ |
| 546 | //:: |
| 547 | //:: struct mutex |
| 548 | //:: { |
| 549 | //:: Addr mutex; /* address of mutex */ |
| 550 | //:: ThreadId owner; /* owner if state == MX_Locked */ |
| 551 | //:: enum mutex_state { |
| 552 | //:: MX_Init, |
| 553 | //:: MX_Free, |
| 554 | //:: MX_Locked, |
| 555 | //:: MX_Unlocking, /* half-unlocked */ |
| 556 | //:: MX_Dead |
| 557 | //:: } state; /* mutex state */ |
| 558 | //:: |
| 559 | //:: ExeContext *ec_create; /* where created/destroyed */ |
| 560 | //:: ExeContext *ec_locked; /* where last locked/unlocked */ |
| 561 | //:: }; |
| 562 | //:: |
| 563 | //:: enum mutex_error |
| 564 | //:: { |
| 565 | //:: MXE_NotExist, /* never existed */ |
| 566 | //:: MXE_NotInit, /* not initialized (use after destroy) */ |
| 567 | //:: MXE_ReInit, /* already initialized */ |
| 568 | //:: MXE_NotLocked, /* not locked */ |
| 569 | //:: MXE_Locked, /* is locked */ |
| 570 | //:: MXE_Deadlock, /* deadlock detected */ |
| 571 | //:: MXE_NotOwner, /* non-owner trying to change lock */ |
| 572 | //:: }; |
| 573 | //:: |
| 574 | //:: struct mutex_error_data |
| 575 | //:: { |
| 576 | //:: enum mutex_error err; |
| 577 | //:: struct mutex *mx; |
| 578 | //:: const Char *action; |
| 579 | //:: }; |
| 580 | //:: |
| 581 | //:: static struct mutex *mutex_get(Addr mutexp); |
| 582 | //:: |
| 583 | //:: static const Char *pp_mutexstate(const struct mutex *mx) |
| 584 | //:: { |
| 585 | //:: static Char buf[20]; |
| 586 | //:: |
| 587 | //:: switch(mx->state) { |
| 588 | //:: case MX_Init: return "Init"; |
| 589 | //:: case MX_Free: return "Free"; |
| 590 | //:: case MX_Dead: return "Dead"; |
| 591 | //:: |
| 592 | //:: case MX_Locked: |
| 593 | //:: VG_(sprintf)(buf, "Locked by tid %d", mx->owner); |
| 594 | //:: break; |
| 595 | //:: |
| 596 | //:: case MX_Unlocking: |
| 597 | //:: VG_(sprintf)(buf, "Being unlocked by tid %d", mx->owner); |
| 598 | //:: break; |
| 599 | //:: |
| 600 | //:: default: |
| 601 | //:: VG_(sprintf)(buf, "?? %d", mx->state); |
| 602 | //:: break; |
| 603 | //:: } |
| 604 | //:: |
| 605 | //:: return buf; |
| 606 | //:: } |
| 607 | //:: |
| 608 | //:: static void mutex_setstate(ThreadId tid, struct mutex *mx, enum mutex_state st) |
| 609 | //:: { |
| 610 | //:: ExeContext *ec = VG_(get_ExeContext)(tid); |
| 611 | //:: |
| 612 | //:: switch(st) { |
| 613 | //:: case MX_Init: |
| 614 | //:: case MX_Dead: |
| 615 | //:: mx->ec_create = ec; |
| 616 | //:: break; |
| 617 | //:: |
| 618 | //:: case MX_Unlocking: |
| 619 | //:: case MX_Locked: |
| 620 | //:: case MX_Free: |
| 621 | //:: mx->ec_locked = ec; |
| 622 | //:: break; |
| 623 | //:: } |
| 624 | //:: |
| 625 | //:: mx->state = st; |
| 626 | //:: if (debug_mutex) |
| 627 | //:: VG_(printf)("setting mutex(%p) -> %s\n", mx->mutex, pp_mutexstate(mx)); |
| 628 | //:: } |
| 629 | //:: |
| 630 | //:: static void mutex_report(ThreadId tid, Addr mutexp, enum mutex_error err, const Char *action) |
| 631 | //:: { |
| 632 | //:: Char *errstr="?"; |
| 633 | //:: struct mutex *mx = mutex_get(mutexp); |
| 634 | //:: struct mutex_error_data errdata; |
| 635 | //:: |
| 636 | //:: switch(err) { |
| 637 | //:: case MXE_NotExist: errstr="non-existent"; break; |
| 638 | //:: case MXE_NotInit: errstr="uninitialized"; break; |
| 639 | //:: case MXE_ReInit: errstr="already initialized"; break; |
| 640 | //:: case MXE_NotLocked: errstr="not locked"; break; |
| 641 | //:: case MXE_Locked: errstr="locked"; break; |
| 642 | //:: case MXE_NotOwner: errstr="unowned"; break; |
| 643 | //:: case MXE_Deadlock: errstr="deadlock on"; break; |
| 644 | //:: } |
| 645 | //:: |
| 646 | //:: errdata.err = err; |
| 647 | //:: errdata.mx = mx; |
| 648 | //:: errdata.action = action; |
| 649 | //:: |
| 650 | //:: VG_(maybe_record_error)(tid, MutexErr, 0, errstr, &errdata); |
| 651 | //:: } |
| 652 | //:: |
| 653 | //:: static void pp_mutex_error(Error *err) |
| 654 | //:: { |
| 655 | //:: struct mutex_error_data *errdata = VG_(get_error_extra)(err); |
| 656 | //:: struct mutex *mx = errdata->mx; |
| 657 | //:: Char *errstr = VG_(get_error_string)(err); |
| 658 | //:: |
| 659 | //:: VG_(message)(Vg_UserMsg, "Found %s mutex %p while %s", |
| 660 | //:: errstr, mx ? mx->mutex : 0, errdata->action); |
| 661 | //:: VG_(pp_ExeContext)(VG_(get_error_where)(err)); |
| 662 | //:: |
| 663 | //:: switch (mx->state) { |
| 664 | //:: case MX_Init: |
| 665 | //:: case MX_Dead: |
| 666 | //:: break; |
| 667 | //:: case MX_Locked: |
| 668 | //:: VG_(message)(Vg_UserMsg, " Mutex was locked by thread %d", mx->owner); |
| 669 | //:: VG_(pp_ExeContext)(mx->ec_locked); |
| 670 | //:: break; |
| 671 | //:: case MX_Unlocking: |
| 672 | //:: VG_(message)(Vg_UserMsg, " Mutex being unlocked"); |
| 673 | //:: VG_(pp_ExeContext)(mx->ec_locked); |
| 674 | //:: break; |
| 675 | //:: case MX_Free: |
| 676 | //:: VG_(message)(Vg_UserMsg, " Mutex was unlocked"); |
| 677 | //:: VG_(pp_ExeContext)(mx->ec_locked); |
| 678 | //:: break; |
| 679 | //:: } |
| 680 | //:: |
| 681 | //:: VG_(message)(Vg_UserMsg, " Mutex was %s", |
| 682 | //:: mx->state == MX_Dead ? "destroyed" : "created"); |
| 683 | //:: VG_(pp_ExeContext)(mx->ec_create); |
| 684 | //:: } |
| 685 | //:: |
| 686 | //:: static SkipList sk_mutex = SKIPLIST_INIT(struct mutex, mutex, VG_(cmp_Addr), NULL, VG_AR_CORE); |
| 687 | //:: |
| 688 | //:: static struct mutex *mutex_get(Addr mutexp) |
| 689 | //:: { |
| 690 | //:: return VG_(SkipList_Find_Exact)(&sk_mutex, &mutexp); |
| 691 | //:: } |
| 692 | //:: |
| 693 | //:: static Bool mx_is_initialized(Addr mutexp) |
| 694 | //:: { |
| 695 | //:: const struct mutex *mx = mutex_get(mutexp); |
| 696 | //:: |
| 697 | //:: return mx && mx->state != MX_Dead; |
| 698 | //:: } |
| 699 | //:: |
| 700 | //:: static struct mutex *mutex_check_initialized(ThreadId tid, Addr mutexp, const Char *action) |
| 701 | //:: { |
| 702 | //:: struct mutex *mx; |
| 703 | //:: |
| 704 | //:: vg_assert(tid != VG_INVALID_THREADID); |
| 705 | //:: |
| 706 | //:: if (!mx_is_initialized(mutexp)) { |
| 707 | //:: mutex_report(tid, mutexp, MXE_NotInit, action); |
| 708 | //:: VG_(tm_mutex_init)(tid, mutexp); |
| 709 | //:: } |
| 710 | //:: |
| 711 | //:: mx = mutex_get(mutexp); |
| 712 | //:: vg_assert(mx != NULL); |
| 713 | //:: |
| 714 | //:: return mx; |
| 715 | //:: } |
| 716 | //:: |
| 717 | //:: #if 0 |
| 718 | //:: static Bool mx_is_locked(Addr mutexp) |
| 719 | //:: { |
| 720 | //:: const struct mutex *mx = mutex_get(mutexp); |
| 721 | //:: |
| 722 | //:: return mx && (mx->state == MX_Locked); |
| 723 | //:: } |
| 724 | //:: #endif |
| 725 | //:: |
| 726 | //:: /* Mutex at mutexp is initialized. This must be done before any |
| 727 | //:: further mutex operations are OK. Fails if: |
| 728 | //:: - mutexp already exists (and is locked?) |
| 729 | //:: */ |
| 730 | //:: void VG_(tm_mutex_init)(ThreadId tid, Addr mutexp) |
| 731 | //:: { |
| 732 | //:: struct mutex *mx = mutex_get(mutexp); |
| 733 | //:: |
| 734 | //:: if (mx == NULL) { |
| 735 | //:: mx = VG_(SkipNode_Alloc)(&sk_mutex); |
| 736 | //:: mx->mutex = mutexp; |
| 737 | //:: VG_(SkipList_Insert)(&sk_mutex, mx); |
| 738 | //:: } else if (mx->state != MX_Dead) |
| 739 | //:: mutex_report(tid, mutexp, MXE_ReInit, "initializing"); |
| 740 | //:: |
| 741 | //:: mx->owner = VG_INVALID_THREADID; |
| 742 | //:: |
| 743 | //:: mutex_setstate(tid, mx, MX_Init); |
| 744 | //:: mutex_setstate(tid, mx, MX_Free); |
| 745 | //:: } |
| 746 | //:: |
| 747 | //:: Bool VG_(tm_mutex_exists)(Addr mutexp) |
| 748 | //:: { |
| 749 | //:: return mx_is_initialized(mutexp); |
| 750 | //:: } |
| 751 | //:: |
| 752 | //:: /* Mutex is being destroyed. Fails if: |
| 753 | //:: - mutex was not initialized |
| 754 | //:: - mutex is locked (?) |
| 755 | //:: */ |
| 756 | //:: void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp) |
| 757 | //:: { |
| 758 | //:: struct mutex *mx = mutex_get(mutexp); |
| 759 | //:: |
| 760 | //:: if (mx == NULL) |
| 761 | //:: mutex_report(tid, mutexp, MXE_NotExist, "destroying"); |
| 762 | //:: else { |
| 763 | //:: switch(mx->state) { |
| 764 | //:: case MX_Dead: |
| 765 | //:: mutex_report(tid, mutexp, MXE_NotInit, "destroying"); |
| 766 | //:: break; |
| 767 | //:: |
| 768 | //:: case MX_Locked: |
| 769 | //:: case MX_Unlocking: |
| 770 | //:: mutex_report(tid, mutexp, MXE_Locked, "destroying"); |
| 771 | //:: VG_(tm_mutex_unlock)(tid, mutexp); |
| 772 | //:: break; |
| 773 | //:: |
| 774 | //:: case MX_Init: |
| 775 | //:: case MX_Free: |
| 776 | //:: /* OK */ |
| 777 | //:: break; |
| 778 | //:: } |
| 779 | //:: mutex_setstate(tid, mx, MX_Dead); |
| 780 | //:: } |
| 781 | //:: } |
| 782 | //:: |
| 783 | //:: /* A thread attempts to lock a mutex. If "blocking" then the thread |
| 784 | //:: is put into a blocked state until the lock is acquired. Fails if: |
| 785 | //:: - tid is invalid |
| 786 | //:: - mutex has not been initialized |
| 787 | //:: - thread is blocked on another object (?) |
| 788 | //:: - blocking on this mutex could cause a deadlock |
| 789 | //:: (Lock rank detection?) |
| 790 | //:: */ |
| 791 | //:: void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp) |
| 792 | //:: { |
| 793 | //:: struct mutex *mx; |
| 794 | //:: |
| 795 | //:: mx = mutex_check_initialized(tid, mutexp, "trylocking"); |
| 796 | //:: |
| 797 | //:: thread_block_mutex(tid, mx); |
| 798 | //:: |
| 799 | //:: if (mx->state == MX_Locked && mx->owner == tid) /* deadlock */ |
| 800 | //:: mutex_report(tid, mutexp, MXE_Deadlock, "trylocking"); |
| 801 | //:: |
| 802 | //:: VG_TRACK( pre_mutex_lock, tid, (void *)mutexp ); |
| 803 | //:: } |
| 804 | //:: |
| 805 | //:: /* Give up waiting for a mutex. Fails if: |
| 806 | //:: - thread is not currently blocked on the mutex |
| 807 | //:: */ |
| 808 | //:: void VG_(tm_mutex_giveup)(ThreadId tid, Addr mutexp) |
| 809 | //:: { |
| 810 | //:: struct mutex *mx; |
| 811 | //:: |
| 812 | //:: mx = mutex_check_initialized(tid, mutexp, "giving up"); |
| 813 | //:: |
| 814 | //:: thread_unblock_mutex(tid, mx, "giving up on mutex"); |
| 815 | //:: } |
| 816 | //:: |
| 817 | //:: /* A thread acquires a mutex. Fails if: |
| 818 | //:: - thread is not blocked waiting for the mutex |
| 819 | //:: - mutex is held by another thread |
| 820 | //:: */ |
| 821 | //:: void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp) |
| 822 | //:: { |
| 823 | //:: struct mutex *mx; |
| 824 | //:: |
| 825 | //:: mx = mutex_check_initialized(tid, mutexp, "acquiring"); |
| 826 | //:: |
| 827 | //:: switch(mx->state) { |
| 828 | //:: case MX_Unlocking: /* ownership transfer or relock */ |
| 829 | //:: VG_TRACK( post_mutex_unlock, mx->owner, (void *)mutexp ); |
| 830 | //:: if (mx->owner != tid) |
| 831 | //:: thread_unblock_mutex(tid, mx, "acquiring mutex"); |
| 832 | //:: break; |
| 833 | //:: |
| 834 | //:: case MX_Free: |
| 835 | //:: thread_unblock_mutex(tid, mx, "acquiring mutex"); |
| 836 | //:: break; |
| 837 | //:: |
| 838 | //:: case MX_Locked: |
| 839 | //:: if (debug_mutex) |
| 840 | //:: VG_(printf)("mutex=%p mx->state=%s\n", mutexp, pp_mutexstate(mx)); |
| 841 | //:: VG_TRACK( post_mutex_unlock, mx->owner, (void *)mutexp ); |
| 842 | //:: mutex_report(tid, mutexp, MXE_Locked, "acquiring"); |
| 843 | //:: thread_unblock_mutex(tid, mx, "acquiring mutex"); |
| 844 | //:: break; |
| 845 | //:: |
| 846 | //:: case MX_Init: |
| 847 | //:: case MX_Dead: |
| 848 | //:: vg_assert(0); |
| 849 | //:: } |
| 850 | //:: |
| 851 | //:: mx->owner = tid; |
| 852 | //:: mutex_setstate(tid, mx, MX_Locked); |
| 853 | //:: |
| 854 | //:: VG_TRACK( post_mutex_lock, tid, (void *)mutexp ); |
| 855 | //:: } |
| 856 | //:: |
| 857 | //:: /* Try unlocking a lock. This will move it into a state where it can |
| 858 | //:: either be unlocked, or change ownership to another thread. If |
| 859 | //:: unlock fails, it will remain locked. */ |
| 860 | //:: void VG_(tm_mutex_tryunlock)(ThreadId tid, Addr mutexp) |
| 861 | //:: { |
| 862 | //:: struct thread *th; |
| 863 | //:: struct mutex *mx; |
| 864 | //:: |
| 865 | //:: mx = mutex_check_initialized(tid, mutexp, "try-unlocking"); |
| 866 | //:: |
| 867 | //:: th = thread_get(tid); |
| 868 | //:: |
| 869 | //:: if (th == NULL) |
| 870 | //:: thread_report(tid, THE_NotExist, "try-unlocking mutex"); |
| 871 | //:: else { |
| 872 | //:: switch(th->state) { |
| 873 | //:: case TS_Alive: |
| 874 | //:: case TS_Running: /* OK */ |
| 875 | //:: break; |
| 876 | //:: |
| 877 | //:: case TS_Dead: |
| 878 | //:: case TS_Zombie: |
| 879 | //:: thread_report(tid, THE_NotAlive, "try-unlocking mutex"); |
| 880 | //:: break; |
| 881 | //:: |
| 882 | //:: case TS_JoinBlocked: |
| 883 | //:: case TS_CVBlocked: |
| 884 | //:: case TS_MutexBlocked: |
| 885 | //:: thread_report(tid, THE_Blocked, "try-unlocking mutex"); |
| 886 | //:: do_thread_run(th); |
| 887 | //:: break; |
| 888 | //:: } |
| 889 | //:: } |
| 890 | //:: |
| 891 | //:: switch(mx->state) { |
| 892 | //:: case MX_Locked: |
| 893 | //:: if (mx->owner != tid) |
| 894 | //:: mutex_report(tid, mutexp, MXE_NotOwner, "try-unlocking"); |
| 895 | //:: break; |
| 896 | //:: |
| 897 | //:: case MX_Free: |
| 898 | //:: mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking"); |
| 899 | //:: break; |
| 900 | //:: |
| 901 | //:: case MX_Unlocking: |
| 902 | //:: mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking"); |
| 903 | //:: break; |
| 904 | //:: |
| 905 | //:: case MX_Init: |
| 906 | //:: case MX_Dead: |
| 907 | //:: vg_assert(0); |
| 908 | //:: } |
| 909 | //:: |
| 910 | //:: mutex_setstate(tid, mx, MX_Unlocking); |
| 911 | //:: } |
| 912 | //:: |
| 913 | //:: /* Finish unlocking a Mutex. The mutex can validly be in one of three |
| 914 | //:: states: |
| 915 | //:: - Unlocking |
| 916 | //:: - Locked, owned by someone else (someone else got it in the meantime) |
| 917 | //:: - Free (someone else completed a lock-unlock cycle) |
| 918 | //:: */ |
| 919 | //:: void VG_(tm_mutex_unlock)(ThreadId tid, Addr mutexp) |
| 920 | //:: { |
| 921 | //:: struct mutex *mx; |
| 922 | //:: struct thread *th; |
| 923 | //:: |
| 924 | //:: mx = mutex_check_initialized(tid, mutexp, "unlocking mutex"); |
| 925 | //:: |
| 926 | //:: th = thread_get(tid); |
| 927 | //:: |
| 928 | //:: if (th == NULL) |
| 929 | //:: thread_report(tid, THE_NotExist, "unlocking mutex"); |
| 930 | //:: else { |
| 931 | //:: switch(th->state) { |
| 932 | //:: case TS_Alive: |
| 933 | //:: case TS_Running: /* OK */ |
| 934 | //:: break; |
| 935 | //:: |
| 936 | //:: case TS_Dead: |
| 937 | //:: case TS_Zombie: |
| 938 | //:: thread_report(tid, THE_NotAlive, "unlocking mutex"); |
| 939 | //:: break; |
| 940 | //:: |
| 941 | //:: case TS_JoinBlocked: |
| 942 | //:: case TS_CVBlocked: |
| 943 | //:: case TS_MutexBlocked: |
| 944 | //:: thread_report(tid, THE_Blocked, "unlocking mutex"); |
| 945 | //:: do_thread_run(th); |
| 946 | //:: break; |
| 947 | //:: } |
| 948 | //:: } |
| 949 | //:: |
| 950 | //:: switch(mx->state) { |
| 951 | //:: case MX_Locked: |
| 952 | //:: /* Someone else might have taken ownership in the meantime */ |
| 953 | //:: if (mx->owner == tid) |
| 954 | //:: mutex_report(tid, mutexp, MXE_Locked, "unlocking"); |
| 955 | //:: break; |
| 956 | //:: |
| 957 | //:: case MX_Free: |
| 958 | //:: /* OK - nothing to do */ |
| 959 | //:: break; |
| 960 | //:: |
| 961 | //:: case MX_Unlocking: |
| 962 | //:: /* OK - we need to complete the unlock */ |
| 963 | //:: VG_TRACK( post_mutex_unlock, tid, (void *)mutexp ); |
| 964 | //:: mutex_setstate(tid, mx, MX_Free); |
| 965 | //:: break; |
| 966 | //:: |
| 967 | //:: case MX_Init: |
| 968 | //:: case MX_Dead: |
| 969 | //:: vg_assert(0); |
| 970 | //:: } |
| 971 | //:: } |
| 972 | //:: |
| 973 | //:: /* -------------------------------------------------- |
| 974 | //:: Condition variables |
| 975 | //:: -------------------------------------------------- */ |
| 976 | //:: |
| 977 | //:: struct condvar_waiter |
| 978 | //:: { |
| 979 | //:: ThreadId waiter; |
| 980 | //:: |
| 981 | //:: struct condvar *condvar; |
| 982 | //:: struct mutex *mutex; |
| 983 | //:: |
| 984 | //:: struct condvar_waiter *next; |
| 985 | //:: }; |
| 986 | //:: |
| 987 | //:: struct condvar |
| 988 | //:: { |
| 989 | //:: Addr condvar; |
| 990 | //:: |
| 991 | //:: enum condvar_state { |
| 992 | //:: CV_Dead, |
| 993 | //:: CV_Alive, |
| 994 | //:: } state; |
| 995 | //:: |
| 996 | //:: struct condvar_waiter *waiters; // XXX skiplist? |
| 997 | //:: |
| 998 | //:: ExeContext *ec_created; // where created |
| 999 | //:: ExeContext *ec_signalled; // where last signalled |
| 1000 | //:: }; |
| 1001 | //:: |
| 1002 | //:: enum condvar_err { |
| 1003 | //:: CVE_NotExist, |
| 1004 | //:: CVE_NotInit, |
| 1005 | //:: CVE_ReInit, |
| 1006 | //:: CVE_Busy, |
| 1007 | //:: CVE_Blocked, |
| 1008 | //:: }; |
| 1009 | //:: |
| 1010 | //:: static SkipList sk_condvar = SKIPLIST_INIT(struct condvar, condvar, VG_(cmp_Addr), |
| 1011 | //:: NULL, VG_AR_CORE); |
| 1012 | //:: |
| 1013 | //:: static struct condvar *condvar_get(Addr condp) |
| 1014 | //:: { |
| 1015 | //:: return VG_(SkipList_Find_Exact)(&sk_condvar, &condp); |
| 1016 | //:: } |
| 1017 | //:: |
| 1018 | //:: static Bool condvar_is_initialized(Addr condp) |
| 1019 | //:: { |
| 1020 | //:: const struct condvar *cv = condvar_get(condp); |
| 1021 | //:: |
| 1022 | //:: return cv && cv->state != CV_Dead; |
| 1023 | //:: } |
| 1024 | //:: |
| 1025 | //:: static void condvar_report(ThreadId tid, Addr condp, enum condvar_err err, const Char *action) |
| 1026 | //:: { |
| 1027 | //:: } |
| 1028 | //:: |
| 1029 | //:: static struct condvar *condvar_check_initialized(ThreadId tid, Addr condp, const Char *action) |
| 1030 | //:: { |
| 1031 | //:: struct condvar *cv; |
| 1032 | //:: vg_assert(tid != VG_INVALID_THREADID); |
| 1033 | //:: |
| 1034 | //:: if (!condvar_is_initialized(condp)) { |
| 1035 | //:: condvar_report(tid, condp, CVE_NotInit, action); |
| 1036 | //:: VG_(tm_cond_init)(tid, condp); |
| 1037 | //:: } |
| 1038 | //:: |
| 1039 | //:: cv = condvar_get(condp); |
| 1040 | //:: vg_assert(cv != NULL); |
| 1041 | //:: |
| 1042 | //:: return cv; |
| 1043 | //:: } |
| 1044 | //:: |
| 1045 | //:: /* Initialize a condition variable. Fails if: |
| 1046 | //:: - condp has already been initialized |
| 1047 | //:: */ |
| 1048 | //:: void VG_(tm_cond_init)(ThreadId tid, Addr condp) |
| 1049 | //:: { |
| 1050 | //:: struct condvar *cv = condvar_get(condp); |
| 1051 | //:: |
| 1052 | //:: if (cv == NULL) { |
| 1053 | //:: cv = VG_(SkipNode_Alloc)(&sk_condvar); |
| 1054 | //:: cv->condvar = condp; |
| 1055 | //:: cv->waiters = NULL; |
| 1056 | //:: VG_(SkipList_Insert)(&sk_condvar, cv); |
| 1057 | //:: } else if (cv->state != CV_Dead) { |
| 1058 | //:: condvar_report(tid, condp, CVE_ReInit, "initializing"); |
| 1059 | //:: /* ? what about existing waiters? */ |
| 1060 | //:: } |
| 1061 | //:: |
| 1062 | //:: cv->state = CV_Alive; |
| 1063 | //:: } |
| 1064 | //:: |
| 1065 | //:: /* Destroy a condition variable. Fails if: |
| 1066 | //:: - condp has not been initialized |
| 1067 | //:: - condp is currently being waited on |
| 1068 | //:: */ |
| 1069 | //:: void VG_(tm_cond_destroy)(ThreadId tid, Addr condp) |
| 1070 | //:: { |
| 1071 | //:: struct condvar *cv = condvar_get(condp); |
| 1072 | //:: |
| 1073 | //:: if (cv == NULL) |
| 1074 | //:: condvar_report(tid, condp, CVE_NotExist, "destroying"); |
| 1075 | //:: else { |
| 1076 | //:: if (cv->state != CV_Alive) |
| 1077 | //:: condvar_report(tid, condp, CVE_NotInit, "destroying"); |
| 1078 | //:: if (cv->waiters != NULL) |
| 1079 | //:: condvar_report(tid, condp, CVE_Busy, "destroying"); |
| 1080 | //:: cv->state = CV_Dead; |
| 1081 | //:: } |
| 1082 | //:: } |
| 1083 | //:: |
| 1084 | //:: static struct condvar_waiter *get_waiter(const struct condvar *cv, ThreadId tid) |
| 1085 | //:: { |
| 1086 | //:: struct condvar_waiter *w; |
| 1087 | //:: |
| 1088 | //:: for(w = cv->waiters; w; w = w->next) |
| 1089 | //:: if (w->waiter == tid) |
| 1090 | //:: return w; |
| 1091 | //:: return NULL; |
| 1092 | //:: } |
| 1093 | //:: |
| 1094 | //:: /* Wait for a condition, putting thread into blocked state. Fails if: |
| 1095 | //:: - condp has not been initialized |
| 1096 | //:: - thread doesn't hold mutexp |
| 1097 | //:: - thread is blocked on some other object |
| 1098 | //:: - thread is already blocked on mutex |
| 1099 | //:: */ |
| 1100 | //:: void VG_(tm_cond_wait)(ThreadId tid, Addr condp, Addr mutexp) |
| 1101 | //:: { |
| 1102 | //:: struct thread *th = thread_get(tid); |
| 1103 | //:: struct mutex *mx; |
| 1104 | //:: struct condvar *cv; |
| 1105 | //:: struct condvar_waiter *waiter; |
| 1106 | //:: |
| 1107 | //:: /* Condvar must exist */ |
| 1108 | //:: cv = condvar_check_initialized(tid, condp, "waiting"); |
| 1109 | //:: |
| 1110 | //:: /* Mutex must exist */ |
| 1111 | //:: mx = mutex_check_initialized(tid, mutexp, "waiting on condvar"); |
| 1112 | //:: |
| 1113 | //:: /* Thread must own mutex */ |
| 1114 | //:: if (mx->state != MX_Locked) { |
| 1115 | //:: mutex_report(tid, mutexp, MXE_NotLocked, "waiting on condvar"); |
| 1116 | //:: VG_(tm_mutex_trylock)(tid, mutexp); |
| 1117 | //:: VG_(tm_mutex_acquire)(tid, mutexp); |
| 1118 | //:: } else if (mx->owner != tid) { |
| 1119 | //:: mutex_report(tid, mutexp, MXE_NotOwner, "waiting on condvar"); |
| 1120 | //:: mx->owner = tid; |
| 1121 | //:: } |
| 1122 | //:: |
| 1123 | //:: /* Thread must not be already waiting for condvar */ |
| 1124 | //:: waiter = get_waiter(cv, tid); |
| 1125 | //:: if (waiter != NULL) |
| 1126 | //:: condvar_report(tid, condp, CVE_Blocked, "waiting"); |
| 1127 | //:: else { |
| 1128 | //:: waiter = VG_(arena_malloc)(VG_AR_CORE, sizeof(*waiter)); |
| 1129 | //:: waiter->condvar = cv; |
| 1130 | //:: waiter->mutex = mx; |
| 1131 | //:: waiter->next = cv->waiters; |
| 1132 | //:: cv->waiters = waiter; |
| 1133 | //:: } |
| 1134 | //:: |
| 1135 | //:: /* Thread is now blocking on condvar */ |
| 1136 | //:: do_thread_block_condvar(th, cv); |
| 1137 | //:: |
| 1138 | //:: /* (half) release mutex */ |
| 1139 | //:: VG_(tm_mutex_tryunlock)(tid, mutexp); |
| 1140 | //:: } |
| 1141 | //:: |
| 1142 | //:: /* Wake from a condition, either because we've been signalled, or |
| 1143 | //:: because of timeout. Fails if: |
| 1144 | //:: - thread is not waiting on condp |
| 1145 | //:: */ |
| 1146 | //:: void VG_(tm_cond_wakeup)(ThreadId tid, Addr condp, Addr mutexp) |
| 1147 | //:: { |
| 1148 | //:: } |
| 1149 | //:: |
| 1150 | //:: /* Signal a condition variable. Fails if: |
| 1151 | //:: - condp has not been initialized |
| 1152 | //:: */ |
| 1153 | //:: void VG_(tm_cond_signal)(ThreadId tid, Addr condp) |
| 1154 | //:: { |
| 1155 | //:: } |
| 1156 | //:: |
| 1157 | //:: /* -------------------------------------------------- |
| 1158 | //:: Error handling |
| 1159 | //:: -------------------------------------------------- */ |
| 1160 | //:: |
| 1161 | //:: UInt VG_(tm_error_update_extra)(Error *err) |
| 1162 | //:: { |
| 1163 | //:: switch (VG_(get_error_kind)(err)) { |
| 1164 | //:: case ThreadErr: { |
| 1165 | //:: struct thread_error_data *errdata = VG_(get_error_extra)(err); |
| 1166 | //:: struct thread *new_th = VG_(arena_malloc)(VG_AR_CORE, sizeof(struct thread)); |
| 1167 | //:: |
| 1168 | //:: VG_(memcpy)(new_th, errdata->th, sizeof(struct thread)); |
| 1169 | //:: |
| 1170 | //:: errdata->th = new_th; |
| 1171 | //:: |
| 1172 | //:: return sizeof(struct thread_error_data); |
| 1173 | //:: } |
| 1174 | //:: |
| 1175 | //:: case MutexErr: { |
| 1176 | //:: struct mutex_error_data *errdata = VG_(get_error_extra)(err); |
| 1177 | //:: struct mutex *new_mx = VG_(arena_malloc)(VG_AR_CORE, sizeof(struct mutex)); |
| 1178 | //:: |
| 1179 | //:: VG_(memcpy)(new_mx, errdata->mx, sizeof(struct mutex)); |
| 1180 | //:: |
| 1181 | //:: errdata->mx = new_mx; |
| 1182 | //:: |
| 1183 | //:: return sizeof(struct mutex_error_data); |
| 1184 | //:: } |
| 1185 | //:: |
| 1186 | //:: default: |
| 1187 | //:: return 0; |
| 1188 | //:: } |
| 1189 | //:: } |
| 1190 | //:: |
| 1191 | //:: Bool VG_(tm_error_equal)(VgRes res, Error *e1, Error *e2) |
| 1192 | //:: { |
| 1193 | //:: /* Guaranteed by calling function */ |
| 1194 | //:: vg_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); |
| 1195 | //:: |
| 1196 | //:: switch (VG_(get_error_kind)(e1)) { |
| 1197 | //:: case ThreadErr: { |
| 1198 | //:: struct thread_error_data *errdata1 = VG_(get_error_extra)(e1); |
| 1199 | //:: struct thread_error_data *errdata2 = VG_(get_error_extra)(e2); |
| 1200 | //:: |
| 1201 | //:: return errdata1->err == errdata2->err; |
| 1202 | //:: } |
| 1203 | //:: |
| 1204 | //:: case MutexErr: { |
| 1205 | //:: struct mutex_error_data *errdata1 = VG_(get_error_extra)(e1); |
| 1206 | //:: struct mutex_error_data *errdata2 = VG_(get_error_extra)(e2); |
| 1207 | //:: |
| 1208 | //:: return errdata1->err == errdata2->err; |
| 1209 | //:: } |
| 1210 | //:: |
| 1211 | //:: default: |
| 1212 | //:: VG_(printf)("Error:\n unknown error code %d\n", |
| 1213 | //:: VG_(get_error_kind)(e1)); |
| 1214 | //:: VG_(core_panic)("unknown error code in VG_(tm_error_equal)"); |
| 1215 | //:: } |
| 1216 | //:: } |
| 1217 | //:: |
| 1218 | //:: void VG_(tm_error_print)(Error *err) |
| 1219 | //:: { |
| 1220 | //:: switch (VG_(get_error_kind)(err)) { |
| 1221 | //:: case ThreadErr: |
| 1222 | //:: pp_thread_error(err); |
| 1223 | //:: break; |
| 1224 | //:: case MutexErr: |
| 1225 | //:: pp_mutex_error(err); |
| 1226 | //:: break; |
| 1227 | //:: } |
| 1228 | //:: } |
| 1229 | //:: |
| 1230 | //:: /* -------------------------------------------------- |
| 1231 | //:: Initialisation |
| 1232 | //:: -------------------------------------------------- */ |
| 1233 | //:: |
| 1234 | //:: void VG_(tm_init)() |
| 1235 | //:: { |
| 1236 | //:: VG_(needs_core_errors)(); |
| 1237 | //:: } |