Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 31
Текст из файла (страница 31)
OnceExecHandler::ServerCreate() has created the kernel-side server132INTER-THREAD COMMUNICATIONobject, it opens a handle on the current (server) thread, so the pointer tothe server thread (iOwningThread) is always valid.This is required because a process-relative handle may be created tothe server object by any thread in the server’s process, using Duplicate() and hence the server objects may be held open after the serverthread terminates. The first handle to the server object that is createdand subsequently vested within the RServer2 will be a process-relativehandle if the server is anonymous (that is, has a zero-length name) andthread-relative otherwise. To exercise control over the use of Duplicate(), DServer over-rides DObject’s RequestUserHandle()method, which is called whenever a user-mode thread wishes to create ahandle to an object.
DServer enforces the policy that handles to it mayonly be created within the server’s process. The server object then closesthe reference to the server thread in its destructor, so the thread object willonly ever be safely destroyed after the server object has finished using it.DServer::Receive() and DServer::Cancel() provide thekernel-side implementation of the private RServer2 API used to retrievemessages for the server.
These receive and cancel functions provide anAPI for an asynchronous request to de-queue the message at the head ofthe FIFO queue. After de-queuing a message, the request is completed onthe server thread. If a message is present in the server’s queue when therequest is made, this operation is performed immediately. Otherwise noaction is taken and the next message delivered to the DServer object isused to immediately complete this request, rather than being placed onthe server’s queue.The server thread may choose to block until a message is available(for example, using User::WaitForRequest()) or may use anothermethod to wait for the request completion. In the case of a standardSymbian OS server, the CServer2-derived class is an active object anduses the active scheduler as a mechanism to wait for the request for thenext message to be completed.The procedure of writing a message to the server process’s addressspace and signaling it to notify it of the completion of its request for amessage is known as ‘‘accepting’’ a message.
DServer::Deliver()is the client thread’s API to deliver a message to the server’s messagequeue. Both DServer::Receive() and DServer::Deliver() willaccept a message immediately, where appropriate. These methods bothcall a common subroutine DServer::Accept(), which contains thecode to accept a message. It updates the message’s state to reflect thefact the server has accepted its delivery before writing the message to theserver’s address space and finally signaling the server’s request status toindicate completion of its request.The kernel-side message object (RMessageK) is converted into thecorrect format for user-side message object by using a utility classeswhose structure mirrors RMessage2:CLIENT-SERVER ITC133class RMessageU2{public:inline RMessageU2(const RMessageK& a);public:TInt iHandle;TInt iFunction;TInt iArgs[KMaxMessageArguments];TUint32 iSpare1;const TAny* iSessionPtr;};Note that the iSpare1 member of RMessageU2 is simply zeroed by thekernel when writing the message to the user (that is, the server thread),but the other ‘‘unused’’ members of RMessage2 will not be overwrittenby the kernel when it writes a message to user-space.
A separate structureis used here as the format of RMessageK is private to the kernel itselfand this class therefore provides translation between the internal messageformat used by the kernel and the public message format of RMessage2used by user-side code.Most of the methods mentioned above can be written to hold thesystem lock for a constant time with relative ease as they encompass taskssuch as adding to or removing from a doubly linked list, updating stateand performing a fast write of a small amount of data.
However, whenthe DServer object is closed for the last time in DServer::Close(),the session list has to be iterated to detach all the sessions still attached tothe server. This must be done under the protection of the system lock sothat the server’s state is updated in a consistent manner. As there are anarbitrary number of sessions to detach, this operation has an executiontime linearly proportional to the number of sessions, as opposed to aconstant execution time.This operation is therefore carefully split up into n separate operations, each of which only hold the system lock for a constant time andeach of which leave the data structures in a consistent state.
DServer::Close() acquires the system lock before detaching each sessionby calling DSession::Detach(), which will release the lock beforereturning. DSession::Detach() is an operation made up of freeing anarbitrary number of uncompleted messages that have been sent by that session to the server. Again, this operation is split up by acquiring the systemlock before freeing each message and then releasing it again afterwards, sothe system lock is never held for more than a constant, bounded time whilstone message is freed or one session is removed from the server’s queue.To achieve the consistency required whilst splitting up these operations, the fact that a server or session is closing (iAccessCount is 0)is used to restrict what actions may occur.
For example, new sessionscannot be attached to a server whilst it is closing and messages cannotbe completed whilst a session is closing.134INTER-THREAD COMMUNICATION4.1.5.2 Sessions – delivery and message pool managementIn the previous section, I described how sessions provide the context forcommunication between the client and server. Specifically, the kernelsession objects manage the delivery of messages to the server and ensuremessage completion, even under out-of-memory conditions. They alsomanage user-mode access to a session, as specified by the session’s‘‘sharability’’.To ensure message completion, the session object maintains a queue ofmessage objects distinct from that of the server. This queue also includesmessages sent by the session that have not yet been completed by theserver.
The interaction of this queue with both the lifetime of the clientand the server is controlled via the state machine shown in Figure 4.4.1., 2.DSession::New()1.PROCESSINGMESSAGEATTACHED2.7.4.3.CLOSING3.4.6.5.DISCONNECTPENDINGUNATTACHED3.4.2.IDLE~DSession()Figure 4.4Session object state machineAgain, the labeled state transitions listed below are executed under theprotection of the system lock to maintain state integrity. These transitionsCLIENT-SERVER ITC135are also designed to hold the system lock for a constant execution time,in order to maintain the real-time characteristics of the kernel:1. DSession::Send()2.
ExecHandler::MessageComplete()3. DSession::Detach()4. DSession::Close() [when closing last reference][no connect message pending ornot accepted by server]5. DSession::CloseFromDisconnect()6. ExecHandler::SetSessionPtr()7. DSession::Close() [when closing last reference][connect message pending andaccepted by server]The states in Figure 4.4 are defined like this:Server queue(of sessions)Client referencesMessage queueATTACHEDQueuedOpenEmptyPROCESSINGMESSAGEQueuedOpenNon-emptyCLOSINGQueuedClosedNon-empty,containsconnect msg.DISCONNECTPENDINGQueuedClosedNon-emptyUNATTACHEDDe-queuedOpenEmptyIDLEDe-queuedClosedEmptyThe implementation of DSession to support this is as follows:class DSession : public DObject{public:DSession();virtual ∼DSession();virtual TInt Close(TAny*);virtual TInt RequestUserHandle(DThread* aThread, TOwnerType aType);136INTER-THREAD COMMUNICATIONvoid Detach(TInt aReason);RMessageK* GetNextFreeMessage();RMessageK* ExpandGlobalPool();void CloseFromDisconnect();static TInt New(DSession*& aS, TInt aMsgSlots, TInt aMode);TInt Add(DServer* aSvr, const TSecurityPolicy* aSecurityPolicy);TInt MakeHandle();TInt Send(TInt aFunction, const TInt* aPtr, TRequestStatus* aStatus);TInt SendSync(TInt aFunction, const TInt* aPtr,TRequestStatus* aStatus);TInt Send(RMessageK* aMsg, TInt aFunction, const TInt* aPtr,TRequestStatus* aStatus);public:inline TBool IsClosing();public:DServer* iServer;// pointer to kernel-side server objectSDblQueLink iServerLink; // link to attach session to serverconst TAny* iSessionPtr;// pointer to server-side CSession2 (user cookie)TUint16 iTotalAccessCount;TUint8 iSessionType; // TIpcSessionTypeTUint8 iSvrSessionType;// TIpcSessionTypeTInt iMsgCount;// total number of outstanding messages on this sessionTInt iMsgLimit;// max number of outstanding messages on this sessionSDblQue iMsgQ;// q of outstanding msgs on this session (by iSessionLink)RMessageK* iNextFreeMessage; // pointer to next free message in// per-session message pool, if anyRMessageK* iPool; // pointer to per-session message pool, if anyRMessageK* iConnectMsg; // pointer to connect msg, if anyRMessageKBase iDisconnectMsg; // vestigial disconnect message};The session is a standard kernel reference-counted object that user-modeclients hold references to via handles (I’ll discuss this mechanism inthe next chapter, Kernel Services).
However, the lifetime of the sessionmust extend beyond that given by client handles, because it needs tostay in existence whilst the server processes the disconnect messagethat is sent to the server when a session is closed. To do this, we useiTotalAccessCount, modified under the protection of the system lock,to keep track of both whether there are any client references and whenthe session is attached to a server, giving it a count of 1 for either, or 2 forboth.
The IDLE state is equivalent to iTotalAccessCount reaching 0.The creation of a session is performed as two distinct operations in twoseparate executive calls by the client – firstly the creation of the kernelside session object itself and secondly the sending of the connect messageto the server. The second part of this operation is identical to sendingany other message to a server. However, since both a client can connectasynchronously and the server can create a session asynchronously it isnow possible for a client to close its handle to the session before thesession object has been created in the server.CLIENT-SERVER ITC137Normally, closing the last handle to the session would result in a disconnect message being immediately delivered to the server, but if this weredone in this case the disconnect message could be accepted by the server,and would contain a null cookie as the session object had not yet beencreated.