Home | History | Annotate | Download | only in drd
      1 /*
      2   This file is part of drd, a thread error detector.
      3 
      4   Copyright (C) 2006-2017 Bart Van Assche <bvanassche (at) acm.org>.
      5 
      6   This program is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU General Public License as
      8   published by the Free Software Foundation; either version 2 of the
      9   License, or (at your option) any later version.
     10 
     11   This program is distributed in the hope that it will be useful, but
     12   WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14   General Public License for more details.
     15 
     16   You should have received a copy of the GNU General Public License
     17   along with this program; if not, write to the Free Software
     18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
     19   02111-1307, USA.
     20 
     21   The GNU General Public License is contained in the file COPYING.
     22 */
     23 
     24 
     25 #include "drd_clientobj.h"
     26 #include "drd_error.h"
     27 #include "drd_rwlock.h"
     28 #include "pub_tool_vki.h"
     29 #include "pub_tool_errormgr.h"    // VG_(maybe_record_error)()
     30 #include "pub_tool_libcassert.h"  // tl_assert()
     31 #include "pub_tool_libcprint.h"   // VG_(message)()
     32 #include "pub_tool_libcproc.h"    // VG_(read_millisecond_timer)()
     33 #include "pub_tool_machine.h"     // VG_(get_IP)()
     34 #include "pub_tool_mallocfree.h"  // VG_(malloc)(), VG_(free)()
     35 #include "pub_tool_threadstate.h" // VG_(get_running_tid)()
     36 
     37 
     38 /* Local type definitions. */
     39 
     40 struct rwlock_thread_info
     41 {
     42    UWord    tid;                 // DrdThreadId.
     43    UInt     reader_nesting_count;
     44    UInt     writer_nesting_count;
     45    // Segment of last unlock call by this thread that unlocked a writer lock.
     46    Segment* latest_wrlocked_segment;
     47    // Segment of last unlock call by this thread that unlocked a reader lock.
     48    Segment* latest_rdlocked_segment;
     49 };
     50 
     51 
     52 /* Local functions. */
     53 
     54 static void rwlock_cleanup(struct rwlock_info* p);
     55 static void rwlock_delete_thread(struct rwlock_info* const p,
     56                                  const DrdThreadId tid);
     57 
     58 
     59 /* Local variables. */
     60 
     61 static Bool DRD_(s_trace_rwlock);
     62 static UInt DRD_(s_exclusive_threshold_ms);
     63 static UInt DRD_(s_shared_threshold_ms);
     64 static ULong DRD_(s_rwlock_segment_creation_count);
     65 
     66 
     67 /* Function definitions. */
     68 
     69 void DRD_(rwlock_set_trace)(const Bool trace_rwlock)
     70 {
     71    tl_assert(trace_rwlock == False || trace_rwlock == True);
     72    DRD_(s_trace_rwlock) = trace_rwlock;
     73 }
     74 
     75 void DRD_(rwlock_set_exclusive_threshold)(const UInt exclusive_threshold_ms)
     76 {
     77    DRD_(s_exclusive_threshold_ms) = exclusive_threshold_ms;
     78 }
     79 
     80 void DRD_(rwlock_set_shared_threshold)(const UInt shared_threshold_ms)
     81 {
     82    DRD_(s_shared_threshold_ms) = shared_threshold_ms;
     83 }
     84 
     85 static Bool DRD_(rwlock_is_rdlocked)(struct rwlock_info* p)
     86 {
     87    struct rwlock_thread_info* q;
     88 
     89    VG_(OSetGen_ResetIter)(p->thread_info);
     90    for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; )
     91    {
     92       return q->reader_nesting_count > 0;
     93    }
     94    return False;
     95 }
     96 
     97 static Bool DRD_(rwlock_is_wrlocked)(struct rwlock_info* p)
     98 {
     99    struct rwlock_thread_info* q;
    100 
    101    VG_(OSetGen_ResetIter)(p->thread_info);
    102    for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; )
    103    {
    104       return q->writer_nesting_count > 0;
    105    }
    106    return False;
    107 }
    108 
    109 static Bool DRD_(rwlock_is_locked)(struct rwlock_info* p)
    110 {
    111    return DRD_(rwlock_is_rdlocked)(p) || DRD_(rwlock_is_wrlocked)(p);
    112 }
    113 
    114 static Bool DRD_(rwlock_is_rdlocked_by)(struct rwlock_info* p,
    115                                         const DrdThreadId tid)
    116 {
    117    const UWord uword_tid = tid;
    118    struct rwlock_thread_info* q;
    119 
    120    q = VG_(OSetGen_Lookup)(p->thread_info, &uword_tid);
    121    return q && q->reader_nesting_count > 0;
    122 }
    123 
    124 static Bool DRD_(rwlock_is_wrlocked_by)(struct rwlock_info* p,
    125                                         const DrdThreadId tid)
    126 {
    127    const UWord uword_tid = tid;
    128    struct rwlock_thread_info* q;
    129 
    130    q = VG_(OSetGen_Lookup)(p->thread_info, &uword_tid);
    131    return q && q->writer_nesting_count > 0;
    132 }
    133 
    134 static Bool DRD_(rwlock_is_locked_by)(struct rwlock_info* p,
    135                                       const DrdThreadId tid)
    136 {
    137    return (DRD_(rwlock_is_rdlocked_by)(p, tid)
    138            || DRD_(rwlock_is_wrlocked_by)(p, tid));
    139 }
    140 
    141 /** Either look up or insert a node corresponding to DRD thread id 'tid'. */
    142 static
    143 struct rwlock_thread_info*
    144 DRD_(lookup_or_insert_node)(OSet* oset, const UWord tid)
    145 {
    146    struct rwlock_thread_info* q;
    147 
    148    q = VG_(OSetGen_Lookup)(oset, &tid);
    149    if (q == 0)
    150    {
    151       q = VG_(OSetGen_AllocNode)(oset, sizeof(*q));
    152       q->tid                       = tid;
    153       q->reader_nesting_count      = 0;
    154       q->writer_nesting_count      = 0;
    155       q->latest_wrlocked_segment   = 0;
    156       q->latest_rdlocked_segment   = 0;
    157       VG_(OSetGen_Insert)(oset, q);
    158    }
    159    tl_assert(q);
    160    return q;
    161 }
    162 
    163 /**
    164  * Combine the vector clock corresponding to the last unlock operation of
    165  * reader-writer lock p into the vector clock of thread 'tid'.
    166  */
    167 static void DRD_(rwlock_combine_other_vc)(struct rwlock_info* const p,
    168                                           const DrdThreadId tid,
    169                                           const Bool readers_too)
    170 {
    171    struct rwlock_thread_info* q;
    172    VectorClock old_vc;
    173 
    174    DRD_(vc_copy)(&old_vc, DRD_(thread_get_vc)(tid));
    175    VG_(OSetGen_ResetIter)(p->thread_info);
    176    for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; ) {
    177       if (q->tid != tid) {
    178          if (q->latest_wrlocked_segment)
    179             DRD_(vc_combine)(DRD_(thread_get_vc)(tid),
    180                              &q->latest_wrlocked_segment->vc);
    181          if (readers_too && q->latest_rdlocked_segment)
    182             DRD_(vc_combine)(DRD_(thread_get_vc)(tid),
    183                              &q->latest_rdlocked_segment->vc);
    184       }
    185    }
    186    DRD_(thread_update_conflict_set)(tid, &old_vc);
    187    DRD_(vc_cleanup)(&old_vc);
    188 }
    189 
    190 /**
    191  * Compare the type of the rwlock specified at initialization time with
    192  * the type passed as an argument, and complain if these two types do not
    193  * match.
    194  */
    195 static Bool drd_rwlock_check_type(struct rwlock_info* const p,
    196                                   const RwLockT rwlock_type)
    197 {
    198    tl_assert(p);
    199    /* The code below has to be updated if additional rwlock types are added. */
    200    tl_assert(rwlock_type == pthread_rwlock || rwlock_type == user_rwlock);
    201    tl_assert(p->rwlock_type == pthread_rwlock || p->rwlock_type == user_rwlock);
    202 
    203    if (p->rwlock_type == rwlock_type)
    204       return True;
    205 
    206    {
    207       RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
    208       VG_(maybe_record_error)
    209          (VG_(get_running_tid)(),
    210           RwlockErr,
    211           VG_(get_IP)(VG_(get_running_tid)()),
    212           rwlock_type == pthread_rwlock
    213           ? "Attempt to use a user-defined rwlock as a POSIX rwlock"
    214           : "Attempt to use a POSIX rwlock as a user-defined rwlock",
    215           &REI);
    216    }
    217    return False;
    218 }
    219 
    220 /** Initialize the rwlock_info data structure *p. */
    221 static
    222 void DRD_(rwlock_initialize)(struct rwlock_info* const p, const Addr rwlock,
    223                              const RwLockT rwlock_type)
    224 {
    225    tl_assert(rwlock != 0);
    226    tl_assert(p->a1 == rwlock);
    227    tl_assert(p->type == ClientRwlock);
    228 
    229    p->cleanup         = (void(*)(DrdClientobj*))rwlock_cleanup;
    230    p->delete_thread
    231       = (void(*)(DrdClientobj*, DrdThreadId))rwlock_delete_thread;
    232    p->rwlock_type     = rwlock_type;
    233    p->thread_info     = VG_(OSetGen_Create)(
    234       0, 0, VG_(malloc), "drd.rwlock.ri.1", VG_(free));
    235    p->acquiry_time_ms = 0;
    236    p->acquired_at     = 0;
    237 }
    238 
    239 /** Deallocate the memory that was allocated by rwlock_initialize(). */
    240 static void rwlock_cleanup(struct rwlock_info* p)
    241 {
    242    struct rwlock_thread_info* q;
    243 
    244    tl_assert(p);
    245 
    246    if (DRD_(s_trace_rwlock))
    247       DRD_(trace_msg)("[%u] rwlock_destroy     0x%lx",
    248                       DRD_(thread_get_running_tid)(), p->a1);
    249 
    250    if (DRD_(rwlock_is_locked)(p))
    251    {
    252       RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
    253       VG_(maybe_record_error)(VG_(get_running_tid)(),
    254                               RwlockErr,
    255                               VG_(get_IP)(VG_(get_running_tid)()),
    256                               "Destroying locked rwlock",
    257                               &REI);
    258    }
    259 
    260    VG_(OSetGen_ResetIter)(p->thread_info);
    261    for ( ; (q = VG_(OSetGen_Next)(p->thread_info)) != 0; )
    262    {
    263       DRD_(sg_put)(q->latest_wrlocked_segment);
    264       DRD_(sg_put)(q->latest_rdlocked_segment);
    265    }
    266 
    267    VG_(OSetGen_Destroy)(p->thread_info);
    268 }
    269 
    270 static
    271 struct rwlock_info*
    272 DRD_(rwlock_get_or_allocate)(const Addr rwlock, const RwLockT rwlock_type)
    273 {
    274    struct rwlock_info* p;
    275 
    276    tl_assert(offsetof(DrdClientobj, rwlock) == 0);
    277    p = &(DRD_(clientobj_get)(rwlock, ClientRwlock)->rwlock);
    278    if (p)
    279    {
    280       drd_rwlock_check_type(p, rwlock_type);
    281       return p;
    282    }
    283 
    284    if (DRD_(clientobj_present)(rwlock, rwlock + 1))
    285    {
    286       GenericErrInfo GEI = {
    287 	 .tid  = DRD_(thread_get_running_tid)(),
    288 	 .addr = rwlock,
    289       };
    290       VG_(maybe_record_error)(VG_(get_running_tid)(),
    291                               GenericErr,
    292                               VG_(get_IP)(VG_(get_running_tid)()),
    293                               "Not a reader-writer lock",
    294                               &GEI);
    295       return 0;
    296    }
    297 
    298    p = &(DRD_(clientobj_add)(rwlock, ClientRwlock)->rwlock);
    299    DRD_(rwlock_initialize)(p, rwlock, rwlock_type);
    300    return p;
    301 }
    302 
    303 static struct rwlock_info* DRD_(rwlock_get)(const Addr rwlock)
    304 {
    305    tl_assert(offsetof(DrdClientobj, rwlock) == 0);
    306    return &(DRD_(clientobj_get)(rwlock, ClientRwlock)->rwlock);
    307 }
    308 
    309 /** Called before pthread_rwlock_init(). */
    310 struct rwlock_info* DRD_(rwlock_pre_init)(const Addr rwlock,
    311                                           const RwLockT rwlock_type)
    312 {
    313    struct rwlock_info* p;
    314 
    315    if (DRD_(s_trace_rwlock))
    316       DRD_(trace_msg)("[%u] rwlock_init        0x%lx",
    317                       DRD_(thread_get_running_tid)(), rwlock);
    318 
    319    p = DRD_(rwlock_get)(rwlock);
    320 
    321    if (p)
    322 	drd_rwlock_check_type(p, rwlock_type);
    323 
    324    if (p)
    325    {
    326       const ThreadId vg_tid = VG_(get_running_tid)();
    327       RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
    328       VG_(maybe_record_error)(vg_tid,
    329                               RwlockErr,
    330                               VG_(get_IP)(vg_tid),
    331                               "Reader-writer lock reinitialization",
    332                               &REI);
    333       return p;
    334    }
    335 
    336    p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type);
    337 
    338    return p;
    339 }
    340 
    341 /** Called after pthread_rwlock_destroy(). */
    342 void DRD_(rwlock_post_destroy)(const Addr rwlock, const RwLockT rwlock_type)
    343 {
    344    struct rwlock_info* p;
    345 
    346    p = DRD_(rwlock_get)(rwlock);
    347    if (p == 0)
    348    {
    349       GenericErrInfo GEI = {
    350 	 .tid = DRD_(thread_get_running_tid)(),
    351 	 .addr = rwlock,
    352       };
    353       VG_(maybe_record_error)(VG_(get_running_tid)(),
    354                               GenericErr,
    355                               VG_(get_IP)(VG_(get_running_tid)()),
    356                               "Not a reader-writer lock",
    357                               &GEI);
    358       return;
    359    }
    360 
    361    drd_rwlock_check_type(p, rwlock_type);
    362 
    363    DRD_(clientobj_remove)(rwlock, ClientRwlock);
    364 }
    365 
    366 /**
    367  * Called before pthread_rwlock_rdlock() is invoked. If a data structure for
    368  * the client-side object was not yet created, do this now. Also check whether
    369  * an attempt is made to lock recursively a synchronization object that must
    370  * not be locked recursively.
    371  */
    372 void DRD_(rwlock_pre_rdlock)(const Addr rwlock, const RwLockT rwlock_type)
    373 {
    374    struct rwlock_info* p;
    375 
    376    if (DRD_(s_trace_rwlock))
    377       DRD_(trace_msg)("[%u] pre_rwlock_rdlock  0x%lx",
    378                       DRD_(thread_get_running_tid)(), rwlock);
    379 
    380    p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type);
    381    tl_assert(p);
    382 
    383    if (DRD_(rwlock_is_wrlocked_by)(p, DRD_(thread_get_running_tid)())) {
    384       RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
    385       VG_(maybe_record_error)(VG_(get_running_tid)(),
    386                               RwlockErr,
    387                               VG_(get_IP)(VG_(get_running_tid)()),
    388                               "Already locked for writing by calling thread",
    389                               &REI);
    390    }
    391 }
    392 
    393 /**
    394  * Update rwlock_info state when locking the pthread_rwlock_t mutex.
    395  * Note: this function must be called after pthread_rwlock_rdlock() has been
    396  * called, or a race condition is triggered !
    397  */
    398 void DRD_(rwlock_post_rdlock)(const Addr rwlock, const RwLockT rwlock_type,
    399                               const Bool took_lock)
    400 {
    401    const DrdThreadId drd_tid = DRD_(thread_get_running_tid)();
    402    struct rwlock_info* p;
    403    struct rwlock_thread_info* q;
    404 
    405    if (DRD_(s_trace_rwlock))
    406       DRD_(trace_msg)("[%u] post_rwlock_rdlock 0x%lx", drd_tid, rwlock);
    407 
    408    p = DRD_(rwlock_get)(rwlock);
    409 
    410    if (! p || ! took_lock)
    411       return;
    412 
    413    tl_assert(! DRD_(rwlock_is_wrlocked)(p));
    414 
    415    q = DRD_(lookup_or_insert_node)(p->thread_info, drd_tid);
    416    if (++q->reader_nesting_count == 1)
    417    {
    418       DRD_(thread_new_segment)(drd_tid);
    419       DRD_(s_rwlock_segment_creation_count)++;
    420       DRD_(rwlock_combine_other_vc)(p, drd_tid, False);
    421 
    422       p->acquiry_time_ms = VG_(read_millisecond_timer)();
    423       p->acquired_at     = VG_(record_ExeContext)(VG_(get_running_tid)(), 0);
    424    }
    425 }
    426 
    427 /**
    428  * Called before pthread_rwlock_wrlock() is invoked. If a data structure for
    429  * the client-side object was not yet created, do this now. Also check whether
    430  * an attempt is made to lock recursively a synchronization object that must
    431  * not be locked recursively.
    432  */
    433 void DRD_(rwlock_pre_wrlock)(const Addr rwlock, const RwLockT rwlock_type)
    434 {
    435    struct rwlock_info* p;
    436 
    437    p = DRD_(rwlock_get)(rwlock);
    438 
    439    if (DRD_(s_trace_rwlock))
    440       DRD_(trace_msg)("[%u] pre_rwlock_wrlock  0x%lx",
    441                       DRD_(thread_get_running_tid)(), rwlock);
    442 
    443    if (p == 0)
    444       p = DRD_(rwlock_get_or_allocate)(rwlock, rwlock_type);
    445 
    446    tl_assert(p);
    447 
    448    if (DRD_(rwlock_is_wrlocked_by)(p, DRD_(thread_get_running_tid)()))
    449    {
    450       RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
    451       VG_(maybe_record_error)(VG_(get_running_tid)(),
    452                               RwlockErr,
    453                               VG_(get_IP)(VG_(get_running_tid)()),
    454                               "Recursive writer locking not allowed",
    455                               &REI);
    456    }
    457 }
    458 
    459 /**
    460  * Update rwlock_info state when locking the pthread_rwlock_t rwlock.
    461  * Note: this function must be called after pthread_rwlock_wrlock() has
    462  * finished, or a race condition is triggered !
    463  */
    464 void DRD_(rwlock_post_wrlock)(const Addr rwlock, const RwLockT rwlock_type,
    465                               const Bool took_lock)
    466 {
    467    const DrdThreadId drd_tid = DRD_(thread_get_running_tid)();
    468    struct rwlock_info* p;
    469    struct rwlock_thread_info* q;
    470 
    471    p = DRD_(rwlock_get)(rwlock);
    472 
    473    if (DRD_(s_trace_rwlock))
    474       DRD_(trace_msg)("[%u] post_rwlock_wrlock 0x%lx", drd_tid, rwlock);
    475 
    476    if (! p || ! took_lock)
    477       return;
    478 
    479    q = DRD_(lookup_or_insert_node)(p->thread_info,
    480                                    DRD_(thread_get_running_tid)());
    481    tl_assert(q->writer_nesting_count == 0);
    482    q->writer_nesting_count++;
    483    tl_assert(q->writer_nesting_count == 1);
    484    DRD_(thread_new_segment)(drd_tid);
    485    DRD_(s_rwlock_segment_creation_count)++;
    486    DRD_(rwlock_combine_other_vc)(p, drd_tid, True);
    487    p->acquiry_time_ms = VG_(read_millisecond_timer)();
    488    p->acquired_at     = VG_(record_ExeContext)(VG_(get_running_tid)(), 0);
    489 }
    490 
    491 /**
    492  * Update rwlock_info state when unlocking the pthread_rwlock_t rwlock.
    493  *
    494  * @param rwlock Pointer to pthread_rwlock_t data structure in the client space.
    495  *
    496  * @return New value of the rwlock recursion count.
    497  *
    498  * @note This function must be called before pthread_rwlock_unlock() is called,
    499  *   or a race condition is triggered !
    500  */
    501 void DRD_(rwlock_pre_unlock)(const Addr rwlock, const RwLockT rwlock_type)
    502 {
    503    const DrdThreadId drd_tid = DRD_(thread_get_running_tid)();
    504    const ThreadId vg_tid = VG_(get_running_tid)();
    505    struct rwlock_info* p;
    506    struct rwlock_thread_info* q;
    507 
    508    if (DRD_(s_trace_rwlock))
    509       DRD_(trace_msg)("[%u] rwlock_unlock      0x%lx", drd_tid, rwlock);
    510 
    511    p = DRD_(rwlock_get)(rwlock);
    512    if (p == 0)
    513    {
    514       GenericErrInfo GEI = {
    515 	 .tid = DRD_(thread_get_running_tid)(),
    516 	 .addr = rwlock,
    517       };
    518       VG_(maybe_record_error)(VG_(get_running_tid)(),
    519                               GenericErr,
    520                               VG_(get_IP)(VG_(get_running_tid)()),
    521                               "Not a reader-writer lock",
    522                               &GEI);
    523       return;
    524    }
    525 
    526    drd_rwlock_check_type(p, rwlock_type);
    527 
    528    if (! DRD_(rwlock_is_locked_by)(p, drd_tid))
    529    {
    530       RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
    531       VG_(maybe_record_error)(vg_tid,
    532                               RwlockErr,
    533                               VG_(get_IP)(vg_tid),
    534                               "Reader-writer lock not locked by calling thread",
    535                               &REI);
    536       return;
    537    }
    538    q = DRD_(lookup_or_insert_node)(p->thread_info, drd_tid);
    539    tl_assert(q);
    540    if (q->reader_nesting_count > 0)
    541    {
    542       q->reader_nesting_count--;
    543       if (q->reader_nesting_count == 0 && DRD_(s_shared_threshold_ms) > 0)
    544       {
    545          Long held = VG_(read_millisecond_timer)() - p->acquiry_time_ms;
    546          if (held > DRD_(s_shared_threshold_ms))
    547          {
    548             HoldtimeErrInfo HEI
    549                = { DRD_(thread_get_running_tid)(),
    550                    rwlock, p->acquired_at, held, DRD_(s_shared_threshold_ms) };
    551             VG_(maybe_record_error)(vg_tid,
    552                                     HoldtimeErr,
    553                                     VG_(get_IP)(vg_tid),
    554                                     "rwlock",
    555                                     &HEI);
    556          }
    557       }
    558       if (q->reader_nesting_count == 0 && q->writer_nesting_count == 0)
    559       {
    560          /*
    561           * This pthread_rwlock_unlock() call really unlocks the rwlock. Save
    562           * the current vector clock of the thread such that it is available
    563           * when this rwlock is locked again.
    564           */
    565          DRD_(thread_get_latest_segment)(&q->latest_rdlocked_segment, drd_tid);
    566          DRD_(thread_new_segment)(drd_tid);
    567          DRD_(s_rwlock_segment_creation_count)++;
    568       }
    569    }
    570    else if (q->writer_nesting_count > 0)
    571    {
    572       q->writer_nesting_count--;
    573       if (q->writer_nesting_count == 0 && DRD_(s_exclusive_threshold_ms) > 0)
    574       {
    575          Long held = VG_(read_millisecond_timer)() - p->acquiry_time_ms;
    576          if (held > DRD_(s_exclusive_threshold_ms))
    577          {
    578             HoldtimeErrInfo HEI
    579                = { DRD_(thread_get_running_tid)(),
    580                    rwlock, p->acquired_at, held,
    581                    DRD_(s_exclusive_threshold_ms) };
    582             VG_(maybe_record_error)(vg_tid,
    583                                     HoldtimeErr,
    584                                     VG_(get_IP)(vg_tid),
    585                                     "rwlock",
    586                                     &HEI);
    587          }
    588       }
    589       if (q->reader_nesting_count == 0 && q->writer_nesting_count == 0)
    590       {
    591          /*
    592           * This pthread_rwlock_unlock() call really unlocks the rwlock. Save
    593           * the current vector clock of the thread such that it is available
    594           * when this rwlock is locked again.
    595           */
    596          DRD_(thread_get_latest_segment)(&q->latest_wrlocked_segment, drd_tid);
    597          DRD_(thread_new_segment)(drd_tid);
    598          DRD_(s_rwlock_segment_creation_count)++;
    599       }
    600    }
    601    else
    602    {
    603       tl_assert(False);
    604    }
    605 }
    606 
    607 /** Called when thread tid stops to exist. */
    608 static void rwlock_delete_thread(struct rwlock_info* const p,
    609                                  const DrdThreadId tid)
    610 {
    611    struct rwlock_thread_info* q;
    612 
    613    if (DRD_(rwlock_is_locked_by)(p, tid))
    614    {
    615       RwlockErrInfo REI = { DRD_(thread_get_running_tid)(), p->a1 };
    616       VG_(maybe_record_error)(VG_(get_running_tid)(),
    617                               RwlockErr,
    618                               VG_(get_IP)(VG_(get_running_tid)()),
    619                               "Reader-writer lock still locked at thread exit",
    620                               &REI);
    621       q = DRD_(lookup_or_insert_node)(p->thread_info, tid);
    622       q->reader_nesting_count = 0;
    623       q->writer_nesting_count = 0;
    624    }
    625 }
    626 
    627 ULong DRD_(get_rwlock_segment_creation_count)(void)
    628 {
    629    return DRD_(s_rwlock_segment_creation_count);
    630 }
    631