|
1 // Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // e32test\active\t_schedrace.cpp |
|
15 // Overview: |
|
16 // Test for race conditions in emulator scheduler |
|
17 // API Information: |
|
18 // RMessage2, RMessagePtr2, RSessionBase, CSession2, CServer2, CPeriodic |
|
19 // Details: |
|
20 // - The client and server shuttle messages back and forth with variable timing |
|
21 // - The client also has a periodic timer, which fires at a random phase relative to the IPC |
|
22 // - The client should have higher priority than the server |
|
23 // - The race may occur when the timer interrupt preempts kernel code and disables interrupts |
|
24 // - The "preempted" code nonetheless runs and sees the "impossible" disabled interrupts |
|
25 // - The result is a precondition failure at a random point |
|
26 // Platforms/Drives/Compatibility: |
|
27 // Will run on all platforms, but precondition failure is expected only on the UDEB emulator. |
|
28 // Assumptions/Requirement/Pre-requisites: |
|
29 // Failures and causes: |
|
30 // Base Port information: |
|
31 |
|
32 #define __E32TEST_EXTENSION__ |
|
33 |
|
34 #include <e32std.h> |
|
35 #include <e32std_private.h> |
|
36 #include <e32math.h> |
|
37 #include <e32panic.h> |
|
38 #include <e32svr.h> |
|
39 #include <e32test.h> |
|
40 #include <e32ver.h> |
|
41 |
|
42 _LIT(KServerName, "CTestServer"); |
|
43 const TVersion version(KE32MajorVersionNumber, |
|
44 KE32MinorVersionNumber, |
|
45 KE32BuildVersionNumber); |
|
46 |
|
47 // Globals for counting number of times each thread/object runs |
|
48 TInt nIdleCalls, nTimerCalls, nServerCalls; |
|
49 |
|
50 |
|
51 |
|
52 // |
|
53 // Server classes and code ... |
|
54 // |
|
55 class CTestServer : public CServer2 |
|
56 { |
|
57 public: |
|
58 IMPORT_C CTestServer(RTest* aTest) : CServer2(EPriorityIdle), iTest(aTest) {}; |
|
59 protected: |
|
60 IMPORT_C CSession2* NewSessionL(const TVersion& aVersion, const RMessage2& aMessage) const; |
|
61 public: |
|
62 RTest* iTest; |
|
63 }; |
|
64 |
|
65 class CTestSession : public CSession2 |
|
66 { |
|
67 public: |
|
68 IMPORT_C void ServiceL(const RMessage2& aMessage); |
|
69 enum Action |
|
70 { |
|
71 EStop = 0, |
|
72 EDelay |
|
73 }; |
|
74 public: |
|
75 RTest* iTest; |
|
76 }; |
|
77 |
|
78 // |
|
79 // CTestServer functions |
|
80 // |
|
81 EXPORT_C CSession2* CTestServer::NewSessionL(const TVersion& aVersion, const RMessage2& /*aMessage*/) const |
|
82 { |
|
83 if (User::QueryVersionSupported(version, aVersion) == EFalse) |
|
84 User::Leave(KErrNotSupported); |
|
85 |
|
86 CTestSession* newCTestSession = new CTestSession; |
|
87 if (newCTestSession == NULL) |
|
88 User::Leave(KErrNoMemory); |
|
89 newCTestSession->iTest = iTest; |
|
90 |
|
91 return newCTestSession; |
|
92 } |
|
93 |
|
94 // |
|
95 // CTestSession functions |
|
96 // |
|
97 EXPORT_C void CTestSession::ServiceL(const RMessage2& aMessage) |
|
98 { |
|
99 if ((++nServerCalls & 0x03ff) == 10000) // never true, to suppress message |
|
100 iTest->Printf(_L("S:calls: I=%-7d S=%-7d T=%-7d D=%-7d\n"), |
|
101 nIdleCalls, nServerCalls, nTimerCalls, aMessage.Int0()); |
|
102 TInt r = KErrNone; |
|
103 TInt i = 0; |
|
104 |
|
105 switch (aMessage.Function()) |
|
106 { |
|
107 case EStop: |
|
108 CActiveScheduler::Stop(); |
|
109 break; |
|
110 |
|
111 case EDelay: |
|
112 // Spin a bit before replying |
|
113 // The compiler can't optimise this away because 'i' is used below ... |
|
114 for (i = 0; i < aMessage.Int0(); ++i) |
|
115 ; |
|
116 break; |
|
117 |
|
118 default: |
|
119 r = KErrNotSupported; |
|
120 break; |
|
121 } |
|
122 |
|
123 if (i != -1) // always true :) |
|
124 aMessage.Complete(r); |
|
125 } |
|
126 |
|
127 // |
|
128 // Thread to run the server code |
|
129 // |
|
130 TInt ServerThread(TAny*) |
|
131 { |
|
132 RTest test(_L("T_SCHEDRACE server")); |
|
133 test.Title(); |
|
134 |
|
135 // UserSvr::FsRegisterThread(); |
|
136 |
|
137 test.Start(_L("Create and install ActiveScheduler")); |
|
138 CActiveScheduler* pScheduler = new CActiveScheduler; |
|
139 test_NotNull(pScheduler); |
|
140 CActiveScheduler::Install(pScheduler); |
|
141 |
|
142 test.Next(_L("Creating and starting Server")); |
|
143 CTestServer* pServer = new CTestServer(&test); |
|
144 test_NotNull(pServer); |
|
145 test_KErrNone(pServer->Start(KServerName)); // Starting a CServer2 also Adds it to the ActiveScheduler |
|
146 |
|
147 test.Next(_L("Rendezvous with main thread, then Start ActiveScheduler")); |
|
148 RThread self; |
|
149 self.Rendezvous(KErrNone); |
|
150 test.Printf(_L(" There might be something going on beneath this window\n")); |
|
151 CActiveScheduler::Start(); |
|
152 |
|
153 // This code is not reached until the active scheduler exits. |
|
154 test.Next(_L("Destroy Server and ActiveScheduler")); |
|
155 delete pServer; |
|
156 delete pScheduler; |
|
157 test.Close(); |
|
158 return (KErrNone); |
|
159 } |
|
160 |
|
161 |
|
162 |
|
163 // |
|
164 // Client classes and code ... |
|
165 // |
|
166 class RTestSession : public RSessionBase |
|
167 { |
|
168 public: |
|
169 RTestSession(RTest* aTest, TInt aTicks) : iTest(aTest), iMax(aTicks) |
|
170 { |
|
171 iDelay = iMax; |
|
172 iDelta = -1; |
|
173 }; |
|
174 TInt Connect(const TDesC& aServer, int aSlots) |
|
175 { |
|
176 return RSessionBase::CreateSession(aServer, version, aSlots); |
|
177 }; |
|
178 TInt SendBlind(TInt aFunction, const TIpcArgs& aArgs) const |
|
179 { |
|
180 return RSessionBase::Send(aFunction, aArgs); |
|
181 }; |
|
182 TInt SendSync(TInt aFunction, const TIpcArgs& aArgs) const |
|
183 { |
|
184 return RSessionBase::SendReceive(aFunction, aArgs); |
|
185 }; |
|
186 void SendAsync(TInt aFunction, const TIpcArgs& aArgs, TRequestStatus& aStatus) const |
|
187 { |
|
188 RSessionBase::SendReceive(aFunction, aArgs, aStatus); |
|
189 }; |
|
190 public: |
|
191 RTest* iTest; |
|
192 TInt iMax; |
|
193 TInt iDelay; |
|
194 TInt iDelta; |
|
195 }; |
|
196 |
|
197 RTestSession* TheSession; |
|
198 |
|
199 class CIdler : public CIdle |
|
200 { |
|
201 public: |
|
202 CIdler() : CIdle(EPriorityIdle) |
|
203 { |
|
204 CActiveScheduler::Add(this); |
|
205 }; |
|
206 void Callback(); |
|
207 }; |
|
208 |
|
209 void CIdler::Callback() |
|
210 { |
|
211 if ((++nIdleCalls & 0x03ff) == 10000) // never true, to suppress message |
|
212 TheSession->iTest->Printf(_L("I:calls: I=%-7d S=%-7d T=%-7d D=%-7d\n"), |
|
213 nIdleCalls, nServerCalls, nTimerCalls, TheSession->iDelay); |
|
214 // TInt delay = Math::Random() & 0xffff; // up to ~64ms |
|
215 TheSession->SendBlind(CTestSession::EDelay, TIpcArgs(TheSession->iDelay)); |
|
216 TheSession->SendAsync(CTestSession::EDelay, TIpcArgs(0), iStatus); |
|
217 SetActive(); |
|
218 } |
|
219 |
|
220 // |
|
221 // CIdler callback wrapper |
|
222 // |
|
223 TInt IdleCallback(TAny* aPtr) |
|
224 { |
|
225 ((CIdler*)aPtr)->Callback(); |
|
226 return EFalse; |
|
227 } |
|
228 |
|
229 // |
|
230 // CPeriodic callback |
|
231 // |
|
232 TInt TimerCallback(TAny*) |
|
233 { |
|
234 if ((++nTimerCalls & 0x003f) == 1) // true in one out of 64 cycles |
|
235 TheSession->iTest->Printf(_L("T:calls: I=%-7d S=%-7d T=%-7d D=%-7d\n"), |
|
236 nIdleCalls, nServerCalls, nTimerCalls, TheSession->iDelay); |
|
237 if (TheSession->iDelay >= TheSession->iMax) |
|
238 TheSession->iDelta = -1; |
|
239 // if (TheSession->iDelay < 1) |
|
240 // TheSession->iDelta = 1; |
|
241 TheSession->iDelay += TheSession->iDelta; |
|
242 if (TheSession->iDelay < 0) |
|
243 CActiveScheduler::Stop(); |
|
244 return ETrue; |
|
245 } |
|
246 |
|
247 // |
|
248 // Thread to run the client code |
|
249 // |
|
250 TInt ClientThread(TAny*) |
|
251 { |
|
252 RTest test(_L("T_SCHEDRACE client")); |
|
253 test.Title(); |
|
254 |
|
255 // UserSvr::FsRegisterThread(); |
|
256 |
|
257 test.Start(_L("Create Session")); |
|
258 TheSession = new RTestSession(&test, 5*60*64); // will run for 5 minutes |
|
259 test_NotNull(TheSession); |
|
260 test_KErrNone(TheSession->Connect(KServerName, 2)); |
|
261 |
|
262 test.Start(_L("Create and install ActiveScheduler")); |
|
263 CActiveScheduler* pScheduler = new CActiveScheduler; |
|
264 test_NotNull(pScheduler); |
|
265 CActiveScheduler::Install(pScheduler); |
|
266 |
|
267 test.Next(_L("Create timer and idle task")); |
|
268 CPeriodic* pTimer = CPeriodic::New(CActive::EPriorityStandard); |
|
269 test_NotNull(pTimer); |
|
270 CIdler* pIdler = new CIdler(); |
|
271 test_NotNull(pIdler); |
|
272 |
|
273 test.Next(_L("Rendezvous with main thread")); |
|
274 RThread self; |
|
275 self.Rendezvous(KErrNone); |
|
276 |
|
277 test.Next(_L("Start idle task, timer, and active scheduler")); |
|
278 pIdler->Start(TCallBack(IdleCallback, pIdler)); |
|
279 pTimer->Start(1000000, 1000, TCallBack(TimerCallback, pTimer)); |
|
280 test.Printf(_L(" There might be something going on beneath this window\n")); |
|
281 CActiveScheduler::Start(); |
|
282 |
|
283 // This code is not reached until the active scheduler exits. |
|
284 TheSession->SendSync(CTestSession::EStop, TIpcArgs()); |
|
285 TheSession->Close(); |
|
286 test.Next(_L("Destroy Idle task, Timer, and ActiveScheduler")); |
|
287 delete pIdler; |
|
288 delete pTimer; |
|
289 delete pScheduler; |
|
290 test.Close(); |
|
291 return (KErrNone); |
|
292 } |
|
293 |
|
294 |
|
295 // |
|
296 // Main program ... |
|
297 // |
|
298 GLDEF_C TInt E32Main() |
|
299 { |
|
300 RTest test(_L("Main T_SCHEDRACE test")); |
|
301 test.Title(); |
|
302 |
|
303 test.Start(_L("Create server and client threads")); |
|
304 const TInt KHeapMinSize = 0x1000; |
|
305 const TInt KHeapMaxSize = 0x1000; |
|
306 RThread serverThread, clientThread; |
|
307 test_KErrNone(serverThread.Create(_L("Server Thread"), ServerThread, KDefaultStackSize, KHeapMinSize, KHeapMaxSize, NULL)); |
|
308 test_KErrNone(clientThread.Create(_L("Client Thread"), ClientThread, KDefaultStackSize, KHeapMinSize, KHeapMaxSize, NULL)); |
|
309 |
|
310 TRequestStatus serverStat, clientStat; |
|
311 serverThread.Rendezvous(serverStat); |
|
312 clientThread.Rendezvous(clientStat); |
|
313 serverThread.SetPriority(EPriorityMuchLess); |
|
314 clientThread.SetPriority(EPriorityMore); |
|
315 |
|
316 test.Next(_L("Start the threads")); |
|
317 serverThread.Resume(); |
|
318 User::WaitForRequest(serverStat); |
|
319 test_KErrNone(serverStat.Int()); |
|
320 clientThread.Resume(); |
|
321 User::WaitForRequest(clientStat); |
|
322 test_KErrNone(clientStat.Int()); |
|
323 |
|
324 test.Next(_L("Wait for the threads to stop")); |
|
325 serverThread.Logon(serverStat); |
|
326 clientThread.Logon(clientStat); |
|
327 User::WaitForRequest(clientStat); |
|
328 User::WaitForRequest(serverStat); |
|
329 |
|
330 switch (clientThread.ExitType()) |
|
331 { |
|
332 case EExitKill: |
|
333 test.Printf(_L(" Client thread killed\n")); |
|
334 break; |
|
335 |
|
336 case EExitTerminate: |
|
337 test.Printf(_L("!!Client thread terminated:")); |
|
338 test.Panic(clientThread.ExitCategory(), clientThread.ExitReason()); |
|
339 break; |
|
340 |
|
341 case EExitPanic: |
|
342 test.Panic(_L("!!Client thread panicked:")); |
|
343 test.Panic(clientThread.ExitCategory(), clientThread.ExitReason()); |
|
344 break; |
|
345 |
|
346 default: |
|
347 test.Panic(_L("!!Client thread did something bizarre"), clientThread.ExitReason()); |
|
348 break; |
|
349 } |
|
350 |
|
351 switch (serverThread.ExitType()) |
|
352 { |
|
353 case EExitKill: |
|
354 test.Printf(_L(" Server thread killed\n")); |
|
355 break; |
|
356 |
|
357 case EExitTerminate: |
|
358 test.Printf(_L("!!Server thread terminated:")); |
|
359 test.Panic(serverThread.ExitCategory(), serverThread.ExitReason()); |
|
360 break; |
|
361 |
|
362 case EExitPanic: |
|
363 // |
|
364 // To catch a panic put a breakpoint in User::Panic() (in UCDT\UC_UNC.CPP). |
|
365 // |
|
366 test.Printf(_L("!!Server thread panicked:")); |
|
367 test.Panic(serverThread.ExitCategory(), serverThread.ExitReason()); |
|
368 break; |
|
369 |
|
370 default: |
|
371 test.Panic(_L("!!Server thread did something bizarre"), serverThread.ExitReason()); |
|
372 break; |
|
373 } |
|
374 |
|
375 test.Next(_L("Close the threads")); |
|
376 CLOSE_AND_WAIT(serverThread); |
|
377 CLOSE_AND_WAIT(clientThread); |
|
378 test.End(); |
|
379 return (KErrNone); |
|
380 } |
|
381 |