VirtualBox

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

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

IPRT: Kernel thread-context hooks, linux implementation. Extended tstR0ThreadPreemption testcase to test the thread-context hooks.

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