...Async_Ex service functions

hi, i’ve started playing around with the Async_Ex service function calls and have a few questions:

  • the create:
    class function Create(const aUrl: string; aDefaultNamespaces: string = ‘’): IProxyService_AsyncEx; overload;
    it seems that i can’t get this one to work, i do include the /BIN but i guess it does not resolve to the right channel… any prerequisites using this?

  • channel and message:
    my these be shared when using the async_ex functions or do these have to have their own channel and message?

  • the callback:

  • is this called in the main thread or do i need to foresee threaded entry?
  • can i have multiple async_ex.beginxxx calls all using the same callback?
  • if so what is the lifetime of the Endxxx call of (const aRequest: IROAsyncRequest)?
  • if i can’t use the same callback for multiple beginxxx calls, do i always nil the aRequest or not? (in the photo sample you check the intf: if aRequest = ClientForm.fDownload then ClientForm.fDownload := nil;)
  • any other things to take into account?

Hi,

Check the Phone Photo Server sample - it shows usage of AsyncEx interfaces.

You should include full URL with /Bin - it will allow to detect valid message.
general rules - you should include desired channel/message into uses section.

Channels/Messages contain code like

ROUrlSchemaParser.ProtocolHandler[URL_PROTOCOL_HTTP] := TROIndyHTTPChannel;
ROUrlSchemaParser.MessageHandler['bin'] := TROBinMessage;

it is used for detecting what channel/message should be used.
you may add registration manually for desired channel if it is missed it.

own channel/message are used for AsyncEx fro avoiding AV issues when the same data is read/wtitten from different threads.
You can retrieve channel/message from IROAsyncRequest interface.

it is called in background thread

You can use the same callback method for handling begin* methods.

for example - you have BeginSum and BeginGetServerTime methods. if same callback is used for both methods you have to call EndSum or EndGetServerTime in that callback. ofc, you can put some additional info into UserData and detect valid method, but I think better to have own callback per method.

server response is stored in object associated with IROAsyncRequest. this object is passed to your callback method.

in this sample we allow only one download request so we cancel previous one:

  if (fDownload <> nil) then fDownload.Cancel;
...
  fDownload := GetService.BeginDownloadPhoto(lItem.Name,Callback_DownloadPhoto,lItem);

so we clear fDownload once download callback was finished.

Channels/Messages contain code like

ROUrlSchemaParser.ProtocolHandler[URL_PROTOCOL_HTTP] := TROIndyHTTPChannel;
ROUrlSchemaParser.MessageHandler['bin'] := TROBinMessage;

it is used for detecting what channel/message should be used.
you may add registration manually for desired channel if it is missed it.

the uses clause is ok, but the channel does not get recognised, we use plain tcp by the way, not https, so my test url is ‘localhost:8888/BIN’
is this supposed to work?

it is called in background thread

You can use the same callback method for handling begin* methods.

so if i execute 5 beginAAA calls (so the same function) all with the same callback interface, and since the callback is being called threaded, i should include thread code that will synchronise with main thread yes? or will the the callback for the beginAAA func be like a fifo callback?

in this sample we allow only one download request so we cancel previous one:

  if (fDownload <> nil) then fDownload.Cancel;
...
  fDownload := GetService.BeginDownloadPhoto(lItem.Name,Callback_DownloadPhoto,lItem);

so we clear fDownload once download callback was finished.

no i was talking about the callback procedure which has:
finally
if aRequest = ClientForm.fDownload then ClientForm.fDownload := nil;

so this frees the iroasyncrequest but only if it was the one that was created in the beginAAA call, but why only if it is the same? since the call has ended, shouldn’t it be always freed?

you should use tcp://localhost:8888/BIN

it shouldn’t matter because you receive aRequest in callback method.
so you will have 5 background threads with

  • callback(aRequest1)
  • callback(aRequest2)
  • callback(aRequest3)
  • callback(aRequest4)
  • callback(aRequest5)

in the Phone Photo Server sample we use PostMessage for passing data from background threads to main thread.

it isn’t freed interface, it just clear private variable of TClientForm [and decrease reference count] so interface will be correctly destroyed.

ok thx for the info!

Evgeny,

consider this:
var intf : IROAsyncRequest

intf := beginAsync_ex();

intf := beginAsync_ex();

intf := beginAsync_ex();

so now i have 3 requests stored in same var intf…

callback(const aRequest: IROAsyncRequest);

  1. so this is a const param, i can’t nil this, so the async_ex object will take care of this?

  2. since i putted all beginAsync_ex() result into the same var i will have created dangling interface references yes?
    if so i need to hold some kind of list of them?
    or can i simply ignore the interface result of the beginAsync_ex call?
    in fact do i need the interface reference at all?

tia!

Hi,

No issue at all. this means that you can work only with last request.
actual IROAsyncRequest is stored internally and it will be passed to your callback as a parameter.

if you don’t need to do anything with request (check state, cancel request, etc), you can just ignore result of begin* method like

beginAsync_ex();
…
beginAsync_ex();
…
beginAsync_ex();

check TClientForm.bUploadClick:

procedure TClientForm.bUploadClick(Sender: TObject);
..
   GetService.BeginUploadPhoto(lFileName,lstr,Callback_UploadPhoto, lstr);
  end;
end;

so please some advice on the usage :slight_smile:

let’s say i have a bulk of things to do, fetching a status of persons remotely
this is a RO service

so i’m processing an list of 100 persons which i want to check using this async_ex method

so a simple approach is to loop over this list and issue the beginAsync_ex call
i guess creating a TTask to execute that call is overhead

now i only have 1 callback procedure
which will be executed threaded, so simultanious entries may occur
if i’m using a thread safe list i guess i’m fine, so i can save the result back for the correct person (i’ll probably either store the IROAsyncRequest or use UserData to match the request with the correct list entry)

now here comes the question: what is the best way to detect the completion of all calls?
in other words, at a certain moment i need to be able to detect if the bulk operation is finished…

any ideas on that?

tia,
Marc

No you don’t, you have the last of three requests stored in the into var. this isn’t quantum computing :wink:

lol
no no, that wat about something else, dangling interface references

so more like a threadlist of personstatus objs
that personstatus class had a IROAsyncRequest member in which i could store the reference

and in the callback i could check aRequest matching that personstatus member

main issue is how to detect if all calls completed (or did not for that matter)

i could loop over that list keeping track of a response for each entry but then again if a call for some reason whatsoever does not result in a callback i will not know about it and wait…

unless the RO infrastructure has an internal timeout which would do the trick?

Hi,

You can have some Integer value so you can do AtomicIncrement/AtomicDecrement when request is started and callback is called.

if you need possibility to cancel requests, better to store them in TInterfaceList.

You can check status of request with Done and Cencelled proeprties.

ok and i can be sure that if i make 100 calls i will get 100 callbacks?

Hi,

yes.
you can examine code of TROAsyncProxyThread2.IntExecute.

Note: inside your callback you have to check Cancelled and Done properties like

if not aRequest.Cancelled and aRequest.Done then ...

ok, didn’t check that…
so aRequest.Cancelled is when there was a problem along the way i guess?
and what is the meaning of aRequest.Done?

Hi,

aRequest.Cancelled is set when user called aRequest.Cancel manually.
aRequest.Done - request is finished - status can be good or bad.

Note: End* method can raise exception if exception was caught during execution of request.

1 Like

Last q: is there any chance that Done is false in the callback?

Hi,

Done will be true in callback method.
You can check this property when you have IROAsyncRequest instance.

that’s what I expected, thx!

but just to be sure:
in the callback method:
.Cancelled is always False right? (except when I trigger .Cancel)
.Done is always true?

problem is that it seems that when the service is not running, i do get the callback with .cancelled false and .done true, leading me to conclude that the .beginxxx method succeeded but in fact it didn’t cause there was no endpoint…

what is a solid way to determine failure of the service side in the client?

Hi,

yep.

have you called End* method inside of your callback ?

if failure was happened, calling of End* method will raise correspondent exception