drd/tests/swapcontext: Improve the portability of this test further
[valgrind.git] / drd / drd_semaphore.c
blob7984c7630fbd334d34e0024540c48ee246efe087
1 /*
2 This file is part of drd, a thread error detector.
4 Copyright (C) 2006-2020 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, see <http://www.gnu.org/licenses/>.
19 The GNU General Public License is contained in the file COPYING.
23 #include "drd_clientobj.h"
24 #include "drd_error.h"
25 #include "drd_semaphore.h"
26 #include "drd_suppression.h"
27 #include "pub_tool_errormgr.h" // VG_(maybe_record_error)()
28 #include "pub_tool_libcassert.h" // tl_assert()
29 #include "pub_tool_libcprint.h" // VG_(printf)()
30 #include "pub_tool_machine.h" // VG_(get_IP)()
31 #include "pub_tool_mallocfree.h" // VG_(malloc), VG_(free)
32 #include "pub_tool_threadstate.h" // VG_(get_running_tid)()
35 /* Local functions. */
37 static void semaphore_cleanup(struct semaphore_info* p);
40 /* Local variables. */
42 static Bool s_trace_semaphore;
43 static ULong s_semaphore_segment_creation_count;
46 /* Function definitions. */
48 /** Push a segment at the end of the queue 'p->last_sem_post_seg'. */
49 static void drd_segment_push(struct semaphore_info* p, Segment* sg)
51 Word n;
53 tl_assert(sg);
54 n = VG_(addToXA)(p->last_sem_post_seg, &sg);
55 #if 0
56 VG_(message)(Vg_DebugMsg, "0x%lx push: added at position %ld/%ld\n",
57 p->a1, n, VG_(sizeXA)(p->last_sem_post_seg));
58 #endif
59 tl_assert(*(Segment**)VG_(indexXA)(p->last_sem_post_seg, n) == sg);
62 /** Pop a segment from the beginning of the queue 'p->last_sem_post_seg'. */
63 static Segment* drd_segment_pop(struct semaphore_info* p)
65 Word sz;
66 Segment* sg;
68 sz = VG_(sizeXA)(p->last_sem_post_seg);
69 #if 0
70 VG_(message)(Vg_DebugMsg, "0x%lx pop: removed from position %ld/%ld\n",
71 p->a1, sz - 1, sz);
72 #endif
73 sg = 0;
74 if (sz > 0)
76 sg = *(Segment**)VG_(indexXA)(p->last_sem_post_seg, sz - 1);
77 tl_assert(sg);
78 VG_(dropTailXA)(p->last_sem_post_seg, 1);
80 return sg;
83 /** Enable or disable tracing of semaphore actions. */
84 void DRD_(semaphore_set_trace)(const Bool trace_semaphore)
86 s_trace_semaphore = trace_semaphore;
89 /**
90 * Initialize the memory 'p' points at as a semaphore_info structure for the
91 * client semaphore at client address 'semaphore'.
93 static
94 void drd_semaphore_initialize(struct semaphore_info* const p,
95 const Addr semaphore)
97 tl_assert(semaphore != 0);
98 tl_assert(p->a1 == semaphore);
99 tl_assert(p->type == ClientSemaphore);
101 p->cleanup = (void(*)(DrdClientobj*))semaphore_cleanup;
102 p->delete_thread = 0;
103 p->waits_to_skip = 0;
104 p->value = 0;
105 p->waiters = 0;
106 p->last_sem_post_tid = DRD_INVALID_THREADID;
107 p->last_sem_post_seg = VG_(newXA)(VG_(malloc), "drd.sg-stack",
108 VG_(free), sizeof(Segment*));
112 * Free the memory that was allocated by semaphore_initialize(). Called by
113 * DRD_(clientobj_remove)().
115 static void semaphore_cleanup(struct semaphore_info* p)
117 Segment* sg;
119 if (p->waiters > 0)
121 SemaphoreErrInfo sei = { DRD_(thread_get_running_tid)(), p->a1 };
122 VG_(maybe_record_error)(VG_(get_running_tid)(),
123 SemaphoreErr,
124 VG_(get_IP)(VG_(get_running_tid)()),
125 "Destruction of semaphore that is being waited"
126 " upon",
127 &sei);
129 while ((sg = drd_segment_pop(p)))
130 DRD_(sg_put)(sg);
131 VG_(deleteXA)(p->last_sem_post_seg);
135 * Return a pointer to the structure with information about the specified
136 * client semaphore. Allocate a new structure if such a structure did not
137 * yet exist.
139 static
140 struct semaphore_info*
141 drd_semaphore_get_or_allocate(const Addr semaphore)
143 struct semaphore_info *p;
145 tl_assert(offsetof(DrdClientobj, semaphore) == 0);
146 p = &(DRD_(clientobj_get)(semaphore, ClientSemaphore)->semaphore);
147 if (p == 0)
149 tl_assert(offsetof(DrdClientobj, semaphore) == 0);
150 p = &(DRD_(clientobj_add)(semaphore, ClientSemaphore)->semaphore);
151 drd_semaphore_initialize(p, semaphore);
153 return p;
157 * Return a pointer to the structure with information about the specified
158 * client semaphore, or null if no such structure was found.
160 static struct semaphore_info* semaphore_get(const Addr semaphore)
162 tl_assert(offsetof(DrdClientobj, semaphore) == 0);
163 return &(DRD_(clientobj_get)(semaphore, ClientSemaphore)->semaphore);
166 /** Called before sem_init(). */
167 struct semaphore_info* DRD_(semaphore_init)(const Addr semaphore,
168 const Word pshared,
169 const UInt value)
171 struct semaphore_info* p;
172 Segment* sg;
174 if (s_trace_semaphore)
175 DRD_(trace_msg)("[%u] sem_init 0x%lx value %u",
176 DRD_(thread_get_running_tid)(), semaphore, value);
178 p = semaphore_get(semaphore);
179 if (p)
181 const ThreadId vg_tid = VG_(get_running_tid)();
182 SemaphoreErrInfo SEI = { DRD_(thread_get_running_tid)(), semaphore };
183 VG_(maybe_record_error)(vg_tid,
184 SemaphoreErr,
185 VG_(get_IP)(vg_tid),
186 "Semaphore reinitialization",
187 &SEI);
188 // Remove all segments from the segment stack.
189 while ((sg = drd_segment_pop(p)))
191 DRD_(sg_put)(sg);
194 else
196 #if defined(VGO_darwin)
197 const ThreadId vg_tid = VG_(get_running_tid)();
198 GenericErrInfo GEI = { DRD_(thread_get_running_tid)(), 0 };
199 VG_(maybe_record_error)(vg_tid,
200 GenericErr,
201 VG_(get_IP)(vg_tid),
202 "sem_init() is not yet supported on Darwin",
203 &GEI);
204 return NULL;
205 #else
206 p = drd_semaphore_get_or_allocate(semaphore);
207 #endif
209 tl_assert(p);
210 p->waits_to_skip = value;
211 p->value = value;
212 return p;
215 /** Called after sem_destroy(). */
216 void DRD_(semaphore_destroy)(const Addr semaphore)
218 struct semaphore_info* p;
220 p = semaphore_get(semaphore);
222 if (s_trace_semaphore)
223 DRD_(trace_msg)("[%u] sem_destroy 0x%lx value %u",
224 DRD_(thread_get_running_tid)(), semaphore,
225 p ? p->value : 0);
227 if (p == 0)
229 GenericErrInfo GEI = {
230 .tid = DRD_(thread_get_running_tid)(),
231 .addr = semaphore,
233 VG_(maybe_record_error)(VG_(get_running_tid)(),
234 GenericErr,
235 VG_(get_IP)(VG_(get_running_tid)()),
236 "Not a semaphore",
237 &GEI);
238 return;
241 DRD_(clientobj_remove)(semaphore, ClientSemaphore);
244 /** Called after sem_open(). */
245 struct semaphore_info* DRD_(semaphore_open)(const Addr semaphore,
246 const HChar* name, const Word oflag,
247 const Word mode, const UInt value)
249 struct semaphore_info* p;
250 Segment* sg;
252 if (s_trace_semaphore)
253 DRD_(trace_msg)("[%u] sem_open 0x%lx name %s"
254 " oflag %#lx mode %#lo value %u",
255 DRD_(thread_get_running_tid)(),
256 semaphore, name, (UWord)oflag, (UWord)mode, value);
258 /* Return if the sem_open() call failed. */
259 if (! semaphore)
260 return NULL;
262 p = semaphore_get(semaphore);
263 if (p)
265 const ThreadId vg_tid = VG_(get_running_tid)();
266 SemaphoreErrInfo SEI = { DRD_(thread_get_running_tid)(), semaphore };
267 VG_(maybe_record_error)(vg_tid,
268 SemaphoreErr,
269 VG_(get_IP)(vg_tid),
270 "Semaphore reinitialization",
271 &SEI);
272 // Remove all segments from the segment stack.
273 while ((sg = drd_segment_pop(p)))
275 DRD_(sg_put)(sg);
278 else
280 p = drd_semaphore_get_or_allocate(semaphore);
282 tl_assert(p);
283 p->waits_to_skip = value;
284 p->value = value;
285 return p;
288 /** Called before sem_close(). */
289 void DRD_(semaphore_close)(const Addr semaphore)
291 struct semaphore_info* p;
293 p = semaphore_get(semaphore);
295 if (s_trace_semaphore)
296 DRD_(trace_msg)("[%u] sem_close 0x%lx value %u",
297 DRD_(thread_get_running_tid)(), semaphore,
298 p ? p->value : 0);
300 if (p == 0)
302 GenericErrInfo GEI = {
303 .tid = DRD_(thread_get_running_tid)(),
304 .addr = semaphore,
306 VG_(maybe_record_error)(VG_(get_running_tid)(),
307 GenericErr,
308 VG_(get_IP)(VG_(get_running_tid)()),
309 "Not a semaphore",
310 &GEI);
311 return;
314 DRD_(clientobj_remove)(semaphore, ClientSemaphore);
317 /** Called before sem_wait(). */
318 void DRD_(semaphore_pre_wait)(const Addr semaphore)
320 struct semaphore_info* p;
322 tl_assert(semaphore < semaphore + 1);
323 p = drd_semaphore_get_or_allocate(semaphore);
324 tl_assert(p);
325 p->waiters++;
327 if ((Word)(p->waiters) <= 0)
329 SemaphoreErrInfo sei = { DRD_(thread_get_running_tid)(), semaphore };
330 VG_(maybe_record_error)(VG_(get_running_tid)(),
331 SemaphoreErr,
332 VG_(get_IP)(VG_(get_running_tid)()),
333 "Invalid semaphore",
334 &sei);
339 * Called after sem_wait() finished.
340 * @note Some C libraries do not set the 'waited' value correctly.
342 void DRD_(semaphore_post_wait)(const DrdThreadId tid, const Addr semaphore,
343 const Bool waited)
345 struct semaphore_info* p;
346 Segment* sg;
348 tl_assert(waited == 0 || waited == 1);
349 p = semaphore_get(semaphore);
350 if (s_trace_semaphore)
351 DRD_(trace_msg)("[%u] sem_wait 0x%lx value %u -> %u%s",
352 DRD_(thread_get_running_tid)(), semaphore,
353 p ? p->value : 0, p ? p->value - waited : 0,
354 waited ? "" : " (did not wait)");
356 if (p) {
357 p->waiters--;
358 p->value -= waited;
362 * Note: if another thread destroyed and reinitialized a semaphore while
363 * the current thread was waiting in sem_wait, p->waiters may have been
364 * set to zero by drd_semaphore_initialize() after
365 * DRD_(semaphore_pre_wait)() has finished before
366 * DRD_(semaphore_post_wait)() has been called.
368 if (p == NULL || (Int)(p->value) < 0 || (Word)(p->waiters) < 0)
370 SemaphoreErrInfo sei = { DRD_(thread_get_running_tid)(), semaphore };
371 VG_(maybe_record_error)(VG_(get_running_tid)(),
372 SemaphoreErr,
373 VG_(get_IP)(VG_(get_running_tid)()),
374 "Invalid semaphore",
375 &sei);
376 return;
379 if (!waited)
380 return;
382 if (p->waits_to_skip > 0)
383 p->waits_to_skip--;
384 else
386 sg = drd_segment_pop(p);
387 tl_assert(sg);
388 if (p->last_sem_post_tid != tid
389 && p->last_sem_post_tid != DRD_INVALID_THREADID)
391 DRD_(thread_new_segment_and_combine_vc)(tid, sg);
393 else
394 DRD_(thread_new_segment)(tid);
395 s_semaphore_segment_creation_count++;
396 DRD_(sg_put)(sg);
400 /** Called before sem_post(). */
401 void DRD_(semaphore_pre_post)(const DrdThreadId tid, const Addr semaphore)
403 struct semaphore_info* p;
404 Segment* sg;
406 p = drd_semaphore_get_or_allocate(semaphore);
407 p->value++;
409 if (s_trace_semaphore)
410 DRD_(trace_msg)("[%u] sem_post 0x%lx value %u -> %u",
411 DRD_(thread_get_running_tid)(),
412 semaphore, p->value - 1, p->value);
414 p->last_sem_post_tid = tid;
415 sg = 0;
416 DRD_(thread_get_latest_segment)(&sg, tid);
417 tl_assert(sg);
418 drd_segment_push(p, sg);
419 DRD_(thread_new_segment)(tid);
420 s_semaphore_segment_creation_count++;
423 /** Called after sem_post() finished. */
424 void DRD_(semaphore_post_post)(const DrdThreadId tid, const Addr semaphore,
425 const Bool succeeded)
428 * Note: it is hard to implement the sem_post() wrapper correctly in
429 * case sem_post() returns an error code. This is because handling this
430 * case correctly requires restoring the vector clock associated with
431 * the semaphore to its original value here. In order to do that without
432 * introducing a race condition, extra locking has to be added around
433 * each semaphore call. Such extra locking would have to be added in
434 * drd_pthread_intercepts.c. However, it is hard to implement
435 * synchronization in drd_pthread_intercepts.c in a portable way without
436 * calling already redirected functions.
440 ULong DRD_(get_semaphore_segment_creation_count)(void)
442 return s_semaphore_segment_creation_count;