|
1 // Copyright (c) 1995-2009 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 // e32\euser\cbase\ub_svr.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include "ub_std.h" |
|
19 //#define __DEBUG_IMAGE__ 1 |
|
20 #if defined(__DEBUG_IMAGE__) && defined (__EPOC32__) |
|
21 #include "e32svr.h" |
|
22 #define __IF_DEBUG(t) {RDebug debug;debug.t;} |
|
23 #else |
|
24 #define __IF_DEBUG(t) |
|
25 #endif |
|
26 |
|
27 |
|
28 |
|
29 _LIT(KSessionPanicCategory,"CSession"); |
|
30 _LIT(KServerPanicCategory,"CServer"); |
|
31 |
|
32 |
|
33 |
|
34 /** |
|
35 Default constructor. |
|
36 |
|
37 This constructor is empty. |
|
38 */ |
|
39 EXPORT_C CSession2::CSession2() |
|
40 {} |
|
41 |
|
42 |
|
43 |
|
44 /** |
|
45 Destructor. |
|
46 |
|
47 It frees resources prior to destruction of the object. |
|
48 Specifically, it removes this session object from the server |
|
49 active object’s list of sessions. |
|
50 */ |
|
51 EXPORT_C CSession2::~CSession2() |
|
52 { |
|
53 if (iLink.iNext) |
|
54 iLink.Deque(); |
|
55 } |
|
56 |
|
57 |
|
58 |
|
59 /** |
|
60 Completes construction of this server-side client session object. |
|
61 |
|
62 The function is called by the server active object, the CServer2 derived |
|
63 class instance, when a client makes a connection request. |
|
64 |
|
65 The connection request results in the creation of this session object, |
|
66 followed by a call to this function. |
|
67 |
|
68 The default implementation is empty. |
|
69 |
|
70 @see CServer2::NewSessionL() |
|
71 */ |
|
72 EXPORT_C void CSession2::CreateL() |
|
73 { |
|
74 } |
|
75 |
|
76 /** |
|
77 Designates the (master or slave) server that is to service this session. |
|
78 |
|
79 This may be called from the NewSessionL() method of the CServer2-derived |
|
80 class or from the CreateL() method of the CSession2-derived class. |
|
81 |
|
82 It must not be called after CreateL() has finished, as this is the point |
|
83 at which the session binding become irrevocable. |
|
84 |
|
85 @see CServer::DoConnect() |
|
86 */ |
|
87 EXPORT_C void CSession2::SetServer(const CServer2* aServer) |
|
88 { |
|
89 ASSERT(iLink.iNext == NULL); |
|
90 ASSERT(aServer); |
|
91 iServer = aServer; |
|
92 } |
|
93 |
|
94 /** |
|
95 Handles the situation when a call to CSession2::ServiceL(), which services |
|
96 a client request, leaves. |
|
97 |
|
98 Servers are active objects, and the call to CSession2::ServiceL() to handle |
|
99 a client request is executed as part of the server's active object RunL() |
|
100 function. If the RunL() leaves, the active object framework calls the active |
|
101 object's RunError() function. The server framework implements this as a call |
|
102 to ServiceError() |
|
103 |
|
104 The default behaviour of this function is to complete the message, using |
|
105 the leave value, if it has not already been completed. |
|
106 |
|
107 Servers can re-implement this as appropriate. |
|
108 |
|
109 @param aMessage The message containing the details of the client request that |
|
110 caused the leave. |
|
111 @param aError The leave code. |
|
112 |
|
113 @see CActive::RunL() |
|
114 @see CActive::RunError() |
|
115 */ |
|
116 EXPORT_C void CSession2::ServiceError(const RMessage2& aMessage,TInt aError) |
|
117 { |
|
118 if(!aMessage.IsNull()) |
|
119 aMessage.Complete(aError); |
|
120 } |
|
121 |
|
122 |
|
123 |
|
124 /** |
|
125 Called by a server when it receives a disconnect message for the session. |
|
126 |
|
127 This message is sent by the kernel when all client handles to the session have |
|
128 been closed. |
|
129 This method deletes the session object and completes the disconnect message. |
|
130 |
|
131 A derived session implementation may overide this virtual method if it needs to |
|
132 perform any asynchronous cleanup actions, these actions must end with a call to the |
|
133 base class implementation of this method, which will delete the session object |
|
134 and complete the disconnect message |
|
135 |
|
136 @param aMessage The disconnect message. |
|
137 |
|
138 @post 'this' session object has been deleted. |
|
139 */ |
|
140 EXPORT_C void CSession2::Disconnect(const RMessage2& aMessage) |
|
141 { |
|
142 delete this; |
|
143 aMessage.Complete(0); |
|
144 } |
|
145 |
|
146 |
|
147 |
|
148 /** |
|
149 Marks the start of resource count checking. |
|
150 |
|
151 It sets up a starting value for resource count checking. |
|
152 |
|
153 The function sets up a starting value for resource count checking by using |
|
154 the value returned by a call to CSession2::CountResources(), and is the value |
|
155 that will be used for comparison if CSession2::ResourceCountMarkEnd() is called |
|
156 at some later time. |
|
157 |
|
158 The client/server framework does not call this function (nor |
|
159 does it call CSession2::ResourceCountMarkEnd()), but is available for servers |
|
160 to use, if appropriate. |
|
161 |
|
162 @see CSession2::CountResources() |
|
163 @see CSession2::ResourceCountMarkEnd() |
|
164 */ |
|
165 EXPORT_C void CSession2::ResourceCountMarkStart() |
|
166 { |
|
167 iResourceCountMark=CountResources(); |
|
168 } |
|
169 |
|
170 |
|
171 |
|
172 /** |
|
173 Marks the end of resource count checking. |
|
174 |
|
175 The function takes the current resource count by |
|
176 calling CSession2::CountResources(), and compares it with the resource count |
|
177 value saved when CSession2::ResourceCountMarkStart() was called. |
|
178 If the resource counts differ, then the client thread is panicked (CSession 2)". |
|
179 |
|
180 The client/server framework does not call this function (nor does it call |
|
181 CSession2::ResourceCountMarkStart()), but the function is available for |
|
182 servers to use, if appropriate. |
|
183 |
|
184 @param aMessage Represents the details of the client request that is requesting |
|
185 this resource check. |
|
186 |
|
187 @see CSession2::CountResources() |
|
188 @see CSession2::ResourceCountMarkStart() |
|
189 */ |
|
190 EXPORT_C void CSession2::ResourceCountMarkEnd(const RMessage2& aMessage) |
|
191 { |
|
192 if (iResourceCountMark!=CountResources()) |
|
193 aMessage.Panic(KSessionPanicCategory,ESesFoundResCountHeaven); |
|
194 } |
|
195 |
|
196 |
|
197 |
|
198 /** |
|
199 Gets the number of resources currently in use. |
|
200 |
|
201 Derived classes must provide a suitable implementation. |
|
202 The meaning of a resource depends on the design intent of the server. |
|
203 |
|
204 The default implementation panics the calling thread (CSession 1) |
|
205 before returning KErrGeneral. |
|
206 |
|
207 @return The current number of resources in use. |
|
208 |
|
209 @see CSession2::ResourceCountmarkStart() |
|
210 @see CSession2::ResourceCountmarkEnd() |
|
211 */ |
|
212 EXPORT_C TInt CSession2::CountResources() |
|
213 { |
|
214 User::Panic(KSessionPanicCategory,ESesCountResourcesNotImplemented); |
|
215 return KErrGeneral; |
|
216 } |
|
217 |
|
218 |
|
219 |
|
220 /** |
|
221 Extension function |
|
222 |
|
223 |
|
224 */ |
|
225 EXPORT_C TInt CSession2::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) |
|
226 { |
|
227 return CBase::Extension_(aExtensionId, a0, a1); |
|
228 } |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 /** |
|
234 Constructs the server object, specifying the server type and the active |
|
235 object priority. |
|
236 |
|
237 Derived classes must define and implement a constructor through which |
|
238 the priority can be specified. A typical implementation calls this server |
|
239 base class constructor through a constructor initialization list. |
|
240 |
|
241 @param aPriority The priority of this active object. |
|
242 @param aType Indicates the type of session that the server creates. |
|
243 If not explicitly stated, then the server creates |
|
244 a session that is not sharable with other threads. |
|
245 */ |
|
246 EXPORT_C CServer2::CServer2(TInt aPriority, TServerType aType) |
|
247 : CActive(aPriority), |
|
248 iSessionType((TUint8)aType), |
|
249 iSessionQ(_FOFF(CSession2,iLink)), |
|
250 iSessionIter(iSessionQ) |
|
251 { |
|
252 ASSERT(iSessionType == aType); |
|
253 __ASSERT_COMPILE(EServerRole_Default == 0); |
|
254 __ASSERT_COMPILE(EServerOpt_PinClientDescriptorsDefault == 0); |
|
255 } |
|
256 |
|
257 |
|
258 |
|
259 /** |
|
260 Frees resources prior to destruction. |
|
261 |
|
262 Specifically, it cancels any outstanding request for messages, and |
|
263 deletes all server-side client session objects. |
|
264 */ |
|
265 EXPORT_C CServer2::~CServer2() |
|
266 { |
|
267 Cancel(); |
|
268 while (!iSessionQ.IsEmpty()) |
|
269 { |
|
270 CSession2 *pS=iSessionQ.First(); |
|
271 pS->iLink.Deque(); |
|
272 pS->iLink.iNext=0; |
|
273 delete pS; |
|
274 } |
|
275 iServer.Close(); |
|
276 } |
|
277 |
|
278 |
|
279 /** |
|
280 Sets whether the kernel will pin descriptors passed to this server in the context of the client |
|
281 thread. |
|
282 |
|
283 Setting this is one way of ensuring that the server will not take page faults when accessing client |
|
284 descriptors, which would otherwise happen if the data was paged out. |
|
285 |
|
286 This method overrides the default pinning policy of the server which is for the server |
|
287 to pin its client's descriptors if the process creating the server is not data paged. |
|
288 I.e. if CServer2::SetPinClientDescriptors() is not invoked on the server and |
|
289 RProcess::DefaultDataPaged() of the process creating the server returns EFalse, |
|
290 the server will pin its client's descriptors, otherwise the server will not pin its |
|
291 client's descriptors. |
|
292 |
|
293 This method must be called prior to starting the server by calling the Start() method. |
|
294 |
|
295 @param aPin Set to ETrue for the server to pin its client's descriptors, set to |
|
296 EFalse otherwise. |
|
297 @panic E32USER-CBase 106 When this method is invoked after the server has been started. |
|
298 @see CServer2::Start() |
|
299 |
|
300 @prototype |
|
301 */ |
|
302 EXPORT_C void CServer2::SetPinClientDescriptors(TBool aPin) |
|
303 { |
|
304 if (iServer.Handle() != KNullHandle) |
|
305 {// Server already started so too late to make it a pinning one. |
|
306 Panic(ECServer2InvalidSetPin); |
|
307 } |
|
308 iServerOpts &= ~EServerOpt_PinClientDescriptorsMask; |
|
309 if (aPin) |
|
310 iServerOpts |= EServerOpt_PinClientDescriptorsEnable; |
|
311 else |
|
312 iServerOpts |= EServerOpt_PinClientDescriptorsDisable; |
|
313 } |
|
314 |
|
315 /** |
|
316 Assigns a role (master or slave) for this server. |
|
317 |
|
318 The master server is typically named, and receives all Connect messages |
|
319 from clients. It can hand off some sessions to be processed by one or |
|
320 more anonymous slave servers, each running in a separate thread. |
|
321 |
|
322 Both master and slave servers must call this function before calling |
|
323 Start(), in order to define their roles. Once the server is started, |
|
324 its role cannot be changed. |
|
325 |
|
326 @panic E32USER-CBase-? When this method is invoked after the server has been started. |
|
327 @see CServer2::Start() |
|
328 |
|
329 @prototype |
|
330 */ |
|
331 EXPORT_C void CServer2::SetMaster(const CServer2* aServer) |
|
332 { |
|
333 // Roles can only be assigned before the call to Start() |
|
334 ASSERT(Server().Handle() == KNullHandle); |
|
335 |
|
336 if (aServer == NULL) |
|
337 iServerRole = EServerRole_Standalone; |
|
338 else if (aServer == this) |
|
339 iServerRole = EServerRole_Master; |
|
340 else |
|
341 iServerRole = EServerRole_Slave; |
|
342 } |
|
343 |
|
344 |
|
345 /** |
|
346 Adds the server with the specified name to the active scheduler, |
|
347 and issues the first request for messages. |
|
348 |
|
349 If KNullDesC is specified for the name, then an anonymous server will be created. |
|
350 To create a session to such a server, an overload of RSessionBase::CreateSession() |
|
351 which takes RServer2 object as a parameter can be used. |
|
352 |
|
353 @param aName The name of the server. |
|
354 KNullDesC, to create anonymous servers. |
|
355 @return KErrNone, if successful, otherwise one of the other system wide error codes. |
|
356 |
|
357 @capability ProtServ if aName starts with a '!' character |
|
358 */ |
|
359 EXPORT_C TInt CServer2::Start(const TDesC& aName) |
|
360 { |
|
361 TInt r = iServer.CreateGlobal(aName, iSessionType, iServerRole, iServerOpts); |
|
362 if (r == KErrNone) |
|
363 { |
|
364 CActiveScheduler::Add(this); |
|
365 ReStart(); |
|
366 } |
|
367 return r; |
|
368 } |
|
369 |
|
370 |
|
371 |
|
372 /** |
|
373 Adds the server with the specified name to the active scheduler, |
|
374 and issues the first request for messages, and leaves if the operation fails. |
|
375 |
|
376 If KNullDesC is specified for the name, then an anonymous server will be created. |
|
377 To create a session to such a server, the overload of RSessionBase::CreateSession() |
|
378 which takes an RServer2 object as a parameter can be used. |
|
379 |
|
380 @param aName The name of the server. |
|
381 KNullDesC, to create anonymous servers. |
|
382 @capability ProtServ if aName starts with a '!' character |
|
383 */ |
|
384 EXPORT_C void CServer2::StartL(const TDesC& aName) |
|
385 { |
|
386 User::LeaveIfError(Start(aName)); |
|
387 } |
|
388 |
|
389 |
|
390 |
|
391 /** |
|
392 Implements the cancellation of any outstanding request for messages. |
|
393 */ |
|
394 EXPORT_C void CServer2::DoCancel() |
|
395 { |
|
396 |
|
397 iServer.Cancel(); |
|
398 } |
|
399 |
|
400 |
|
401 |
|
402 void CServer2::Connect(const RMessage2& aMessage) |
|
403 // |
|
404 // Handle a connect request. Ptr0()==Version. |
|
405 // NOTE: We don't want this to leave as that may kill the server |
|
406 // |
|
407 { |
|
408 |
|
409 if (aMessage.Session()) |
|
410 { |
|
411 aMessage.Panic(KServerPanicCategory,ESessionAlreadyConnected); |
|
412 return; |
|
413 } |
|
414 DoConnect(aMessage); |
|
415 } |
|
416 |
|
417 |
|
418 |
|
419 // |
|
420 //This is all of the Leaving code for connection creation. |
|
421 //This is in a seperate function in an effort to force compilers to store aSession |
|
422 //on the stack which enables cleanup to perform correctly when a Leave occurs |
|
423 // |
|
424 void CServer2::DoConnectL(const RMessage2& aMessage,CSession2* volatile& aSession) |
|
425 { |
|
426 TVersion v; |
|
427 *(TInt*)&v = aMessage.Int0(); |
|
428 aSession = NewSessionL(v, aMessage); |
|
429 if (!aSession->iServer) |
|
430 aSession->iServer = this; |
|
431 aSession->CreateL(); |
|
432 iSessionQ.AddLast(*aSession); |
|
433 Exec::SetSessionPtr(aMessage.Handle(), aSession); |
|
434 } |
|
435 |
|
436 |
|
437 |
|
438 /** |
|
439 Handles the connect request from the client. We trap Leaves, to ensure |
|
440 that existing sessions aren't affected by failure to create a new one. |
|
441 |
|
442 @param aMessage The Connect message sent by the client requesting the |
|
443 connection. aMessage.Ptr0() is the required Version. |
|
444 */ |
|
445 EXPORT_C void CServer2::DoConnect(const RMessage2& aMessage) |
|
446 { |
|
447 ASSERT(aMessage.Function() == RMessage2::EConnect); |
|
448 ASSERT(aMessage.Session() == NULL); |
|
449 ASSERT(!aMessage.IsNull()); |
|
450 |
|
451 CSession2* newSession = NULL; |
|
452 TRAPD(err, DoConnectL(aMessage, newSession)); |
|
453 if (err != KErrNone) |
|
454 { |
|
455 // Connect failed |
|
456 delete newSession; |
|
457 aMessage.Complete(err); |
|
458 } |
|
459 else |
|
460 { |
|
461 ASSERT(newSession != NULL); |
|
462 CServer2* sessionServer = const_cast<CServer2*>(newSession->Server()); |
|
463 ASSERT(sessionServer != NULL); |
|
464 |
|
465 // The return value of Server() will be 'this', unless it was |
|
466 // changed by a call to SetServer(). |
|
467 if (sessionServer == this) |
|
468 { |
|
469 // no SetServer() call, so just complete the Connect message |
|
470 aMessage.Complete(err); |
|
471 } |
|
472 else |
|
473 { |
|
474 // Transfer the new Csession to the specified slave Cserver |
|
475 newSession->iLink.Deque(); |
|
476 sessionServer->iSessionQ.AddLast(*newSession); |
|
477 |
|
478 // Ask the kernel to transfer the DSession to the slave DServer. |
|
479 // Note: this Exec call also completes the Connect message. |
|
480 TInt msgHandle = aMessage.iHandle; |
|
481 const_cast<TInt&>(aMessage.iHandle) = 0; |
|
482 ASSERT(msgHandle); |
|
483 Exec::TransferSession(msgHandle, sessionServer->Server().Handle()); |
|
484 } |
|
485 } |
|
486 |
|
487 ASSERT(aMessage.IsNull()); |
|
488 } |
|
489 |
|
490 |
|
491 |
|
492 /** |
|
493 Handles the situation where a call to CServer2::RunL(), leaves. |
|
494 |
|
495 This is the server active object's implementation of the active object |
|
496 framework's RunError() function. |
|
497 |
|
498 In practice, the leave can only be caused by a session's ServiceL() function, |
|
499 which is called from this RunL(); this error is reflected back to that session |
|
500 by calling its ServiceError() function. |
|
501 |
|
502 @param aError The leave code. |
|
503 |
|
504 @return KErrNone. |
|
505 |
|
506 @see CActive::RunL() |
|
507 @see CActive::RunError() |
|
508 @see CSession2::ServiceError() |
|
509 */ |
|
510 EXPORT_C TInt CServer2::RunError(TInt aError) |
|
511 { |
|
512 Message().Session()->ServiceError(Message(),aError); |
|
513 if (!IsActive()) |
|
514 ReStart(); |
|
515 return KErrNone; |
|
516 } |
|
517 |
|
518 |
|
519 |
|
520 /** |
|
521 Restarts the server. |
|
522 |
|
523 The function issues a request for messages. |
|
524 */ |
|
525 EXPORT_C void CServer2::ReStart() |
|
526 { |
|
527 |
|
528 iServer.Receive(iMessage,iStatus); |
|
529 SetActive(); |
|
530 } |
|
531 |
|
532 |
|
533 |
|
534 #ifndef __CSERVER_MACHINE_CODED__ |
|
535 /** |
|
536 Handles the receipt of a message. |
|
537 */ |
|
538 EXPORT_C void CServer2::RunL() |
|
539 { |
|
540 TInt fn = Message().Function(); |
|
541 |
|
542 if(fn>=0) |
|
543 { |
|
544 // Service the message |
|
545 CSession2* session=Message().Session(); |
|
546 if(session) |
|
547 session->ServiceL(Message()); |
|
548 else |
|
549 NotConnected(Message()); |
|
550 } |
|
551 else if(fn==RMessage2::EConnect) |
|
552 { |
|
553 Connect(Message()); |
|
554 } |
|
555 else if(fn==RMessage2::EDisConnect) |
|
556 { |
|
557 Disconnect(Message()); |
|
558 } |
|
559 else |
|
560 { |
|
561 BadMessage(Message()); |
|
562 } |
|
563 // Queue reception of next message if it hasn't already been done |
|
564 if(!IsActive()) |
|
565 ReStart(); |
|
566 } |
|
567 |
|
568 #endif |
|
569 |
|
570 |
|
571 |
|
572 void CServer2::Disconnect(const RMessage2& aMessage) |
|
573 // |
|
574 // Process a disconnect message |
|
575 // |
|
576 { |
|
577 CSession2* session=Message().Session(); |
|
578 if(!session) |
|
579 { |
|
580 // Session not created yet, so just complete message. |
|
581 aMessage.Complete(0); |
|
582 return; |
|
583 } |
|
584 session->Disconnect(aMessage); |
|
585 } |
|
586 |
|
587 |
|
588 |
|
589 void CServer2::BadMessage(const RMessage2& aMessage) |
|
590 { |
|
591 aMessage.Panic(KServerPanicCategory,EBadMessageNumber); |
|
592 } |
|
593 |
|
594 |
|
595 |
|
596 void CServer2::NotConnected(const RMessage2& aMessage) |
|
597 { |
|
598 aMessage.Panic(KServerPanicCategory,ESessionNotConnected); |
|
599 } |
|
600 |
|
601 |
|
602 |
|
603 /** |
|
604 Extension function |
|
605 |
|
606 |
|
607 */ |
|
608 EXPORT_C TInt CServer2::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) |
|
609 { |
|
610 return CActive::Extension_(aExtensionId, a0, a1); |
|
611 } |