2 This file is part of drd, a thread error detector.
4 Copyright (C) 2006-2017 Bart Van Assche <bvanassche@acm.org>.
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.
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.
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
21 The GNU General Public License is contained in the file COPYING.
26 #include "pub_tool_basics.h" // Addr, SizeT
27 #include "pub_tool_libcassert.h" // tl_assert()
28 #include "pub_tool_libcbase.h" // VG_(memcpy)
29 #include "pub_tool_libcprint.h" // VG_(printf)
30 #include "pub_tool_mallocfree.h" // VG_(malloc), VG_(free)
33 /* Local function declarations. */
36 void DRD_(vc_reserve
)(VectorClock
* const vc
, const unsigned new_capacity
);
39 /* Function definitions. */
42 * Initialize the memory 'vc' points at as a vector clock with size 'size'.
43 * If the pointer 'vcelem' is not null, it is assumed to be an array with
44 * 'size' elements and it becomes the initial value of the vector clock.
46 void DRD_(vc_init
)(VectorClock
* const vc
,
47 const VCElem
* const vcelem
,
54 DRD_(vc_reserve
)(vc
, size
);
55 tl_assert(size
== 0 || vc
->vc
!= 0);
58 VG_(memcpy
)(vc
->vc
, vcelem
, size
* sizeof(vcelem
[0]));
61 #ifdef ENABLE_DRD_CONSISTENCY_CHECKS
66 /** Reset vc to the empty vector clock. */
67 void DRD_(vc_cleanup
)(VectorClock
* const vc
)
69 DRD_(vc_reserve
)(vc
, 0);
72 /** Copy constructor -- initializes *new. */
73 void DRD_(vc_copy
)(VectorClock
* const new, const VectorClock
* const rhs
)
75 DRD_(vc_init
)(new, rhs
->vc
, rhs
->size
);
78 /** Assignment operator -- *lhs is already a valid vector clock. */
79 void DRD_(vc_assign
)(VectorClock
* const lhs
, const VectorClock
* const rhs
)
81 DRD_(vc_cleanup
)(lhs
);
82 DRD_(vc_copy
)(lhs
, rhs
);
85 /** Increment the clock of thread 'tid' in vector clock 'vc'. */
86 void DRD_(vc_increment
)(VectorClock
* const vc
, DrdThreadId
const tid
)
89 for (i
= 0; i
< vc
->size
; i
++)
91 if (vc
->vc
[i
].threadid
== tid
)
93 typeof(vc
->vc
[i
].count
) const oldcount
= vc
->vc
[i
].count
;
95 // Check for integer overflow.
96 tl_assert(oldcount
< vc
->vc
[i
].count
);
102 * The specified thread ID does not yet exist in the vector clock
106 const VCElem vcelem
= { tid
, 1 };
108 DRD_(vc_init
)(&vc2
, &vcelem
, 1);
109 DRD_(vc_combine
)(vc
, &vc2
);
110 DRD_(vc_cleanup
)(&vc2
);
115 * @return True if vector clocks vc1 and vc2 are ordered, and false otherwise.
116 * Order is as imposed by thread synchronization actions ("happens before").
118 Bool
DRD_(vc_ordered
)(const VectorClock
* const vc1
,
119 const VectorClock
* const vc2
)
121 return DRD_(vc_lte
)(vc1
, vc2
) || DRD_(vc_lte
)(vc2
, vc1
);
124 /** Compute elementwise minimum. */
125 void DRD_(vc_min
)(VectorClock
* const result
, const VectorClock
* const rhs
)
133 DRD_(vc_check
)(result
);
135 /* Next, combine both vector clocks into one. */
137 for (j
= 0; j
< rhs
->size
; j
++)
139 while (i
< result
->size
&& result
->vc
[i
].threadid
< rhs
->vc
[j
].threadid
)
141 /* Thread ID is missing in second vector clock. Clear the count. */
142 result
->vc
[i
].count
= 0;
145 if (i
>= result
->size
)
149 if (result
->vc
[i
].threadid
<= rhs
->vc
[j
].threadid
)
151 /* The thread ID is present in both vector clocks. Compute the */
152 /* minimum of vc[i].count and vc[j].count. */
153 tl_assert(result
->vc
[i
].threadid
== rhs
->vc
[j
].threadid
);
154 if (rhs
->vc
[j
].count
< result
->vc
[i
].count
)
156 result
->vc
[i
].count
= rhs
->vc
[j
].count
;
160 DRD_(vc_check
)(result
);
164 * Compute elementwise maximum.
166 void DRD_(vc_combine
)(VectorClock
* const result
, const VectorClock
* const rhs
)
176 // First count the number of shared thread id's.
179 for (i
= 0; i
< result
->size
; i
++)
181 while (j
< rhs
->size
&& rhs
->vc
[j
].threadid
< result
->vc
[i
].threadid
)
185 if (result
->vc
[i
].threadid
== rhs
->vc
[j
].threadid
)
189 DRD_(vc_check
)(result
);
191 new_size
= result
->size
+ rhs
->size
- shared
;
192 if (new_size
> result
->capacity
)
193 DRD_(vc_reserve
)(result
, new_size
);
195 DRD_(vc_check
)(result
);
197 // Next, combine both vector clocks into one.
199 for (j
= 0; j
< rhs
->size
; j
++)
201 /* First of all, skip those clocks in result->vc[] for which there */
202 /* is no corresponding clock in rhs->vc[]. */
203 while (i
< result
->size
&& result
->vc
[i
].threadid
< rhs
->vc
[j
].threadid
)
207 /* If the end of *result is met, append rhs->vc[j] to *result. */
208 if (i
>= result
->size
)
211 result
->vc
[i
] = rhs
->vc
[j
];
213 /* If clock rhs->vc[j] is not in *result, insert it. */
214 else if (result
->vc
[i
].threadid
> rhs
->vc
[j
].threadid
)
217 for (k
= result
->size
; k
> i
; k
--)
219 result
->vc
[k
] = result
->vc
[k
- 1];
222 result
->vc
[i
] = rhs
->vc
[j
];
224 /* Otherwise, both *result and *rhs have a clock for thread */
225 /* result->vc[i].threadid == rhs->vc[j].threadid. Compute the maximum. */
228 tl_assert(result
->vc
[i
].threadid
== rhs
->vc
[j
].threadid
);
229 if (rhs
->vc
[j
].count
> result
->vc
[i
].count
)
231 result
->vc
[i
].count
= rhs
->vc
[j
].count
;
235 DRD_(vc_check
)(result
);
236 tl_assert(result
->size
== new_size
);
239 /** Print the contents of vector clock 'vc'. */
240 void DRD_(vc_print
)(const VectorClock
* const vc
)
244 if ((str
= DRD_(vc_aprint
)(vc
)) != NULL
)
246 VG_(printf
)("%s", str
);
252 * Print the contents of vector clock 'vc' to a newly allocated string.
253 * The caller must call VG_(free)() on the return value of this function.
255 HChar
* DRD_(vc_aprint
)(const VectorClock
* const vc
)
265 str
= VG_(realloc
)("drd.vc.aprint.1", str
, reserved
);
268 size
+= VG_(snprintf
)(str
, reserved
, "[");
269 for (i
= 0; i
< vc
->size
; i
++)
272 if (VG_(strlen
)(str
) + 32 > reserved
)
275 str
= VG_(realloc
)("drd.vc.aprint.2", str
, reserved
);
279 size
+= VG_(snprintf
)(str
+ size
, reserved
- size
,
280 "%s %u: %u", i
> 0 ? "," : "",
281 vc
->vc
[i
].threadid
, vc
->vc
[i
].count
);
283 size
+= VG_(snprintf
)(str
+ size
, reserved
- size
, " ]");
291 * The function below tests whether the following two conditions are
293 * - size <= capacity.
294 * - Vector clock elements are stored in thread ID order.
296 * If one of these conditions is not met, an assertion failure is triggered.
298 void DRD_(vc_check
)(const VectorClock
* const vc
)
302 tl_assert(vc
->size
<= vc
->capacity
);
304 for (i
= 1; i
< vc
->size
; i
++)
305 tl_assert(vc
->vc
[i
-1].threadid
< vc
->vc
[i
].threadid
);
309 * Change the size of the memory block pointed at by vc->vc.
310 * Changes capacity, but does not change size. If the size of the memory
311 * block is increased, the newly allocated memory is not initialized.
314 void DRD_(vc_reserve
)(VectorClock
* const vc
, const unsigned new_capacity
)
317 tl_assert(vc
->capacity
> VC_PREALLOCATED
319 || vc
->vc
== vc
->preallocated
);
321 if (new_capacity
> vc
->capacity
)
323 if (vc
->vc
&& vc
->capacity
> VC_PREALLOCATED
)
326 && vc
->vc
!= vc
->preallocated
327 && vc
->capacity
> VC_PREALLOCATED
);
328 vc
->vc
= VG_(realloc
)("drd.vc.vr.1",
329 vc
->vc
, new_capacity
* sizeof(vc
->vc
[0]));
331 else if (vc
->vc
&& new_capacity
> VC_PREALLOCATED
)
333 tl_assert((vc
->vc
== 0 || vc
->vc
== vc
->preallocated
)
334 && new_capacity
> VC_PREALLOCATED
335 && vc
->capacity
<= VC_PREALLOCATED
);
336 vc
->vc
= VG_(malloc
)("drd.vc.vr.2",
337 new_capacity
* sizeof(vc
->vc
[0]));
338 VG_(memcpy
)(vc
->vc
, vc
->preallocated
,
339 vc
->capacity
* sizeof(vc
->vc
[0]));
343 tl_assert(vc
->vc
== vc
->preallocated
344 && new_capacity
<= VC_PREALLOCATED
345 && vc
->capacity
<= VC_PREALLOCATED
);
347 else if (new_capacity
> VC_PREALLOCATED
)
349 tl_assert(vc
->vc
== 0
350 && new_capacity
> VC_PREALLOCATED
351 && vc
->capacity
== 0);
352 vc
->vc
= VG_(malloc
)("drd.vc.vr.3",
353 new_capacity
* sizeof(vc
->vc
[0]));
357 tl_assert(vc
->vc
== 0
358 && new_capacity
<= VC_PREALLOCATED
359 && vc
->capacity
== 0);
360 vc
->vc
= vc
->preallocated
;
362 vc
->capacity
= new_capacity
;
364 else if (new_capacity
== 0 && vc
->vc
)
366 if (vc
->capacity
> VC_PREALLOCATED
)
372 tl_assert(new_capacity
== 0 || vc
->vc
!= 0);
373 tl_assert(vc
->capacity
> VC_PREALLOCATED
375 || vc
->vc
== vc
->preallocated
);