drd_vc.c revision 9eecbbb9a9cbbd30b903c09a9e04d8efc20bda33
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 <bart.vanassche@gmail.com>. 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_vc.h" 27#include "pub_tool_basics.h" // Addr, SizeT 28#include "pub_tool_libcassert.h" // tl_assert() 29#include "pub_tool_libcbase.h" // VG_(memcpy) 30#include "pub_tool_libcprint.h" // VG_(printf) 31#include "pub_tool_mallocfree.h" // VG_(malloc), VG_(free) 32 33 34/* Local function declarations. */ 35 36static 37void DRD_(vc_reserve)(VectorClock* const vc, const unsigned new_capacity); 38 39 40/* Function definitions. */ 41 42/** 43 * Initialize the memory 'vc' points at as a vector clock with size 'size'. 44 * If the pointer 'vcelem' is not null, it is assumed to be an array with 45 * 'size' elements and it becomes the initial value of the vector clock. 46 */ 47void DRD_(vc_init)(VectorClock* const vc, 48 const VCElem* const vcelem, 49 const unsigned size) 50{ 51 tl_assert(vc); 52 vc->size = 0; 53 vc->capacity = 0; 54 vc->vc = 0; 55 DRD_(vc_reserve)(vc, size); 56 tl_assert(size == 0 || vc->vc != 0); 57 if (vcelem) 58 { 59 VG_(memcpy)(vc->vc, vcelem, size * sizeof(vcelem[0])); 60 vc->size = size; 61 } 62} 63 64/** Reset vc to the empty vector clock. */ 65void DRD_(vc_cleanup)(VectorClock* const vc) 66{ 67 DRD_(vc_reserve)(vc, 0); 68} 69 70/** Copy constructor -- initializes *new. */ 71void DRD_(vc_copy)(VectorClock* const new, const VectorClock* const rhs) 72{ 73 DRD_(vc_init)(new, rhs->vc, rhs->size); 74} 75 76/** Assignment operator -- *lhs is already a valid vector clock. */ 77void DRD_(vc_assign)(VectorClock* const lhs, const VectorClock* const rhs) 78{ 79 DRD_(vc_cleanup)(lhs); 80 DRD_(vc_copy)(lhs, rhs); 81} 82 83/** Increment the clock of thread 'tid' in vector clock 'vc'. */ 84void DRD_(vc_increment)(VectorClock* const vc, DrdThreadId const tid) 85{ 86 unsigned i; 87 for (i = 0; i < vc->size; i++) 88 { 89 if (vc->vc[i].threadid == tid) 90 { 91 typeof(vc->vc[i].count) const oldcount = vc->vc[i].count; 92 vc->vc[i].count++; 93 // Check for integer overflow. 94 tl_assert(oldcount < vc->vc[i].count); 95 return; 96 } 97 } 98 99 /* 100 * The specified thread ID does not yet exist in the vector clock 101 * -- insert it. 102 */ 103 { 104 const VCElem vcelem = { tid, 1 }; 105 VectorClock vc2; 106 DRD_(vc_init)(&vc2, &vcelem, 1); 107 DRD_(vc_combine)(vc, &vc2); 108 DRD_(vc_cleanup)(&vc2); 109 } 110} 111 112/** 113 * @return True if vector clocks vc1 and vc2 are ordered, and false otherwise. 114 * Order is as imposed by thread synchronization actions ("happens before"). 115 */ 116Bool DRD_(vc_ordered)(const VectorClock* const vc1, 117 const VectorClock* const vc2) 118{ 119 return DRD_(vc_lte)(vc1, vc2) || DRD_(vc_lte)(vc2, vc1); 120} 121 122/** Compute elementwise minimum. */ 123void DRD_(vc_min)(VectorClock* const result, const VectorClock* const rhs) 124{ 125 unsigned i; 126 unsigned j; 127 128 tl_assert(result); 129 tl_assert(rhs); 130 131 DRD_(vc_check)(result); 132 133 /* Next, combine both vector clocks into one. */ 134 i = 0; 135 for (j = 0; j < rhs->size; j++) 136 { 137 while (i < result->size && result->vc[i].threadid < rhs->vc[j].threadid) 138 { 139 /* Thread ID is missing in second vector clock. Clear the count. */ 140 result->vc[i].count = 0; 141 i++; 142 } 143 if (i >= result->size) 144 { 145 break; 146 } 147 if (result->vc[i].threadid <= rhs->vc[j].threadid) 148 { 149 /* The thread ID is present in both vector clocks. Compute the */ 150 /* minimum of vc[i].count and vc[j].count. */ 151 tl_assert(result->vc[i].threadid == rhs->vc[j].threadid); 152 if (rhs->vc[j].count < result->vc[i].count) 153 { 154 result->vc[i].count = rhs->vc[j].count; 155 } 156 } 157 } 158 DRD_(vc_check)(result); 159} 160 161/** 162 * Compute elementwise maximum. 163 */ 164void DRD_(vc_combine)(VectorClock* const result, const VectorClock* const rhs) 165{ 166 unsigned i; 167 unsigned j; 168 unsigned shared; 169 unsigned new_size; 170 171 tl_assert(result); 172 tl_assert(rhs); 173 174 // First count the number of shared thread id's. 175 j = 0; 176 shared = 0; 177 for (i = 0; i < result->size; i++) 178 { 179 while (j < rhs->size && rhs->vc[j].threadid < result->vc[i].threadid) 180 j++; 181 if (j >= rhs->size) 182 break; 183 if (result->vc[i].threadid == rhs->vc[j].threadid) 184 shared++; 185 } 186 187 DRD_(vc_check)(result); 188 189 new_size = result->size + rhs->size - shared; 190 if (new_size > result->capacity) 191 DRD_(vc_reserve)(result, new_size); 192 193 DRD_(vc_check)(result); 194 195 // Next, combine both vector clocks into one. 196 i = 0; 197 for (j = 0; j < rhs->size; j++) 198 { 199 /* First of all, skip those clocks in result->vc[] for which there */ 200 /* is no corresponding clock in rhs->vc[]. */ 201 while (i < result->size && result->vc[i].threadid < rhs->vc[j].threadid) 202 { 203 i++; 204 } 205 /* If the end of *result is met, append rhs->vc[j] to *result. */ 206 if (i >= result->size) 207 { 208 result->size++; 209 result->vc[i] = rhs->vc[j]; 210 } 211 /* If clock rhs->vc[j] is not in *result, insert it. */ 212 else if (result->vc[i].threadid > rhs->vc[j].threadid) 213 { 214 unsigned k; 215 for (k = result->size; k > i; k--) 216 { 217 result->vc[k] = result->vc[k - 1]; 218 } 219 result->size++; 220 result->vc[i] = rhs->vc[j]; 221 } 222 /* Otherwise, both *result and *rhs have a clock for thread */ 223 /* result->vc[i].threadid == rhs->vc[j].threadid. Compute the maximum. */ 224 else 225 { 226 tl_assert(result->vc[i].threadid == rhs->vc[j].threadid); 227 if (rhs->vc[j].count > result->vc[i].count) 228 { 229 result->vc[i].count = rhs->vc[j].count; 230 } 231 } 232 } 233 DRD_(vc_check)(result); 234 tl_assert(result->size == new_size); 235} 236 237/** Print the contents of vector clock 'vc'. */ 238void DRD_(vc_print)(const VectorClock* const vc) 239{ 240 char* str; 241 242 if ((str = DRD_(vc_aprint)(vc)) != NULL) 243 { 244 VG_(printf)("%s", str); 245 VG_(free)(str); 246 } 247} 248 249/** 250 * Print the contents of vector clock 'vc' to a newly allocated string. 251 * The caller must call VG_(free)() on the return value of this function. 252 */ 253char* DRD_(vc_aprint)(const VectorClock* const vc) 254{ 255 unsigned i; 256 unsigned reserved; 257 unsigned size; 258 char* str = 0; 259 260 tl_assert(vc); 261 reserved = 64; 262 size = 0; 263 str = VG_(realloc)("drd.vc.aprint.1", str, reserved); 264 if (! str) 265 return str; 266 size += VG_(snprintf)(str, reserved, "["); 267 for (i = 0; i < vc->size; i++) 268 { 269 tl_assert(vc->vc); 270 if (VG_(strlen)(str) + 32 > reserved) 271 { 272 reserved *= 2; 273 str = VG_(realloc)("drd.vc.aprint.2", str, reserved); 274 if (! str) 275 return str; 276 } 277 size += VG_(snprintf)(str + size, reserved - size, 278 "%s %d: %d", i > 0 ? "," : "", 279 vc->vc[i].threadid, vc->vc[i].count); 280 } 281 size += VG_(snprintf)(str + size, reserved - size, " ]"); 282 283 return str; 284} 285 286/** 287 * Invariant test. 288 * 289 * The function below tests whether the following two conditions are 290 * satisfied: 291 * - size <= capacity. 292 * - Vector clock elements are stored in thread ID order. 293 * 294 * If one of these conditions is not met, an assertion failure is triggered. 295 */ 296void DRD_(vc_check)(const VectorClock* const vc) 297{ 298 unsigned i; 299 tl_assert(vc->size <= vc->capacity); 300 for (i = 1; i < vc->size; i++) 301 { 302 tl_assert(vc->vc[i-1].threadid < vc->vc[i].threadid); 303 } 304} 305 306/** 307 * Change the size of the memory block pointed at by vc->vc. 308 * Changes capacity, but does not change size. If the size of the memory 309 * block is increased, the newly allocated memory is not initialized. 310 */ 311static 312void DRD_(vc_reserve)(VectorClock* const vc, const unsigned new_capacity) 313{ 314 tl_assert(vc); 315 tl_assert(vc->capacity > VC_PREALLOCATED 316 || vc->vc == 0 317 || vc->vc == vc->preallocated); 318 319 if (new_capacity > vc->capacity) 320 { 321 if (vc->vc && vc->capacity > VC_PREALLOCATED) 322 { 323 tl_assert(vc->vc 324 && vc->vc != vc->preallocated 325 && vc->capacity > VC_PREALLOCATED); 326 vc->vc = VG_(realloc)("drd.vc.vr.1", 327 vc->vc, new_capacity * sizeof(vc->vc[0])); 328 } 329 else if (vc->vc && new_capacity > VC_PREALLOCATED) 330 { 331 tl_assert((vc->vc == 0 || vc->vc == vc->preallocated) 332 && new_capacity > VC_PREALLOCATED 333 && vc->capacity <= VC_PREALLOCATED); 334 vc->vc = VG_(malloc)("drd.vc.vr.2", 335 new_capacity * sizeof(vc->vc[0])); 336 VG_(memcpy)(vc->vc, vc->preallocated, 337 vc->capacity * sizeof(vc->vc[0])); 338 } 339 else if (vc->vc) 340 { 341 tl_assert(vc->vc == vc->preallocated 342 && new_capacity <= VC_PREALLOCATED 343 && vc->capacity <= VC_PREALLOCATED); 344 } 345 else if (new_capacity > VC_PREALLOCATED) 346 { 347 tl_assert(vc->vc == 0 348 && new_capacity > VC_PREALLOCATED 349 && vc->capacity == 0); 350 vc->vc = VG_(malloc)("drd.vc.vr.3", 351 new_capacity * sizeof(vc->vc[0])); 352 } 353 else 354 { 355 tl_assert(vc->vc == 0 356 && new_capacity <= VC_PREALLOCATED 357 && vc->capacity == 0); 358 vc->vc = vc->preallocated; 359 } 360 vc->capacity = new_capacity; 361 } 362 else if (new_capacity == 0 && vc->vc) 363 { 364 if (vc->capacity > VC_PREALLOCATED) 365 VG_(free)(vc->vc); 366 vc->vc = 0; 367 vc->capacity = 0; 368 } 369 370 tl_assert(new_capacity == 0 || vc->vc != 0); 371 tl_assert(vc->capacity > VC_PREALLOCATED 372 || vc->vc == 0 373 || vc->vc == vc->preallocated); 374} 375