VirtualBox

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

Last change on this file since 47559 was 47521, checked in by vboxsync, 11 years ago

I really wonder why gcc doesn't warn/error here ...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: tstR0ThreadPreemption.cpp 47521 2013-08-02 12:11:14Z 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 }
106
107 RTCPUID uCurrentCpuId = RTMpCpuId();
108 if (pData->uSourceCpuId != uCurrentCpuId)
109 {
110 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
111 "!tstR0ThreadCtxHook[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_RESUMED:
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 "!tstR0ThreadCtxHook[RTTHREADCTXEVENT_RESUMED]: Called before preempting callback was invoked.");
132 }
133
134 RTNATIVETHREAD hCurrentThread = RTThreadNativeSelf();
135 if (pData->hSourceThread != hCurrentThread)
136 {
137 RTStrPrintf(pData->achResult, sizeof(pData->achResult),
138 "!tstR0ThreadCtxHook[RTTHREADCTXEVENT_RESUMED]: Thread switched! Source=%RTnthrd Current=%RTnthrd.",
139 pData->hSourceThread, hCurrentThread);
140 }
141
142 ASMAtomicWriteBool(&pData->fResumedSuccess, true);
143 break;
144 }
145
146 default:
147 AssertMsgFailed(("Invalid event %#x\n", enmEvent));
148 break;
149 }
150}
151
152
153/**
154 * Service request callback function.
155 *
156 * @returns VBox status code.
157 * @param pSession The caller's session.
158 * @param u64Arg 64-bit integer argument.
159 * @param pReqHdr The request header. Input / Output. Optional.
160 */
161DECLEXPORT(int) TSTR0ThreadPreemptionSrvReqHandler(PSUPDRVSESSION pSession, uint32_t uOperation,
162 uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr)
163{
164 NOREF(pSession);
165 if (u64Arg)
166 return VERR_INVALID_PARAMETER;
167 if (!VALID_PTR(pReqHdr))
168 return VERR_INVALID_PARAMETER;
169 char *pszErr = (char *)(pReqHdr + 1);
170 size_t cchErr = pReqHdr->cbReq - sizeof(*pReqHdr);
171 if (cchErr < 32 || cchErr >= 0x10000)
172 return VERR_INVALID_PARAMETER;
173 *pszErr = '\0';
174
175 /*
176 * The big switch.
177 */
178 switch (uOperation)
179 {
180 case TSTR0THREADPREMEPTION_SANITY_OK:
181 break;
182
183 case TSTR0THREADPREMEPTION_SANITY_FAILURE:
184 RTStrPrintf(pszErr, cchErr, "!42failure42%1024s", "");
185 break;
186
187 case TSTR0THREADPREMEPTION_BASIC:
188 {
189 if (!ASMIntAreEnabled())
190 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
191 else if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
192 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns false by default");
193 else
194 {
195 RTTHREADPREEMPTSTATE State = RTTHREADPREEMPTSTATE_INITIALIZER;
196 RTThreadPreemptDisable(&State);
197 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
198 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after RTThreadPreemptDisable");
199 else if (!ASMIntAreEnabled())
200 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
201 RTThreadPreemptRestore(&State);
202 }
203 break;
204 }
205
206 case TSTR0THREADPREMEPTION_IS_TRUSTY:
207 if (!RTThreadPreemptIsPendingTrusty())
208 RTStrPrintf(pszErr, cchErr, "!Untrusty");
209 break;
210
211 case TSTR0THREADPREMEPTION_IS_PENDING:
212 {
213 RTTHREADPREEMPTSTATE State = RTTHREADPREEMPTSTATE_INITIALIZER;
214 RTThreadPreemptDisable(&State);
215 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
216 {
217#ifdef RT_OS_DARWIN
218 uint64_t const cNsMax = UINT64_C(8)*1000U*1000U*1000U;
219#else
220 uint64_t const cNsMax = UINT64_C(2)*1000U*1000U*1000U;
221#endif
222 if (ASMIntAreEnabled())
223 {
224 uint64_t u64StartTS = RTTimeNanoTS();
225 uint64_t u64StartSysTS = RTTimeSystemNanoTS();
226 uint64_t cLoops = 0;
227 uint64_t cNanosSysElapsed;
228 uint64_t cNanosElapsed;
229 bool fPending;
230 do
231 {
232 fPending = RTThreadPreemptIsPending(NIL_RTTHREAD);
233 cNanosElapsed = RTTimeNanoTS() - u64StartTS;
234 cNanosSysElapsed = RTTimeSystemNanoTS() - u64StartSysTS;
235 cLoops++;
236 } while ( !fPending
237 && cNanosElapsed < cNsMax
238 && cNanosSysElapsed < cNsMax
239 && cLoops < 100U*_1M);
240 if (!fPending)
241 RTStrPrintf(pszErr, cchErr, "!Preempt not pending after %'llu loops / %'llu ns / %'llu ns (sys)",
242 cLoops, cNanosElapsed, cNanosSysElapsed);
243 else if (cLoops == 1)
244 RTStrPrintf(pszErr, cchErr, "!cLoops=1\n");
245 else
246 RTStrPrintf(pszErr, cchErr, "RTThreadPreemptIsPending returned true after %'llu loops / %'llu ns / %'llu ns (sys)",
247 cLoops, cNanosElapsed, cNanosSysElapsed);
248 }
249 else
250 RTStrPrintf(pszErr, cchErr, "!Interrupts disabled");
251 }
252 else
253 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after RTThreadPreemptDisable");
254 RTThreadPreemptRestore(&State);
255 break;
256 }
257
258 case TSTR0THREADPREMEPTION_NESTED:
259 {
260 bool const fDefault = RTThreadPreemptIsEnabled(NIL_RTTHREAD);
261 RTTHREADPREEMPTSTATE State1 = RTTHREADPREEMPTSTATE_INITIALIZER;
262 RTThreadPreemptDisable(&State1);
263 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
264 {
265 RTTHREADPREEMPTSTATE State2 = RTTHREADPREEMPTSTATE_INITIALIZER;
266 RTThreadPreemptDisable(&State2);
267 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
268 {
269 RTTHREADPREEMPTSTATE State3 = RTTHREADPREEMPTSTATE_INITIALIZER;
270 RTThreadPreemptDisable(&State3);
271 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
272 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 3rd RTThreadPreemptDisable");
273
274 RTThreadPreemptRestore(&State3);
275 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) && !*pszErr)
276 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 1st RTThreadPreemptRestore");
277 }
278 else
279 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 2nd RTThreadPreemptDisable");
280
281 RTThreadPreemptRestore(&State2);
282 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) && !*pszErr)
283 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 2nd RTThreadPreemptRestore");
284 }
285 else
286 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns true after 1st RTThreadPreemptDisable");
287 RTThreadPreemptRestore(&State1);
288 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD) != fDefault && !*pszErr)
289 RTStrPrintf(pszErr, cchErr, "!RTThreadPreemptIsEnabled returns false after 3rd RTThreadPreemptRestore");
290 break;
291 }
292
293 case TSTR0THREADPREEMPTION_CTXHOOKS:
294 {
295 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
296 {
297 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksCreate must be called with preemption enabled");
298 break;
299 }
300
301 RTTHREADCTX hThreadCtx;
302 int rc = RTThreadCtxHooksCreate(&hThreadCtx);
303 if (RT_FAILURE(rc))
304 {
305 if (rc == VERR_NOT_SUPPORTED)
306 RTStrPrintf(pszErr, cchErr, "RTThreadCtxHooksCreate returns VERR_NOT_SUPPORTED");
307 else
308 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksCreate returns %Rrc", rc);
309 break;
310 }
311
312 PTSTRTR0THREADCTXDATA pCtxData = (PTSTRTR0THREADCTXDATA)RTMemAllocZ(sizeof(*pCtxData));
313 AssertReturn(pCtxData, VERR_NO_MEMORY);
314 pCtxData->u32Magic = TSTRTR0THREADCTXDATA_MAGIC;
315 pCtxData->hThreadCtx = hThreadCtx;
316 pCtxData->fPreemptingSuccess = false;
317 pCtxData->fPreemptingInvoked = false;
318 pCtxData->fResumedInvoked = false;
319 pCtxData->fResumedSuccess = false;
320 pCtxData->hSourceThread = RTThreadNativeSelf();
321 RT_ZERO(pCtxData->achResult);
322
323 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
324 RTThreadPreemptDisable(&PreemptState);
325 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
326
327 pCtxData->uSourceCpuId = RTMpCpuId();
328
329 rc = RTThreadCtxHooksRegister(hThreadCtx, &tstR0ThreadCtxHook, pCtxData);
330 if (RT_FAILURE(rc))
331 {
332 RTMemFree(pCtxData);
333 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksRegister returns %Rrc", rc);
334 break;
335 }
336
337 RTThreadPreemptRestore(&PreemptState);
338
339 /* Check if the preempting callback has/will been invoked. */
340 const uint32_t cMsTimeout = 8000;
341 const uint32_t cMsSleepGranularity = 50;
342 uint32_t cMsSlept = 0;
343 for (;;)
344 {
345 RTThreadPreemptDisable(&PreemptState);
346 const RTCPUID uCurrentCpuId = RTMpCpuId();
347 RTThreadPreemptRestore(&PreemptState);
348
349 if ( pCtxData->uSourceCpuId != uCurrentCpuId
350 || cMsSlept >= cMsTimeout)
351 {
352 break;
353 }
354
355 RTThreadSleep(cMsSleepGranularity);
356 cMsSlept += cMsSleepGranularity;
357 }
358
359 if (!ASMAtomicReadBool(&pCtxData->fPreemptingInvoked))
360 {
361 RTStrPrintf(pszErr, cchErr, "!tstR0ThreadCtxHooks[RTTHREADCTXEVENT_PREEMPTING] not invoked after ca. %u ms",
362 cMsSlept);
363 }
364 else if (!pCtxData->fPreemptingSuccess)
365 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
366 else
367 {
368 /* Preempting callback succeeded, now check if the resumed callback has/will been invoked. */
369 cMsSlept = 0;
370 for (;;)
371 {
372 if ( ASMAtomicReadBool(&pCtxData->fResumedInvoked)
373 || cMsSlept >= cMsTimeout)
374 {
375 break;
376 }
377
378 RTThreadSleep(cMsSleepGranularity);
379 cMsSlept += cMsSleepGranularity;
380 }
381
382 if (!ASMAtomicReadBool(&pCtxData->fResumedInvoked))
383 {
384 RTStrPrintf(pszErr, cchErr, "!tstR0ThreadCtxHooks[RTTHREADCTXEVENT_RESUMED] not invoked after ca. %u ms",
385 cMsSlept);
386 }
387 else if (!pCtxData->fResumedSuccess)
388 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
389 }
390
391 RTThreadCtxHooksDeregister(hThreadCtx);
392
393 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
394 uint32_t cRefs = RTThreadCtxHooksRelease(hThreadCtx);
395 if (cRefs == UINT32_MAX)
396 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksRelease returns invalid cRefs!");
397
398 RTMemFree(pCtxData);
399 break;
400 }
401
402 default:
403 RTStrPrintf(pszErr, cchErr, "!Unknown test #%d", uOperation);
404 break;
405 }
406
407 /* The error indicator is the '!' in the message buffer. */
408 return VINF_SUCCESS;
409}
410
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