VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstR0ThreadPreemption.cpp@ 51230

Last change on this file since 51230 was 48464, checked in by vboxsync, 11 years ago

Wrong place.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: tstR0ThreadPreemption.cpp 48464 2013-09-12 22:39:28Z vboxsync $ */
2/** @file
3 * IPRT R0 Testcase - Thread Preemption.
4 */
5
6/*
7 * Copyright (C) 2009-2013 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 "tstR0ThreadPreemption.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 RTTHREADCTX hThreadCtx;
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) tstR0ThreadCtxHook(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), "!tstR0ThreadCtxHook: Invalid magic.");
79 return;
80 }
81
82 switch (enmEvent)
83 {
84 case RTTHREADCTXEVENT_PREEMPTING:
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 "!tstR0ThreadCtxHook[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 "!tstR0ThreadCtxHook[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 "!tstR0ThreadCtxHook[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_RESUMED:
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 "!tstR0ThreadCtxHook[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 "!tstR0ThreadCtxHook[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) TSTR0ThreadPreemptionSrvReqHandler(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 (!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 TSTR0THREADPREMEPTION_SANITY_OK:
184 break;
185
186 case TSTR0THREADPREMEPTION_SANITY_FAILURE:
187 RTStrPrintf(pszErr, cchErr, "!42failure42%1024s", "");
188 break;
189
190 case TSTR0THREADPREMEPTION_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 TSTR0THREADPREMEPTION_IS_TRUSTY:
210 if (!RTThreadPreemptIsPendingTrusty())
211 RTStrPrintf(pszErr, cchErr, "!Untrusty");
212 break;
213
214 case TSTR0THREADPREMEPTION_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 TSTR0THREADPREMEPTION_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 TSTR0THREADPREEMPTION_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 = RTThreadCtxHooksAreRegistered(NIL_RTTHREADCTX);
305 if (fRegistered)
306 {
307 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksAreRegistered returns true before creating any hooks");
308 break;
309 }
310
311 RTTHREADCTX hThreadCtx;
312 int rc = RTThreadCtxHooksCreate(&hThreadCtx);
313 if (RT_FAILURE(rc))
314 {
315 if (rc == VERR_NOT_SUPPORTED)
316 RTStrPrintf(pszErr, cchErr, "RTThreadCtxHooksCreate returns VERR_NOT_SUPPORTED");
317 else
318 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksCreate returns %Rrc", rc);
319 break;
320 }
321
322 fRegistered = RTThreadCtxHooksAreRegistered(hThreadCtx);
323 if (fRegistered)
324 {
325 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksAreRegistered returns true before registering any hooks");
326 RTThreadCtxHooksRelease(hThreadCtx);
327 break;
328 }
329
330 PTSTRTR0THREADCTXDATA pCtxData = (PTSTRTR0THREADCTXDATA)RTMemAllocZ(sizeof(*pCtxData));
331 AssertReturn(pCtxData, VERR_NO_MEMORY);
332 pCtxData->u32Magic = TSTRTR0THREADCTXDATA_MAGIC;
333 pCtxData->hThreadCtx = hThreadCtx;
334 pCtxData->fPreemptingSuccess = false;
335 pCtxData->fPreemptingInvoked = false;
336 pCtxData->fResumedInvoked = false;
337 pCtxData->fResumedSuccess = false;
338 pCtxData->hSourceThread = RTThreadNativeSelf();
339 RT_ZERO(pCtxData->achResult);
340
341 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
342 RTThreadPreemptDisable(&PreemptState);
343 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
344
345 pCtxData->uSourceCpuId = RTMpCpuId();
346
347 rc = RTThreadCtxHooksRegister(hThreadCtx, &tstR0ThreadCtxHook, pCtxData);
348 if (RT_FAILURE(rc))
349 {
350 RTThreadPreemptRestore(&PreemptState);
351 RTMemFree(pCtxData);
352 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksRegister returns %Rrc", rc);
353 break;
354 }
355
356 fRegistered = RTThreadCtxHooksAreRegistered(hThreadCtx);
357 if (!fRegistered)
358 {
359 RTThreadPreemptRestore(&PreemptState);
360 RTMemFree(pCtxData);
361 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksAreRegistered return false when hooks are supposedly registered");
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 "!tstR0ThreadCtxHooks[RTTHREADCTXEVENT_PREEMPTING] not invoked before migrating from CPU %RU32 to %RU32",
395 pCtxData->uSourceCpuId, uCurrentCpuId);
396 }
397 else
398 {
399 RTStrPrintf(pszErr, cchErr, "!tstR0ThreadCtxHooks[RTTHREADCTXEVENT_PREEMPTING] 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, "!tstR0ThreadCtxHooks[RTTHREADCTXEVENT_RESUMED] not invoked after ca. %u ms",
424 cMsSlept);
425 }
426 else if (!pCtxData->fResumedSuccess)
427 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
428 }
429
430 RTThreadCtxHooksDeregister(hThreadCtx);
431
432 fRegistered = RTThreadCtxHooksAreRegistered(hThreadCtx);
433 if (fRegistered)
434 {
435 RTMemFree(pCtxData);
436 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksAreRegistered return true when hooks are deregistered");
437 break;
438 }
439
440 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
441 uint32_t cRefs = RTThreadCtxHooksRelease(hThreadCtx);
442 if (cRefs == UINT32_MAX)
443 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksRelease returns invalid cRefs!");
444
445 RTMemFree(pCtxData);
446 break;
447 }
448
449 default:
450 RTStrPrintf(pszErr, cchErr, "!Unknown test #%d", uOperation);
451 break;
452 }
453
454 /* The error indicator is the '!' in the message buffer. */
455 return VINF_SUCCESS;
456}
457
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