VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 17 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
RevLine 
[25426]1/* $Id: semxroads-generic.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT Testcase - RTSemXRoads, generic implementation.
4 */
5
6/*
[98103]7 * Copyright (C) 2009-2023 Oracle and/or its affiliates.
[25426]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
[25426]11 *
[96407]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 *
[25426]25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
[96407]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
[25426]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.
[96407]33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
[25426]35 */
36
37
[57358]38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
[25549]41#define RTASSERT_QUIET
[25426]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>
[25431]48#include <iprt/mem.h>
[25426]49#include <iprt/thread.h>
50
51#include "internal/magics.h"
52
53
[57358]54/*********************************************************************************************************************************
55* Structures and Typedefs *
56*********************************************************************************************************************************/
[25431]57typedef struct RTSEMXROADSINTERNAL
58{
59 /** Magic value (RTSEMXROADS_MAGIC). */
60 uint32_t volatile u32Magic;
[25549]61 uint32_t u32Padding; /**< alignment padding.*/
62 /* The state variable.
[25431]63 * All accesses are atomic and it bits are defined like this:
64 * Bits 0..14 - cNorthSouth.
[25549]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.
[25431]72 */
[25549]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];
[25431]83} RTSEMXROADSINTERNAL;
[25426]84
85
[57358]86/*********************************************************************************************************************************
87* Defined Constants And Macros *
88*********************************************************************************************************************************/
[25549]89#define RTSEMXROADS_CNT_BITS 15
90#define RTSEMXROADS_CNT_MASK UINT64_C(0x00007fff)
[25431]91
[25549]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)
[25431]98
[25549]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
[25426]142RTDECL(int) RTSemXRoadsCreate(PRTSEMXROADS phXRoads)
143{
[25431]144 RTSEMXROADSINTERNAL *pThis = (RTSEMXROADSINTERNAL *)RTMemAlloc(sizeof(*pThis));
145 if (!pThis)
146 return VERR_NO_MEMORY;
147
[25549]148 int rc = RTSemEventMultiCreate(&pThis->aDirs[0].hEvt);
[25431]149 if (RT_SUCCESS(rc))
150 {
[25549]151 rc = RTSemEventMultiCreate(&pThis->aDirs[1].hEvt);
[25431]152 if (RT_SUCCESS(rc))
153 {
[25549]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;
[25431]161 }
[25549]162 RTSemEventMultiDestroy(pThis->aDirs[0].hEvt);
[25431]163 }
164 return rc;
[25426]165}
166
167
168RTDECL(int) RTSemXRoadsDestroy(RTSEMXROADS hXRoads)
169{
[25431]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);
[25549]178 Assert(!(ASMAtomicReadU64(&pThis->u64State) & (RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK)));
[25431]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;
[25549]186 ASMAtomicXchgHandle(&pThis->aDirs[0].hEvt, NIL_RTSEMEVENTMULTI, &hEvt);
[25431]187 int rc = RTSemEventMultiDestroy(hEvt);
188 AssertRC(rc);
189
[25549]190 ASMAtomicXchgHandle(&pThis->aDirs[1].hEvt, NIL_RTSEMEVENTMULTI, &hEvt);
[25431]191 rc = RTSemEventMultiDestroy(hEvt);
192 AssertRC(rc);
193
[25549]194 RTMemFree(pThis);
[25431]195 return VINF_SUCCESS;
[25426]196}
197
198
[25431]199/**
200 * Internal worker for RTSemXRoadsNSEnter and RTSemXRoadsEWEnter.
201 *
202 * @returns IPRT status code.
[33540]203 * @param pThis The semaphore instance.
[25549]204 * @param fDir The direction.
[25431]205 * @param uCountShift The shift count for getting the count.
206 * @param fCountMask The mask for getting the count.
[25549]207 * @param uWaitCountShift The shift count for getting the wait count.
208 * @param fWaitCountMask The mask for getting the wait count.
[25431]209 */
[25549]210DECL_FORCE_INLINE(int) rtSemXRoadsEnter(RTSEMXROADSINTERNAL *pThis, uint64_t fDir,
211 uint64_t uCountShift, uint64_t fCountMask,
212 uint64_t uWaitCountShift, uint64_t fWaitCountMask)
[25431]213{
[25549]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
[25431]221 for (;;)
222 {
[25549]223 if ((u64State & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT))
[25431]224 {
[25549]225 /* It flows in the right direction, try follow it before it changes. */
226 uint64_t c = (u64State & fCountMask) >> uCountShift;
[25431]227 c++;
228 Assert(c < 8*_1K);
[25549]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 }
[25431]236 }
[25549]237 else if ((u64State & (RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK)) == 0)
[25431]238 {
239 /* Wrong direction, but we're alone here and can simply try switch the direction. */
[25549]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 }
[25431]248 }
249 else
250 {
[25549]251 /* Add ourselves to the queue and wait for the direction to change. */
252 uint64_t c = (u64State & fCountMask) >> uCountShift;
[25431]253 c++;
[25549]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))
[25431]265 {
[25549]266 add_hist(u64State, u64OldState, fDir, "enter-wait");
[25431]267 for (uint32_t iLoop = 0; ; iLoop++)
268 {
[25549]269 int rc = RTSemEventMultiWait(pThis->aDirs[fDir].hEvt, RT_INDEFINITE_WAIT);
[25431]270 AssertRCReturn(rc, rc);
271
272 if (pThis->u32Magic != RTSEMXROADS_MAGIC)
273 return VERR_SEM_DESTROYED;
274
[25549]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 }
[25431]282
[25549]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);
[25431]310 }
[25549]311 break;
[25431]312 }
[25549]313
314 add_hist(u64State, u64OldState, fDir, "enter-wait-failed");
[25431]315 }
316
317 if (pThis->u32Magic != RTSEMXROADS_MAGIC)
318 return VERR_SEM_DESTROYED;
[25549]319
320 ASMNopPause();
321 u64State = ASMAtomicReadU64(&pThis->u64State);
322 u64OldState = u64State;
[25431]323 }
[25549]324
325 /* got it! */
326 Assert((ASMAtomicReadU64(&pThis->u64State) & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT));
327 return VINF_SUCCESS;
[25431]328}
329
330
331/**
332 * Internal worker for RTSemXRoadsNSLeave and RTSemXRoadsEWLeave.
333 *
334 * @returns IPRT status code.
[33540]335 * @param pThis The semaphore instance.
[25549]336 * @param fDir The direction.
[25431]337 * @param uCountShift The shift count for getting the count.
338 * @param fCountMask The mask for getting the count.
339 */
[25549]340DECL_FORCE_INLINE(int) rtSemXRoadsLeave(RTSEMXROADSINTERNAL *pThis, uint64_t fDir, uint64_t uCountShift, uint64_t fCountMask)
[25431]341{
342 for (;;)
343 {
[25549]344 uint64_t u64OldState;
345 uint64_t u64State;
346 uint64_t c;
[25431]347
[25549]348 u64State = ASMAtomicReadU64(&pThis->u64State);
349 u64OldState = u64State;
[25431]350
351 /* The direction cannot change until we've left or we'll crash. */
[25549]352 Assert((u64State & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT));
[25431]353
[25549]354 c = (u64State & fCountMask) >> uCountShift;
[25431]355 Assert(c > 0);
356 c--;
357
358 if ( c > 0
[25549]359 || (u64State & ((RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK) & ~fCountMask)) == 0)
[25431]360 {
361 /* We're not the last one across or there aren't any one waiting in the other direction. */
[25549]362 u64State &= ~fCountMask;
363 u64State |= c << uCountShift;
364 if (ASMAtomicCmpXchgU64(&pThis->u64State, u64State, u64OldState))
365 {
366 add_hist(u64State, u64OldState, fDir, "leave-simple");
[25431]367 return VINF_SUCCESS;
[25549]368 }
[25431]369 }
370 else
371 {
[25549]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))
[25431]376 {
[25549]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);
[25431]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
[25426]393RTDECL(int) RTSemXRoadsNSEnter(RTSEMXROADS hXRoads)
394{
[25431]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
[25549]404 return rtSemXRoadsEnter(pThis, 0, RTSEMXROADS_CNT_NS_SHIFT, RTSEMXROADS_CNT_NS_MASK, RTSEMXROADS_WAIT_CNT_NS_SHIFT, RTSEMXROADS_WAIT_CNT_NS_MASK);
[25426]405}
406
407
408RTDECL(int) RTSemXRoadsNSLeave(RTSEMXROADS hXRoads)
409{
[25431]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
[25549]419 return rtSemXRoadsLeave(pThis, 0, RTSEMXROADS_CNT_NS_SHIFT, RTSEMXROADS_CNT_NS_MASK);
[25426]420}
421
422
423RTDECL(int) RTSemXRoadsEWEnter(RTSEMXROADS hXRoads)
424{
[25431]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
[25549]434 return rtSemXRoadsEnter(pThis, 1, RTSEMXROADS_CNT_EW_SHIFT, RTSEMXROADS_CNT_EW_MASK, RTSEMXROADS_WAIT_CNT_EW_SHIFT, RTSEMXROADS_WAIT_CNT_EW_MASK);
[25426]435}
436
437
438RTDECL(int) RTSemXRoadsEWLeave(RTSEMXROADS hXRoads)
439{
[25431]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
[25549]449 return rtSemXRoadsLeave(pThis, 1, RTSEMXROADS_CNT_EW_SHIFT, RTSEMXROADS_CNT_EW_MASK);
[25426]450}
451
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use