VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTR0ThreadPreemption.cpp@ 106061

Last change on this file since 106061 was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.4 KB
Line 
1/* $Id: tstRTR0ThreadPreemption.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT R0 Testcase - Thread Preemption.
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#include <iprt/thread.h>
42
43#include <iprt/asm-amd64-x86.h>
44#include <iprt/errcore.h>
45#include <iprt/mem.h>
46#include <iprt/time.h>
47#include <iprt/string.h>
48#include <VBox/sup.h>
49#include "tstRTR0ThreadPreemption.h"
50
51
52#define TSTRTR0THREADCTXDATA_MAGIC 0xc01a50da
53
54/**
55 * Thread-context hook data.
56 */
57typedef struct TSTRTR0THREADCTXDATA
58{
59 uint32_t volatile u32Magic;
60 RTCPUID uSourceCpuId;
61 RTNATIVETHREAD hSourceThread;
62
63 /* For RTTHREADCTXEVENT_PREEMPTING. */
64 bool fPreemptingSuccess;
65 volatile bool fPreemptingInvoked;
66
67 /* For RTTHREADCTXEVENT_RESUMED. */
68 bool fResumedSuccess;
69 volatile bool fResumedInvoked;
70
71 char achResult[512];
72} TSTRTR0THREADCTXDATA, *PTSTRTR0THREADCTXDATA;
73
74
75/**
76 * Thread-context hook function.
77 *
78 * @param enmEvent The thread-context event.
79 * @param pvUser Pointer to the user argument.
80 */
81static DECLCALLBACK(void) tstRTR0ThreadCtxHook(RTTHREADCTXEVENT enmEvent, void *pvUser)
82{
83 PTSTRTR0THREADCTXDATA pData = (PTSTRTR0THREADCTXDATA)pvUser;
84 AssertPtrReturnVoid(pData);
85
86 if (pData->u32Magic != TSTRTR0THREADCTXDATA_MAGIC)
87 {
88 RTStrPrintf(pData->achResult, sizeof(pData->achResult), "!tstRTR0ThreadCtxHook: Invalid magic.");
89 return;
90 }
91
92 switch (enmEvent)
93 {
94 case RTTHREADCTXEVENT_OUT:
95 {
96 ASMAtomicWriteBool(&pData->fPreemptingInvoked, true);
97
98 /* We've already been called once, we now might very well be on another CPU. Nothing to do here. */
99 if (pData->fPreemptingSuccess)
100 return;
101
102 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
103 {
104 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
105 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_PREEMPTING]: Called with preemption enabled");
106 break;
107 }
108
109 RTNATIVETHREAD hCurrentThread = RTThreadNativeSelf();
110 if (pData->hSourceThread != hCurrentThread)
111 {
112 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
113 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_PREEMPTING]: Thread switched! Source=%RTnthrd Current=%RTnthrd.",
114 pData->hSourceThread, hCurrentThread);
115 break;
116 }
117
118 RTCPUID uCurrentCpuId = RTMpCpuId();
119 if (pData->uSourceCpuId != uCurrentCpuId)
120 {
121 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
122 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_PREEMPTING]: migrated uSourceCpuId=%RU32 uCurrentCpuId=%RU32",
123 pData->uSourceCpuId, uCurrentCpuId);
124 break;
125 }
126
127 pData->fPreemptingSuccess = true;
128 break;
129 }
130
131 case RTTHREADCTXEVENT_IN:
132 {
133 ASMAtomicWriteBool(&pData->fResumedInvoked, true);
134
135 /* We've already been called once successfully, nothing more to do. */
136 if (ASMAtomicReadBool(&pData->fResumedSuccess))
137 return;
138
139 if (!pData->fPreemptingSuccess)
140 {
141 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
142 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_RESUMED]: Called before preempting callback was invoked.");
143 break;
144 }
145
146 RTNATIVETHREAD hCurrentThread = RTThreadNativeSelf();
147 if (pData->hSourceThread != hCurrentThread)
148 {
149 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
150 "!tstRTR0ThreadCtxHook[RTTHREADCTXEVENT_RESUMED]: Thread switched! Source=%RTnthrd Current=%RTnthrd.",
151 pData->hSourceThread, hCurrentThread);
152 break;
153 }
154
155 ASMAtomicWriteBool(&pData->fResumedSuccess, true);
156 break;
157 }
158
159 default:
160 AssertMsgFailed(("Invalid event %#x\n", enmEvent));
161 break;
162 }
163}
164
165
166/**
167 * Service request callback function.
168 *
169 * @returns VBox status code.
170 * @param pSession The caller's session.
171 * @param u64Arg 64-bit integer argument.
172 * @param pReqHdr The request header. Input / Output. Optional.
173 */
174DECLEXPORT(int) TSTRTR0ThreadPreemptionSrvReqHandler(PSUPDRVSESSION pSession, uint32_t uOperation,
175 uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr)
176{
177 NOREF(pSession);
178 if (u64Arg)
179 return VERR_INVALID_PARAMETER;
180 if (!RT_VALID_PTR(pReqHdr))
181 return VERR_INVALID_PARAMETER;
182 char *pszErr = (char *)(pReqHdr + 1);
183 size_t cchErr = pReqHdr->cbReq - sizeof(*pReqHdr);
184 if (cchErr < 32 || cchErr >= 0x10000)
185 return VERR_INVALID_PARAMETER;
186 *pszErr = '\0';
187
188 /*
189 * The big switch.
190 */
191 switch (uOperation)
192 {
193 case TSTRTR0THREADPREEMPTION_SANITY_OK:
194 break;
195
196 case TSTRTR0THREADPREEMPTION_SANITY_FAILURE:
197 RTStrPrintf(pszErr, cchErr, "!42failure42%1024s", "");
198 break;
199
200 case TSTRTR0THREADPREEMPTION_BASIC:
201 {
202 if (!ASMIntAreEnabled())
203 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
204 else if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
205 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns false by default");
206 else
207 {
208 RTTHREADPREEMPTSTATE State = RTTHREADPREEMPTSTATE_INITIALIZER;
209 RTThreadPreemptDisable(&State);
210 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
211 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after RTThreadPreemptDisable");
212 else if (!ASMIntAreEnabled())
213 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
214 RTThreadPreemptRestore(&State);
215 }
216 break;
217 }
218
219 case TSTRTR0THREADPREEMPTION_IS_TRUSTY:
220 if (!RTThreadPreemptIsPendingTrusty())
221 RTStrPrintf(pszErr, cchErr, "!Untrusty");
222 break;
223
224 case TSTRTR0THREADPREEMPTION_IS_PENDING:
225 {
226 RTTHREADPREEMPTSTATE State = RTTHREADPREEMPTSTATE_INITIALIZER;
227 RTThreadPreemptDisable(&State);
228 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
229 {
230#ifdef RT_OS_DARWIN
231 uint64_t const cNsMax = UINT64_C(8)*1000U*1000U*1000U;
232#else
233 uint64_t const cNsMax = UINT64_C(2)*1000U*1000U*1000U;
234#endif
235 if (ASMIntAreEnabled())
236 {
237 uint64_t u64StartTS = RTTimeNanoTS();
238 uint64_t u64StartSysTS = RTTimeSystemNanoTS();
239 uint64_t cLoops = 0;
240 uint64_t cNanosSysElapsed;
241 uint64_t cNanosElapsed;
242 bool fPending;
243 do
244 {
245 fPending = RTThreadPreemptIsPending(NIL_RTTHREAD);
246 cNanosElapsed = RTTimeNanoTS() - u64StartTS;
247 cNanosSysElapsed = RTTimeSystemNanoTS() - u64StartSysTS;
248 cLoops++;
249 } while ( !fPending
250 && cNanosElapsed < cNsMax
251 && cNanosSysElapsed < cNsMax
252 && cLoops < 100U*_1M);
253 if (!fPending)
254 RTStrPrintf(pszErr, cchErr, "!Preempt not pending after %'llu loops / %'llu ns / %'llu ns (sys)",
255 cLoops, cNanosElapsed, cNanosSysElapsed);
256 else if (cLoops == 1)
257 RTStrPrintf(pszErr, cchErr, "!cLoops=1\n");
258 else
259 RTStrPrintf(pszErr, cchErr, "RTThreadPreemptIsPending returned true after %'llu loops / %'llu ns / %'llu ns (sys)",
260 cLoops, cNanosElapsed, cNanosSysElapsed);
261 }
262 else
263 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
264 }
265 else
266 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after RTThreadPreemptDisable");
267 RTThreadPreemptRestore(&State);
268 break;
269 }
270
271 case TSTRTR0THREADPREEMPTION_NESTED:
272 {
273 bool const fDefault = RTThreadPreemptIsEnabled(NIL_RTTHREAD);
274 RTTHREADPREEMPTSTATE State1 = RTTHREADPREEMPTSTATE_INITIALIZER;
275 RTThreadPreemptDisable(&State1);
276 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
277 {
278 RTTHREADPREEMPTSTATE State2 = RTTHREADPREEMPTSTATE_INITIALIZER;
279 RTThreadPreemptDisable(&State2);
280 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
281 {
282 RTTHREADPREEMPTSTATE State3 = RTTHREADPREEMPTSTATE_INITIALIZER;
283 RTThreadPreemptDisable(&State3);
284 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
285 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 3rd RTThreadPreemptDisable");
286
287 RTThreadPreemptRestore(&State3);
288 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) && !*pszErr)
289 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 1st RTThreadPreemptRestore");
290 }
291 else
292 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 2nd RTThreadPreemptDisable");
293
294 RTThreadPreemptRestore(&State2);
295 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) && !*pszErr)
296 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 2nd RTThreadPreemptRestore");
297 }
298 else
299 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 1st RTThreadPreemptDisable");
300 RTThreadPreemptRestore(&State1);
301 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) != fDefault && !*pszErr)
302 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns false after 3rd RTThreadPreemptRestore");
303 break;
304 }
305
306 case TSTRTR0THREADPREEMPTION_CTXHOOKS:
307 {
308 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
309 {
310 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksCreate must be called with preemption enabled");
311 break;
312 }
313
314 bool fRegistered = RTThreadCtxHookIsEnabled(NIL_RTTHREADCTXHOOK);
315 if (fRegistered)
316 {
317 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookIsEnabled returns true before creating any hooks");
318 break;
319 }
320
321 PTSTRTR0THREADCTXDATA pCtxData = (PTSTRTR0THREADCTXDATA)RTMemAllocZ(sizeof(*pCtxData));
322 AssertReturn(pCtxData, VERR_NO_MEMORY);
323 pCtxData->u32Magic = TSTRTR0THREADCTXDATA_MAGIC;
324 pCtxData->fPreemptingSuccess = false;
325 pCtxData->fPreemptingInvoked = false;
326 pCtxData->fResumedInvoked = false;
327 pCtxData->fResumedSuccess = false;
328 pCtxData->hSourceThread = RTThreadNativeSelf();
329 RT_ZERO(pCtxData->achResult);
330
331 RTTHREADCTXHOOK hThreadCtx;
332 int rc = RTThreadCtxHookCreate(&hThreadCtx, 0, tstRTR0ThreadCtxHook, pCtxData);
333 if (RT_FAILURE(rc))
334 {
335 if (rc == VERR_NOT_SUPPORTED)
336 RTStrPrintf(pszErr, cchErr, "RTThreadCtxHooksCreate returns VERR_NOT_SUPPORTED");
337 else
338 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksCreate returns %Rrc", rc);
339 RTMemFree(pCtxData);
340 break;
341 }
342
343 fRegistered = RTThreadCtxHookIsEnabled(hThreadCtx);
344 if (fRegistered)
345 {
346 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookIsEnabled returns true before registering any hooks");
347 RTThreadCtxHookDestroy(hThreadCtx);
348 break;
349 }
350
351 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
352 RTThreadPreemptDisable(&PreemptState);
353 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
354
355 pCtxData->uSourceCpuId = RTMpCpuId();
356
357 rc = RTThreadCtxHookEnable(hThreadCtx);
358 if (RT_FAILURE(rc))
359 {
360 RTThreadPreemptRestore(&PreemptState);
361 RTMemFree(pCtxData);
362 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookEnable returns %Rrc", rc);
363 break;
364 }
365
366 fRegistered = RTThreadCtxHookIsEnabled(hThreadCtx);
367 if (!fRegistered)
368 {
369 RTThreadPreemptRestore(&PreemptState);
370 RTThreadCtxHookDestroy(hThreadCtx);
371 RTMemFree(pCtxData);
372 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookIsEnabled return false when hooks are supposed to be enabled");
373 break;
374 }
375
376 RTThreadPreemptRestore(&PreemptState);
377
378 /* Check if the preempting callback has/will been invoked. */
379 const uint32_t cMsTimeout = 10000;
380 const uint32_t cMsSleepGranularity = 50;
381 uint32_t cMsSlept = 0;
382 RTCPUID uCurrentCpuId = NIL_RTCPUID;
383 for (;;)
384 {
385 RTThreadYield();
386 RTThreadPreemptDisable(&PreemptState);
387 uCurrentCpuId = RTMpCpuId();
388 RTThreadPreemptRestore(&PreemptState);
389
390 if ( pCtxData->uSourceCpuId != uCurrentCpuId
391 || cMsSlept >= cMsTimeout)
392 {
393 break;
394 }
395
396 RTThreadSleep(cMsSleepGranularity);
397 cMsSlept += cMsSleepGranularity;
398 }
399
400 if (!ASMAtomicReadBool(&pCtxData->fPreemptingInvoked))
401 {
402 if (pCtxData->uSourceCpuId != uCurrentCpuId)
403 {
404 RTStrPrintf(pszErr, cchErr,
405 "!tstRTR0ThreadCtxHooks[RTTHREADCTXEVENT_OUT] not invoked before migrating from CPU %RU32 to %RU32",
406 pCtxData->uSourceCpuId, uCurrentCpuId);
407 }
408 else
409 {
410 RTStrPrintf(pszErr, cchErr, "!tstRTR0ThreadCtxHooks[RTTHREADCTXEVENT_OUT] not invoked after ca. %u ms",
411 cMsSlept);
412 }
413 }
414 else if (!pCtxData->fPreemptingSuccess)
415 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
416 else
417 {
418 /* Preempting callback succeeded, now check if the resumed callback has/will been invoked. */
419 cMsSlept = 0;
420 for (;;)
421 {
422 if ( ASMAtomicReadBool(&pCtxData->fResumedInvoked)
423 || cMsSlept >= cMsTimeout)
424 {
425 break;
426 }
427
428 RTThreadSleep(cMsSleepGranularity);
429 cMsSlept += cMsSleepGranularity;
430 }
431
432 if (!ASMAtomicReadBool(&pCtxData->fResumedInvoked))
433 {
434 RTStrPrintf(pszErr, cchErr, "!tstRTR0ThreadCtxHooks[RTTHREADCTXEVENT_IN] not invoked after ca. %u ms",
435 cMsSlept);
436 }
437 else if (!pCtxData->fResumedSuccess)
438 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
439 }
440
441 rc = RTThreadCtxHookDisable(hThreadCtx);
442 if (RT_SUCCESS(rc))
443 {
444 fRegistered = RTThreadCtxHookIsEnabled(hThreadCtx);
445 if (fRegistered)
446 {
447 RTThreadCtxHookDestroy(hThreadCtx);
448 RTMemFree(pCtxData);
449 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookIsEnabled return true when hooks are disabled");
450 break;
451 }
452 }
453 else
454 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHookDisable failed, returns %Rrc!", rc);
455
456 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
457 rc = RTThreadCtxHookDestroy(hThreadCtx);
458 if (RT_FAILURE(rc))
459 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksRelease returns %Rrc!", rc);
460
461 RTMemFree(pCtxData);
462 break;
463 }
464
465 default:
466 RTStrPrintf(pszErr, cchErr, "!Unknown test #%d", uOperation);
467 break;
468 }
469
470 /* The error indicator is the '!' in the message buffer. */
471 return VINF_SUCCESS;
472}
473
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