VirtualBox

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

Last change on this file since 94130 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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