Read-only archive of the All About Symbian forum (2001–2013) · About this archive

waiting for an active object

3 replies · 3,692 views · Started 16 June 2003

Hello,

I have been trying to figure out how to wait for an active object to finish. I've searched the forums and read the documentation but cant seem to find the answer.

I thought this code below would work but it doesnt:

[code:1]
iGrabber->IssueRequest(aURL, aFilename);
while (!(iGrabber->IsFinished())) {
User::WaitForAnyRequest();
}
[/code:1]
IsFinished is a member function of iGrabber that checks an iConnectedState member variable, and if the data (ie the page has been downloaded) is ready is returns ETrue.

Instead of doing what I hoped, it seems the WaitForAnyRequest eats up the active objects async operations and RunL never gets called.

The active object works fine without this wait loop.

So my question is how do you wait for an active object to be in a state that you want?

Thanks

I'm assuming the IssueRequest is an asynchronous function.
In which case you should be passing it the TRequestStatus of your active object. So you need an extra parameter.
When IssueRequest is finished it should set the TRequestStatus to complete.
Then the Active Scheduler will call the RunL of your active object. i.e. the class which owns iGrabber
The idea of active objects is so you don't have the while loop checking if you've finished as you will stop other code from being able to execute.
I think this is the solution in your case its not that easy to tell just from 3 lines of code.

Hope this helps

hmm, i dont think i explained it right, lemme try again.

iGrabber is the active object, it works fine, IssueRequest starts the active object and it does a few async operations with SetActive called after each one, and RunL in iGrabber gets called. But what I want to do is in the thread that started/created the iGrabber active object, I want to wait for it to finish.

Here is some more code:

Here is the code thats starts the PageGrabber active object:
[code:1]
// create a pagegrabber object
PageGrabber* iGrabber;
iGrabber = PageGrabber::NewL();

// setup form vars
iGrabber->SetNumFormVars(3);
iGrabber->SetFormVar(0, _L8("username"), aUsername);
iGrabber->SetFormVar(1, _L8("password"), aPassword);
iGrabber->SetFormVar(2, _L8("t"), _L8("1"));

TRequestStatus status;
// then issue the request for the page
iGrabber->IssueRequest(aURL, aFilename, status);
[/code:1]
from the above code you can see that iGrabber is an instance of PageGrabber, which is an active object

Below is some code from the PageGrabber active object:
[code:1]
void PageGrabber::IssueRequest(const TDesC& aURLstring, TDes& aFilename) {
// init the url object
webpage.Set(aURLstring);

// validate filename, setting it if needed
ValidateFilename(aFilename);

// format post data
SetPostDataL();

// format header data
SetPostHeadersL();

// open socket server
// connect to the socket server
User::LeaveIfError(iSocketServ.Connect());

// now resolve the name given in the URL
iHostResolver.Open(iSocketServ, KAfInet, KProtocolInetUdp);
iHostResolver.GetByName(webpage.host, iNameEntry, iStatus);

// set the connection state and tell the active object to wait for the async operation
iConnectState = ENameResolved;
SetActive();
}

.. RunL function ommited, typical active object RunL that performs async operations setting SetActive with a big switch statement for different connect states ....

TBool PageGrabber::IsFinished()
{
return (iConnectState == EDataReady);
}

[/code:1]
Now the above code starts off the scheduler and causes RunL to run after the GetByName function has finished. This all works fine, the problem is, if at the place where I created the active object I try and wait for it to finish using the WaitForAnyRequest it seems to eat up the async operation that the active object is going to use.

Surely there is a simple way for a program to create an active object, have it go do its thing in the background, then for the program that created the active object to wait for a certain state of the active object (ie finished). I could put a timer in the loop instead of WaitForAnyRequest, but that is not the symbian way, I need a way to tell the main program that it should wait for the completion of the active object that it created so that it can manipulate data from the active object, and then ultimatly destroy it when finished with it.

I tried to make my active object behave like the symbian async function, and put a TRequestStatus parameter in and setting that to KResultPending at the start and then setting it to KErrNone when completed with User::RequestComplete. But when I tried waiting for that with WaitForRequest, it had the same result, after the active object did its first SetActive, it never ran RunL.

If you call User::WaitForAnyRequest, the WaitForAnyRequest function will wait until the next request before continuing. It means that the Active Scheduler never knows that the status of an Active Object changed and thus never calls the RunL method of the AO.

So, you are right in your statement that it seems the WaitForAnyRequest eats up the active objects async operations and RunL never gets called.

The way around this is to create a callback function using TCallback, a observer class with a callback method (similar to the HandleXXXXEvent functions in some of the controls) or another Active Object to signal that the first AO is at the correct state. Anyone of the three will work.