Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

handling asynchronous events

Zaplok Riggles
Registered User
Join date: 25 Feb 2008
Posts: 119
02-28-2008 09:34
Hi all. I am trying to create a loop that makes an llhttprequest per loop cycle. The problem is, I don't want the requests to queue up. I want to wait and check the status of the request before moving on in my loop. I tried something like this:

make httprequest
loop if status = 0 {
sleep(1)
}

httprequest handler {
status = 1
}

this doesn't work because when Im in my loop, the events coming in are queued and not processed. Thus, the global never gets set. So, what I need to find out is a good way to have the part of my code that makes the request pause until I get a valid response back. If I need to make this a state machine, it will get a bit ugly as I will need to track where I was in the loop and process one iteration of the loop, and then set my state to waiting or something, and then have the httprequest event handler set my state to processing or something. This will require me to preserve a number of variables that should be local but instead will need to become global, not to mention, making the code much more difficult to understand.

Ideas?
Pale Spectre
Registered User
Join date: 2 Sep 2005
Posts: 586
02-28-2008 10:51


timer()
if status == 1
{
// do stuff
}

:

llSetTimerEvent(60);
Zaplok Riggles
Registered User
Join date: 25 Feb 2008
Posts: 119
02-28-2008 11:27
From: Pale Spectre


timer()
if status == 1
{
// do stuff
}

:

llSetTimerEvent(60);


I really don't see how a timer would help me with the issue at hand. A timer doesn't fire when you are processing script code. Thus, I would still need to leave my procedure, go into a dormant state, and then wake back up when the timer fires. This requires me to have my main block of code continually restarting and using globals to determine where to pick back up after it wakes up again. The timer and the httpresponse event both won't fire while I am executing code. So, the architecture of both would require a state driven system which is more complex. I know how to do that, but was hoping someone with some experience with asynchronous events in LSL could offer some advices.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
02-28-2008 12:01
you use the timer AS your loop....

CODE

integer gBooReady;

default{
state_entry(){
llSetTimerEvent( 30.00 ); //-- twice a minute
}

http_response( <insert declaratiosn> ){
gBooReady = TRUE;
//-- process data
}

timer(){
//-- optionally check a loop counter

if (gBooReady){
//-- request http page
gBooReady = !gBooReady;
//--optionally increment loop counter
}
}
}


you could also structure it like a dataserver event for notecard reads, calling the next http page from within the response event, or setting a timer when you're done with the event, which triggers a new call.
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Zaplok Riggles
Registered User
Join date: 25 Feb 2008
Posts: 119
02-28-2008 12:22
From: Void Singer
you use the timer AS your loop....

CODE

integer gBooReady;

default{
state_entry(){
llSetTimerEvent( 30.00 ); //-- twice a minute
}

http_response( <insert declaratiosn> ){
gBooReady = TRUE;
//-- process data
}

timer(){
//-- optionally check a loop counter

if (gBooReady){
//-- request http page
gBooReady = !gBooReady;
//--optionally increment loop counter
}
}
}


you could also structure it like a dataserver event for notecard reads, calling the next http page from within the response event, or setting a timer when you're done with the event, which triggers a new call.


I think I'm doing a bad job of explaining my needs. Example:

CODE

myloop() {
integer counter;
for (counter = 0; counter < 10; counter++) {
llHttpRequest ......
I DO NOT WANT TO CONTINUE anymore in my loop until I know the above
request worked.
}
}

This is a very simplified version of what I will be doing. I am not just doing 5 requests in a row but may be doing many different requests to different urls etc. but all must succeed prior to moving onto the next one. Example: verify a credit card, then update a database with the transaction info, then retrieve a unique ID, then post the unique ID in a different database. If at any stage a failure occurs, I want to stop and be able to roll back my processes. If we had sychronous http requests, my code would look like this:

myloop()
{
integer result;
result = llHttpRequest (credit card verification)
if (result == 200) {
result = llHttpRequest (database update)
if (result == 200) {
result = llHttpRequest (retrieve unique id)
if (result == 200) {
result = llHttpRequest (post unique ID)
if (result == 200) {
show transaction completed message
}
}
}
}
if (result != 200) {
rollback transaction as needed, display failure message
}
}
Domchi Underwood
Registered User
Join date: 4 Aug 2007
Posts: 44
02-28-2008 12:32
When you sleep, the code doesn't get executed; unlike "normal" programming languages, LSL has only one thread which can be locked somewhere in your script. If that thread sleeps, the rest of your events don't get anywhere.

How about this solution... global variable(s) to track where you are, function which executes stuff now you have in your loop, and then:
1. you fire llHTTPRequest at the end of your function
2. after processing response in http_response event, call your function
3. at the beginning of your function, check if you're done and finish if you are

So basically you connect your function (insides of your now-loop) and http_response and make them call eachother.

There are several other approaches you could take but no way to avoid making your local variables global. (Since you can't get response in http_response event while you're executing another event, and you can't delegate this to other scripts since you can't receive any communication from them for the same reason.)

By your question, I'm guessing that you're struggling with LSL event-driven philosophy; if that's the case, learn about how events get executed and how to program with events. A starting point would be http://www.lslwiki.net/lslwiki/wakka.php?wakka=events (ignore the list of events itself).
Domchi Underwood
Registered User
Join date: 4 Aug 2007
Posts: 44
02-28-2008 12:38
I've just read your last message - of course, if you're processing different http responses, you might want to call another state on the end of the http_response event of the previous state, and call llHTTPRequest in state_entry event for each state.

The solution with function assumes that you have the some code which has to be executed for each iteration of your "loop", but if you're processing the different HTTP responses, you might want to have separate http_response event and state for each HTTP request.
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
02-28-2008 12:42
From: Zaplok Riggles
I really don't see how a timer would help me with the issue at hand. A timer doesn't fire when you are processing script code. Thus, I would still need to leave my procedure


Exactly. You MUST leave your procedure to handle another event. In general, avoid architectures that use llSleep() in loops in procedures; instead structure your code to run from timers.
Pale Spectre
Registered User
Join date: 2 Sep 2005
Posts: 586
02-28-2008 12:47
If you're trying to control Flow Driven logic in an Event Driven environment, then I think this will prove difficult unless you can structure your code such that you, in effect, have your own events with User Functions that handle those events.

I don't know of any way LSL can pause inside a function where the continuation of the logic relies on the handling of an Event.
Domchi Underwood
Registered User
Join date: 4 Aug 2007
Posts: 44
02-28-2008 12:55
Oh, and one more thing - don't use timer and assume that you received HTTP response since some amount of time has passed. Timer is not a good mechanism for that. HTTP response can be terribly slow if the network is slow, or it can simply timeout. To know if you received response, use variable and set it to received in http_response event.
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
02-28-2008 15:01
From: Pale Spectre
I don't know of any way LSL can pause inside a function where the continuation of the logic relies on the handling of an Event.

There's a very hacky workaround, but it is less effective with the recently increased restriction on prim name and description, and I really don't recommend it in any case (it's sloppy, assumes no other scripts are going to use the same "prim-global" data, etc): you can poll for persistent prim properties such as name, description, color, texture key, etc., and use a wacky protocol of some kind to communicate with another script that uses proper events. Yuck and a half.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
02-29-2008 00:04
this isn't optimized, but it's essentially what you want

CODE

//-- in globals
integer result;
integer step = 1;

timer(){
//-- some optional loop counter?
if (1 == step){
llHttpRequest (credit card verification);
}else if (result == 200){
if (2 == step){
llHttpRequest (database update);
}else if (3 == step){
llHttpRequest (retrieve unique id);
}else if (4 == step){
result = llHttpRequest (post unique ID);
}else if (5 == step){
step = 0;
//-- show transaction completed message
}
}

if (200 == result) {
++step;
}else if (result){ //--some other return code besides zero meaning failure
--step;
//-- rollback transaction as needed, display failure message
}
//-- reset result code, waiting for change from http response
result = 0;
}


you can either continue checking for a response as above, or as an improvement, start the timer, stop it in the timer code, and restart it from the http response event (where you'll be setting the result variable to the return code).

you could also use a similar structure from within the http response event, and only triggering the loop start through the code, just as you would with a notecard reader.

the key here is to nest the logic, since you effectively have a select case inside a loop... (vs the nested ifs where you had to keep track of all past responses)
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Domchi Underwood
Registered User
Join date: 4 Aug 2007
Posts: 44
02-29-2008 02:20
Void, I think that the timer would be bad decision in this case; there is no need for time constraints, since Zaplok said he wanted to wait for the next action until he receives the reply. With timer used in this way, he would basically check if he has received http_response and know that he has once he set some global variable through http_response.

If he's waiting for HTTP response to take the next step, there's no need to process response, and then wait for timer to trigger to do something with that response - as you correctly suggested (agreed on the rest of your post).
Zaplok Riggles
Registered User
Join date: 25 Feb 2008
Posts: 119
02-29-2008 04:57
From: Void Singer
this isn't optimized, but it's essentially what you want

CODE

//-- in globals
integer result;
integer step = 1;

timer(){
//-- some optional loop counter?
if (1 == step){
llHttpRequest (credit card verification);
}else if (result == 200){
if (2 == step){
llHttpRequest (database update);
}else if (3 == step){
llHttpRequest (retrieve unique id);
}else if (4 == step){
result = llHttpRequest (post unique ID);
}else if (5 == step){
step = 0;
//-- show transaction completed message
}
}

if (200 == result) {
++step;
}else if (result){ //--some other return code besides zero meaning failure
--step;
//-- rollback transaction as needed, display failure message
}
//-- reset result code, waiting for change from http response
result = 0;
}


you can either continue checking for a response as above, or as an improvement, start the timer, stop it in the timer code, and restart it from the http response event (where you'll be setting the result variable to the return code).

you could also use a similar structure from within the http response event, and only triggering the loop start through the code, just as you would with a notecard reader.

the key here is to nest the logic, since you effectively have a select case inside a loop... (vs the nested ifs where you had to keep track of all past responses)


This is basically what I had come up with as well. A state machine. I reset timers with each http request to check for timeouts and reset on the next http success. It sure would be nice to have an llHttpRequestSynchronous function that took a timeout parameter though. Thanks all for the excellent feedback!
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
02-29-2008 13:05
From: Domchi Underwood
Void, I think that the timer would be bad decision in this case; there is no need for time constraints, since Zaplok said he wanted to wait for the next action until he receives the reply. With timer used in this way, he would basically check if he has received http_response and know that he has once he set some global variable through http_response.

If he's waiting for HTTP response to take the next step, there's no need to process response, and then wait for timer to trigger to do something with that response - as you correctly suggested (agreed on the rest of your post).

there is a possibility that the script could NEVER get a response, in which case the script would effectively lock mid-cycle, which might throw the level of another cycle started (or logically there could be no new requests until the current sequence is over....)

one such guaranteed instance of this would be a sim crash or reset... the script would within the loop, but it may never recieve the response... customer wouldn't notice, but the script would need manualy reset... not good for something in scaled use.

now this could be avoided by setting a timeout either when the first call is made, or during the repeated http events, but that'll require adding a timer in a different way to my second suggestion.

PS guess I shoulda mentioned that before, but I couldn't think of a clear example at the time.
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -