VirtualBox

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

Last change on this file since 47705 was 47572, checked in by vboxsync, 11 years ago

Runtime/threadctxhooks: RTThreadCtxHooksAreRegistered().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/* $Id: tstR0ThreadPreemption.cpp 47572 2013-08-07 09:51:45Z 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 bool fRegistered = RTThreadCtxHooksAreRegistered(NIL_RTTHREADCTX);
302 if (fRegistered)
303 {
304 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksAreRegistered returns true before creating any hooks");
305 break;
306 }
307
308 RTTHREADCTX hThreadCtx;
309 int rc = RTThreadCtxHooksCreate(&hThreadCtx);
310 if (RT_FAILURE(rc))
311 {
312 if (rc == VERR_NOT_SUPPORTED)
313 RTStrPrintf(pszErr, cchErr, "RTThreadCtxHooksCreate returns VERR_NOT_SUPPORTED");
314 else
315 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksCreate returns %Rrc", rc);
316 break;
317 }
318
319 fRegistered = RTThreadCtxHooksAreRegistered(hThreadCtx);
320 if (fRegistered)
321 {
322 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksAreRegistered returns true before registering any hooks");
323 RTThreadCtxHooksRelease(hThreadCtx);
324 break;
325 }
326
327 PTSTRTR0THREADCTXDATA pCtxData = (PTSTRTR0THREADCTXDATA)RTMemAllocZ(sizeof(*pCtxData));
328 AssertReturn(pCtxData, VERR_NO_MEMORY);
329 pCtxData->u32Magic = TSTRTR0THREADCTXDATA_MAGIC;
330 pCtxData->hThreadCtx = hThreadCtx;
331 pCtxData->fPreemptingSuccess = false;
332 pCtxData->fPreemptingInvoked = false;
333 pCtxData->fResumedInvoked = false;
334 pCtxData->fResumedSuccess = false;
335 pCtxData->hSourceThread = RTThreadNativeSelf();
336 RT_ZERO(pCtxData->achResult);
337
338 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
339 RTThreadPreemptDisable(&PreemptState);
340 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
341
342 pCtxData->uSourceCpuId = RTMpCpuId();
343
344 rc = RTThreadCtxHooksRegister(hThreadCtx, &tstR0ThreadCtxHook, pCtxData);
345 if (RT_FAILURE(rc))
346 {
347 RTThreadPreemptRestore(&PreemptState);
348 RTMemFree(pCtxData);
349 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksRegister returns %Rrc", rc);
350 break;
351 }
352
353 fRegistered = RTThreadCtxHooksAreRegistered(hThreadCtx);
354 if (!fRegistered)
355 {
356 RTThreadPreemptRestore(&PreemptState);
357 RTMemFree(pCtxData);
358 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksAreRegistered return false when hooks are supposedly registered");
359 break;
360 }
361
362 RTThreadPreemptRestore(&PreemptState);
363
364 /* Check if the preempting callback has/will been invoked. */
365 const uint32_t cMsTimeout = 10000;
366 const uint32_t cMsSleepGranularity = 50;
367 uint32_t cMsSlept = 0;
368 RTCPUID uCurrentCpuId = NIL_RTCPUID;
369 for (;;)
370 {
371 RTThreadPreemptDisable(&PreemptState);
372 uCurrentCpuId = RTMpCpuId();
373 RTThreadPreemptRestore(&PreemptState);
374
375 if ( pCtxData->uSourceCpuId != uCurrentCpuId
376 || cMsSlept >= cMsTimeout)
377 {
378 break;
379 }
380
381 RTThreadSleep(cMsSleepGranularity);
382 cMsSlept += cMsSleepGranularity;
383 }
384
385 if (!ASMAtomicReadBool(&pCtxData->fPreemptingInvoked))
386 {
387 if (pCtxData->uSourceCpuId != uCurrentCpuId)
388 {
389 RTStrPrintf(pszErr, cchErr,
390 "!tstR0ThreadCtxHooks[RTTHREADCTXEVENT_PREEMPTING] not invoked before migrating from CPU %RU32 to %RU32",
391 pCtxData->uSourceCpuId, uCurrentCpuId);
392 }
393 else
394 {
395 RTStrPrintf(pszErr, cchErr, "!tstR0ThreadCtxHooks[RTTHREADCTXEVENT_PREEMPTING] not invoked after ca. %u ms",
396 cMsSlept);
397 }
398 }
399 else if (!pCtxData->fPreemptingSuccess)
400 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
401 else
402 {
403 /* Preempting callback succeeded, now check if the resumed callback has/will been invoked. */
404 cMsSlept = 0;
405 for (;;)
406 {
407 if ( ASMAtomicReadBool(&pCtxData->fResumedInvoked)
408 || cMsSlept >= cMsTimeout)
409 {
410 break;
411 }
412
413 RTThreadSleep(cMsSleepGranularity);
414 cMsSlept += cMsSleepGranularity;
415 }
416
417 if (!ASMAtomicReadBool(&pCtxData->fResumedInvoked))
418 {
419 RTStrPrintf(pszErr, cchErr, "!tstR0ThreadCtxHooks[RTTHREADCTXEVENT_RESUMED] not invoked after ca. %u ms",
420 cMsSlept);
421 }
422 else if (!pCtxData->fResumedSuccess)
423 RTStrCopy(pszErr, cchErr, pCtxData->achResult);
424 }
425
426 RTThreadCtxHooksDeregister(hThreadCtx);
427
428 fRegistered = RTThreadCtxHooksAreRegistered(hThreadCtx);
429 if (fRegistered)
430 {
431 RTMemFree(pCtxData);
432 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksAreRegistered return true when hooks are deregistered");
433 break;
434 }
435
436 Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
437 uint32_t cRefs = RTThreadCtxHooksRelease(hThreadCtx);
438 if (cRefs == UINT32_MAX)
439 RTStrPrintf(pszErr, cchErr, "!RTThreadCtxHooksRelease returns invalid cRefs!");
440
441 RTMemFree(pCtxData);
442 break;
443 }
444
445 default:
446 RTStrPrintf(pszErr, cchErr, "!Unknown test #%d", uOperation);
447 break;
448 }
449
450 /* The error indicator is the '!' in the message buffer. */
451 return VINF_SUCCESS;
452}
453
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