VirtualBox

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

Last change on this file since 57192 was 57070, checked in by vboxsync, 10 years ago

testcase/tstRTR0ThreadPreemption: check for failures for RTThreadCtxHookDisable as well.

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