Issott_Common Design Patterns for Symbian OS-The Foundations of Smartphone Software_0470516356 (779879), страница 31
Текст из файла (страница 31)
Noasynchronous request is made at this point but a transition method hasbeen added to keep the RunL() consistent.class CSocketConnector : public CActive{public:static CSocketConnector* NewL();virtual ∼CSocketConnector();void ConnectL(MSocketConnectObserver& aObserver,const TDesC8& aRemoteHost,TUint aRemotePort);private:CSocketConnector();// methods from CActivevirtual void RunL();virtual void DoCancel();virtual TInt RunError(TInt aError);// State transition methodsvoid WaitingForDnsLookupTransitionL();void WaitingForConnectionTransitionL();void ConnectedTransitionL();private:enum TConnectState{EIdle = 0,// A connection has been requested so a DNS lookup needs to be// initiated to find the IP address of the remote host.EWaitingForDnsLookup,// The IP address of the remote host has been found so we need to// initiate a TCP connection to that remote host.EWaitingForConnection,// Task completeEConnected,};private:TConnectState iState;MSocketConnectObserver& iObserver;ASYNCHRONOUS CONTROLLER159// Service Provider for the EWaitingForDNSLookup stateRHostResolver iHostResolver;// Service Provider for the EWaitingForConnection stateCSocket* iSocket;...};The following function starts the whole task going.
You should note thatit takes as parameters all the information needed to complete the wholetask rather than just the first sub-task. This is a common phenomenonsince the client of the asynchronous controller only knows about theoverall task and has just this one opportunity to provide all the necessaryinformation.void CSocketConnector::ConnectL(MSocketConnectObserver& aObserver,const TDesC8& aRemoteost,TUint aRemotePort){// Use Fail Fast (see page 17) to catch faults in the client__ASSERT_DEBUG( iState == EIdle, Panic(EBadSocketConnectorState) );// Store parameters for lateriObserver = aObserver;iHost = HBufC::NewL(aRemoteHost.Length());iHost->Des().Copy(aRemoteHost);iPort = aRemotePort;// Start the taskWaitingForDnsLookupTransitionL();}The RunL() implements the normal flow through the FSM which isfairly simple for this case simply moving onto the next state in sequence:void CSocketConnector::RunL(){// Leave if there has been an errorUser::LeaveIfError(iStatus.Int());switch(iState){case EWaitingForDnsLookup:{WaitingForConnectionTransitionL();} break;case EWaitingForConnection:{ConnectedTransitionL();} break;case EIdle:default:160COOPERATIVE MULTITASKINGPanic(EBadSocketConnectorState);break;} // End of switch statement}The error paths through the FSM are slightly more interesting in that inthe EWaitingForDnsLookup and EWaitingForConnection statesit’s worth re-trying if KErrNotReady is received.
Otherwise an errorends the task early so the observer needs to be informed.TInt CSocketConnector::RunError(TInt aError){TBool errorResolved = EFalse;switch(iState){case EWaitingForDnsLookup:{if (aError == KErrNotReady){WaitingForDnsLookupTransitionL();errorResolved = ETrue;}} break;case EWaitingForConnection:{if (aError == KErrNotReady){WaitingForConnectionTransitionL();errorResolved = ETrue;}} break;case EConnected:{// This is a valid state to have an error in but there’s nothing we// can do about it} break;case EIdle:default:Panic(EBadSocketConnectorState);break;}if(errorResolved){return KErrNone;}iObserver->HandleConnectError(aError);return error;}DoCancel() simply ensures we send a cancellation to the correctservice provider:ASYNCHRONOUS CONTROLLER161void CSocketConnector::DoCancel(){switch(iState){case EWaitingForDnsLookup:{// DNS lookup is pending soiHostResolver.Cancel();} break;case EWaitingForConnection:{if(iSocket){// Connection is pending - cancel and delete the socketiSocket->CancelConnect();delete iSocket;iSocket = NULL;}} break;case EConnected:case EIdle:default:// Do nothing...break;}}All the other functions not mentioned are either as per the standardpattern given above or, as in the case of the transition functions, don’thelp to illustrate this pattern.Other Known Uses• Mass StorageThis pattern is used within the File System to handle the ongoing taskof managing the bulk-only, mass-storage transport and communications with the SCSI protocol.
Its asynchronous controller has statessuch as:• waiting for a Command Block Wrapper (CBW) packet• sending a Command Status Wrapper (CSW) packet• reading and writing data.• SyncMLThe SyncML Sync Engine uses the ‘Long-Running Active Object’ variation of this pattern when processing messages. Although messagescould be parsed and processed synchronously, their processing ispotentially long running. Therefore, the large parsing task is broken162COOPERATIVE MULTITASKINGdown into smaller more manageable parsing sub-tasks and driven byan asynchronous controller.Variants and Extensions• Self-Completing Active ObjectSome active objects need to complete their own requests to inducean asynchronous callback.
The main reason for doing this is that ityields control back to the active scheduler so that other, perhapshigher-priority, active objects can run. This is done via the followingsnippet of code:void CMyAsyncController::SelfComplete(TInt aError){SetActive();TRequestStatus* s = &iStatus;User::RequestComplete(s,aError);}This is a not a variation of Active Objects (see page 133) since if youonly need to self-complete once and don’t have any other use for theactive object then a better alternative is to use CAsyncCallBackprovided by the operating system. If you call self-complete more thanonce, you’ve probably got some kind of FSM going on and henceshould use this pattern.• Long-Running Active ObjectWhile handling external event signals is a common use case for thispattern your asynchronous controller can just as well be driven byinternal sub-tasks.
Most structured actions can be recast in terms ofa succession of sub-tasks; each portion of the whole action is treatedas a sub-task, and the asynchronous controller tracks the progressthrough them. The main reason you’d want to do this is because theoverall task would take too long to do purely within the RunL() ofan active object since it would prevent all the other active objectsfrom running.
Hence the task is broken up into chunks and regularyield points inserted as per the self-completing active object variationabove. For example, the re-calculation of values in a spreadsheetcould mean thousands of evaluations and hence might be betterrestructured as 10 sub-tasks each doing hundreds of evaluations. Notethat you need to choose the step size with care because the cost ofthis solution is increased execution overhead which is proportionalto the number of sub-tasks. This pattern is clearly suited to addressingthis problem as well given a suitable FSM that divides up the originaltask.
In addition Symbian OS provides a specialized class to help withspecifically this issue – CIdle.ASYNCHRONOUS CONTROLLER163• Other FSM DesignsThis pattern uses a fairly simple FSM design with enums and switcheshowever it can also be generalized to work with other state patternssuch as those described in the Models for Object-Oriented Designof State (MOODS) section of [Vlissides et al., 1996] and the StatePatterns of [Martin et al., 1998].References• Active Objects (see page 133) provides the foundation for this pattern.• The State Patterns of [Martin et al., 1998] and the MOODS of [Vlissideset al., 1996] provide alternatives to the enum–switch approach toFSMs taken here.6Providing ServicesWe define a service to be a collection of useful actions that are packagedup by a vendor, the service provider, and supplied for use by softwarecomponents, the clients.
An action can be anything that is of benefit toa client. The dialog or conversation between the service and a clientis known as a session which is started at a certain point and thenstopped. An established session often involves more than one messagein each direction. When a client asks the service for an action to beperformed on its behalf this is known as a service request. When theservice has finished performing the action it sends a request completemessage in response (this might be implemented as a simple functionreturn).A session is often stateful : either the client or, more commonly, theservice has to maintain information about the session history in order to beable to communicate. In stateless sessions, the communication consistsof independent requests with responses.