VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/semxroads-generic.cpp@ 106061

Last change on this file since 106061 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.4 KB
Line 
1/* $Id: semxroads-generic.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT Testcase - RTSemXRoads, generic implementation.
4 */
5
6/*
7 * Copyright (C) 2009-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define RTASSERT_QUIET
42#include <iprt/semaphore.h>
43#include "internal/iprt.h"
44
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/err.h>
48#include <iprt/mem.h>
49#include <iprt/thread.h>
50
51#include "internal/magics.h"
52
53
54/*********************************************************************************************************************************
55* Structures and Typedefs *
56*********************************************************************************************************************************/
57typedef struct RTSEMXROADSINTERNAL
58{
59 /** Magic value (RTSEMXROADS_MAGIC). */
60 uint32_t volatile u32Magic;
61 uint32_t u32Padding; /**< alignment padding.*/
62 /* The state variable.
63 * All accesses are atomic and it bits are defined like this:
64 * Bits 0..14 - cNorthSouth.
65 * Bit 15 - Unused.
66 * Bits 16..31 - cEastWest.
67 * Bit 31 - fDirection; 0=NS, 1=EW.
68 * Bits 32..46 - cWaitingNS
69 * Bit 47 - Unused.
70 * Bits 48..62 - cWaitingEW
71 * Bit 63 - Unused.
72 */
73 uint64_t volatile u64State;
74 /** Per-direction data. */
75 struct
76 {
77 /** What the north/south bound threads are blocking on when waiting for
78 * east/west traffic to stop. */
79 RTSEMEVENTMULTI hEvt;
80 /** Indicates whether the semaphore needs resetting. */
81 bool volatile fNeedReset;
82 } aDirs[2];
83} RTSEMXROADSINTERNAL;
84
85
86/*********************************************************************************************************************************
87* Defined Constants And Macros *
88*********************************************************************************************************************************/
89#define RTSEMXROADS_CNT_BITS 15
90#define RTSEMXROADS_CNT_MASK UINT64_C(0x00007fff)
91
92#define RTSEMXROADS_CNT_NS_SHIFT 0
93#define RTSEMXROADS_CNT_NS_MASK (RTSEMXROADS_CNT_MASK << RTSEMXROADS_CNT_NS_SHIFT)
94#define RTSEMXROADS_CNT_EW_SHIFT 16
95#define RTSEMXROADS_CNT_EW_MASK (RTSEMXROADS_CNT_MASK << RTSEMXROADS_CNT_EW_SHIFT)
96#define RTSEMXROADS_DIR_SHIFT 31
97#define RTSEMXROADS_DIR_MASK RT_BIT_64(RTSEMXROADS_DIR_SHIFT)
98
99#define RTSEMXROADS_WAIT_CNT_NS_SHIFT 32
100#define RTSEMXROADS_WAIT_CNT_NS_MASK (RTSEMXROADS_CNT_MASK << RTSEMXROADS_WAIT_CNT_NS_SHIFT)
101#define RTSEMXROADS_WAIT_CNT_EW_SHIFT 48
102#define RTSEMXROADS_WAIT_CNT_EW_MASK (RTSEMXROADS_CNT_MASK << RTSEMXROADS_WAIT_CNT_EW_SHIFT)
103
104
105#if 0 /* debugging aid */
106static uint32_t volatile g_iHist = 0;
107static struct
108{
109 void *tsc;
110 RTTHREAD hThread;
111 uint32_t line;
112 bool fDir;
113 void *u64State;
114 void *u64OldState;
115 bool fNeedResetNS;
116 bool fNeedResetEW;
117 const char *psz;
118} g_aHist[256];
119
120# define add_hist(ns, os, dir, what) \
121 do \
122 { \
123 uint32_t i = (ASMAtomicIncU32(&g_iHist) - 1) % RT_ELEMENTS(g_aHist);\
124 g_aHist[i].line = __LINE__; \
125 g_aHist[i].u64OldState = (void *)(os); \
126 g_aHist[i].u64State = (void *)(ns); \
127 g_aHist[i].fDir = (dir); \
128 g_aHist[i].psz = (what); \
129 g_aHist[i].fNeedResetNS = pThis->aDirs[0].fNeedReset; \
130 g_aHist[i].fNeedResetEW = pThis->aDirs[1].fNeedReset; \
131 g_aHist[i].hThread = RTThreadSelf(); \
132 g_aHist[i].tsc = (void *)ASMReadTSC(); \
133 } while (0)
134
135# undef DECL_FORCE_INLINE
136# define DECL_FORCE_INLINE(type) static type
137#else
138# define add_hist(ns, os, dir, what) do { } while (0)
139#endif
140
141
142RTDECL(int) RTSemXRoadsCreate(PRTSEMXROADS phXRoads)
143{
144 RTSEMXROADSINTERNAL *pThis = (RTSEMXROADSINTERNAL *)RTMemAlloc(sizeof(*pThis));
145 if (!pThis)
146 return VERR_NO_MEMORY;
147
148 int rc = RTSemEventMultiCreate(&pThis->aDirs[0].hEvt);
149 if (RT_SUCCESS(rc))
150 {
151 rc = RTSemEventMultiCreate(&pThis->aDirs[1].hEvt);
152 if (RT_SUCCESS(rc))
153 {
154 pThis->u32Magic = RTSEMXROADS_MAGIC;
155 pThis->u32Padding = 0;
156 pThis->u64State = 0;
157 pThis->aDirs[0].fNeedReset = false;
158 pThis->aDirs[1].fNeedReset = false;
159 *phXRoads = pThis;
160 return VINF_SUCCESS;
161 }
162 RTSemEventMultiDestroy(pThis->aDirs[0].hEvt);
163 }
164 return rc;
165}
166
167
168RTDECL(int) RTSemXRoadsDestroy(RTSEMXROADS hXRoads)
169{
170 /*
171 * Validate input.
172 */
173 RTSEMXROADSINTERNAL *pThis = hXRoads;
174 if (pThis == NIL_RTSEMXROADS)
175 return VINF_SUCCESS;
176 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
177 AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
178 Assert(!(ASMAtomicReadU64(&pThis->u64State) & (RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK)));
179
180 /*
181 * Invalidate the object and free up the resources.
182 */
183 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMXROADS_MAGIC_DEAD, RTSEMXROADS_MAGIC), VERR_INVALID_HANDLE);
184
185 RTSEMEVENTMULTI hEvt;
186 ASMAtomicXchgHandle(&pThis->aDirs[0].hEvt, NIL_RTSEMEVENTMULTI, &hEvt);
187 int rc = RTSemEventMultiDestroy(hEvt);
188 AssertRC(rc);
189
190 ASMAtomicXchgHandle(&pThis->aDirs[1].hEvt, NIL_RTSEMEVENTMULTI, &hEvt);
191 rc = RTSemEventMultiDestroy(hEvt);
192 AssertRC(rc);
193
194 RTMemFree(pThis);
195 return VINF_SUCCESS;
196}
197
198
199/**
200 * Internal worker for RTSemXRoadsNSEnter and RTSemXRoadsEWEnter.
201 *
202 * @returns IPRT status code.
203 * @param pThis The semaphore instance.
204 * @param fDir The direction.
205 * @param uCountShift The shift count for getting the count.
206 * @param fCountMask The mask for getting the count.
207 * @param uWaitCountShift The shift count for getting the wait count.
208 * @param fWaitCountMask The mask for getting the wait count.
209 */
210DECL_FORCE_INLINE(int) rtSemXRoadsEnter(RTSEMXROADSINTERNAL *pThis, uint64_t fDir,
211 uint64_t uCountShift, uint64_t fCountMask,
212 uint64_t uWaitCountShift, uint64_t fWaitCountMask)
213{
214 uint64_t u64OldState;
215 uint64_t u64State;
216
217 u64State = ASMAtomicReadU64(&pThis->u64State);
218 u64OldState = u64State;
219 add_hist(u64State, u64OldState, fDir, "enter");
220
221 for (;;)
222 {
223 if ((u64State & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT))
224 {
225 /* It flows in the right direction, try follow it before it changes. */
226 uint64_t c = (u64State & fCountMask) >> uCountShift;
227 c++;
228 Assert(c < 8*_1K);
229 u64State &= ~fCountMask;
230 u64State |= c << uCountShift;
231 if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
232 {
233 add_hist(u64State, u64OldState, fDir, "enter-simple");
234 break;
235 }
236 }
237 else if ((u64State & (RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK)) == 0)
238 {
239 /* Wrong direction, but we're alone here and can simply try switch the direction. */
240 u64State &= ~(RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK | RTSEMXROADS_DIR_MASK);
241 u64State |= (UINT64_C(1) << uCountShift) | (fDir << RTSEMXROADS_DIR_SHIFT);
242 if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
243 {
244 Assert(!pThis->aDirs[fDir].fNeedReset);
245 add_hist(u64State, u64OldState, fDir, "enter-switch");
246 break;
247 }
248 }
249 else
250 {
251 /* Add ourselves to the queue and wait for the direction to change. */
252 uint64_t c = (u64State & fCountMask) >> uCountShift;
253 c++;
254 Assert(c < RTSEMXROADS_CNT_MASK / 2);
255
256 uint64_t cWait = (u64State & fWaitCountMask) >> uWaitCountShift;
257 cWait++;
258 Assert(cWait <= c);
259 Assert(cWait < RTSEMXROADS_CNT_MASK / 2);
260
261 u64State &= ~(fCountMask | fWaitCountMask);
262 u64State |= (c << uCountShift) | (cWait << uWaitCountShift);
263
264 if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
265 {
266 add_hist(u64State, u64OldState, fDir, "enter-wait");
267 for (uint32_t iLoop = 0; ; iLoop++)
268 {
269 int rc = RTSemEventMultiWait(pThis->aDirs[fDir].hEvt, RT_INDEFINITE_WAIT);
270 AssertRCReturn(rc, rc);
271
272 if (pThis->u32Magic != RTSEMXROADS_MAGIC)
273 return VERR_SEM_DESTROYED;
274
275 Assert(pThis->aDirs[fDir].fNeedReset);
276 u64State = ASMAtomicReadU64(&pThis->u64State);
277 add_hist(u64State, u64OldState, fDir, "enter-wakeup");
278 if ((u64State & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT))
279 break;
280 AssertMsg(iLoop < 1, ("%u\n", iLoop));
281 }
282
283 /* Decrement the wait count and maybe reset the semaphore (if we're last). */
284 for (;;)
285 {
286 u64OldState = u64State;
287
288 cWait = (u64State & fWaitCountMask) >> uWaitCountShift;
289 Assert(cWait > 0);
290 cWait--;
291 u64State &= ~fWaitCountMask;
292 u64State |= cWait << uWaitCountShift;
293
294 if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
295 {
296 if (cWait == 0)
297 {
298 if (ASMAtomicXchgBool(&pThis->aDirs[fDir].fNeedReset, false))
299 {
300 add_hist(u64State, u64OldState, fDir, fDir ? "enter-reset-EW" : "enter-reset-NS");
301 int rc = RTSemEventMultiReset(pThis->aDirs[fDir].hEvt);
302 AssertRCReturn(rc, rc);
303 }
304 else
305 add_hist(u64State, u64OldState, fDir, "enter-dec-no-need");
306 }
307 break;
308 }
309 u64State = ASMAtomicReadU64(&pThis->u64State);
310 }
311 break;
312 }
313
314 add_hist(u64State, u64OldState, fDir, "enter-wait-failed");
315 }
316
317 if (pThis->u32Magic != RTSEMXROADS_MAGIC)
318 return VERR_SEM_DESTROYED;
319
320 ASMNopPause();
321 u64State = ASMAtomicReadU64(&pThis->u64State);
322 u64OldState = u64State;
323 }
324
325 /* got it! */
326 Assert((ASMAtomicReadU64(&pThis->u64State) & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT));
327 return VINF_SUCCESS;
328}
329
330
331/**
332 * Internal worker for RTSemXRoadsNSLeave and RTSemXRoadsEWLeave.
333 *
334 * @returns IPRT status code.
335 * @param pThis The semaphore instance.
336 * @param fDir The direction.
337 * @param uCountShift The shift count for getting the count.
338 * @param fCountMask The mask for getting the count.
339 */
340DECL_FORCE_INLINE(int) rtSemXRoadsLeave(RTSEMXROADSINTERNAL *pThis, uint64_t fDir, uint64_t uCountShift, uint64_t fCountMask)
341{
342 for (;;)
343 {
344 uint64_t u64OldState;
345 uint64_t u64State;
346 uint64_t c;
347
348 u64State = ASMAtomicReadU64(&pThis->u64State);
349 u64OldState = u64State;
350
351 /* The direction cannot change until we've left or we'll crash. */
352 Assert((u64State & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT));
353
354 c = (u64State & fCountMask) >> uCountShift;
355 Assert(c > 0);
356 c--;
357
358 if ( c > 0
359 || (u64State & ((RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK) & ~fCountMask)) == 0)
360 {
361 /* We're not the last one across or there aren't any one waiting in the other direction. */
362 u64State &= ~fCountMask;
363 u64State |= c << uCountShift;
364 if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
365 {
366 add_hist(u64State, u64OldState, fDir, "leave-simple");
367 return VINF_SUCCESS;
368 }
369 }
370 else
371 {
372 /* Reverse the direction and signal the threads in the other direction. */
373 u64State &= ~(fCountMask | RTSEMXROADS_DIR_MASK);
374 u64State |= (uint64_t)!fDir << RTSEMXROADS_DIR_SHIFT;
375 if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
376 {
377 add_hist(u64State, u64OldState, fDir, fDir ? "leave-signal-NS" : "leave-signal-EW");
378 Assert(!pThis->aDirs[!fDir].fNeedReset);
379 ASMAtomicWriteBool(&pThis->aDirs[!fDir].fNeedReset, true);
380 int rc = RTSemEventMultiSignal(pThis->aDirs[!fDir].hEvt);
381 AssertRC(rc);
382 return VINF_SUCCESS;
383 }
384 }
385
386 ASMNopPause();
387 if (pThis->u32Magic != RTSEMXROADS_MAGIC)
388 return VERR_SEM_DESTROYED;
389 }
390}
391
392
393RTDECL(int) RTSemXRoadsNSEnter(RTSEMXROADS hXRoads)
394{
395 /*
396 * Validate input.
397 */
398 RTSEMXROADSINTERNAL *pThis = hXRoads;
399 if (pThis == NIL_RTSEMXROADS)
400 return VINF_SUCCESS;
401 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
402 AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
403
404 return rtSemXRoadsEnter(pThis, 0, RTSEMXROADS_CNT_NS_SHIFT, RTSEMXROADS_CNT_NS_MASK, RTSEMXROADS_WAIT_CNT_NS_SHIFT, RTSEMXROADS_WAIT_CNT_NS_MASK);
405}
406
407
408RTDECL(int) RTSemXRoadsNSLeave(RTSEMXROADS hXRoads)
409{
410 /*
411 * Validate input.
412 */
413 RTSEMXROADSINTERNAL *pThis = hXRoads;
414 if (pThis == NIL_RTSEMXROADS)
415 return VINF_SUCCESS;
416 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
417 AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
418
419 return rtSemXRoadsLeave(pThis, 0, RTSEMXROADS_CNT_NS_SHIFT, RTSEMXROADS_CNT_NS_MASK);
420}
421
422
423RTDECL(int) RTSemXRoadsEWEnter(RTSEMXROADS hXRoads)
424{
425 /*
426 * Validate input.
427 */
428 RTSEMXROADSINTERNAL *pThis = hXRoads;
429 if (pThis == NIL_RTSEMXROADS)
430 return VINF_SUCCESS;
431 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
432 AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
433
434 return rtSemXRoadsEnter(pThis, 1, RTSEMXROADS_CNT_EW_SHIFT, RTSEMXROADS_CNT_EW_MASK, RTSEMXROADS_WAIT_CNT_EW_SHIFT, RTSEMXROADS_WAIT_CNT_EW_MASK);
435}
436
437
438RTDECL(int) RTSemXRoadsEWLeave(RTSEMXROADS hXRoads)
439{
440 /*
441 * Validate input.
442 */
443 RTSEMXROADSINTERNAL *pThis = hXRoads;
444 if (pThis == NIL_RTSEMXROADS)
445 return VINF_SUCCESS;
446 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
447 AssertReturn(pThis->u32Magic == RTSEMXROADS_MAGIC, VERR_INVALID_HANDLE);
448
449 return rtSemXRoadsLeave(pThis, 1, RTSEMXROADS_CNT_EW_SHIFT, RTSEMXROADS_CNT_EW_MASK);
450}
451
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette