A Squirrel question regarding preservation of references

I’ve been experiencing some conflicts when juggling multiple simultaneous http requests. This is probably due to my misunderstanding of handling references in a context. I have some example code below that will run as is.

`http.onrequest(function(httpReq,httpResp){

function onResult(response){ 
    server.log("onResult httpReq.path="+httpReq.path+" httpResp="+httpResp+" func="+onResult);        
    httpResp.send(200,http.jsonencode(response)); 
}
server.log("onRequest httpReq.path="+httpReq.path+" httpResp="+httpResp+" func="+onResult);
switch(httpReq.path){
    case "/first": imp.wakeup(10.0,function(){ onResult({ result="first" })}.bindenv(this)); break;
    case "/second": imp.wakeup(0.0,function(){ onResult({ result="second" })}.bindenv(this)); break;
    default:    onResult({ result="unknown path" });
}

});`

The server.log calls help to identify the object ids of the httpResp and the embedded function onResult that I use to dispatch the HTTP response.

If I query agent.electricimp.com/xxxxx/first and wait 10 seconds, I get { result=first }

If I query agent.electricimp.com/xxxxx/second, I immediately get { result=second }

However, if I query /first followed quickly by /second, I get { result=second } and then (after 10 seconds) an error as the agent tries to send the same HTTP response twice.

I had thought that by binding the context of function(httpReq,httpResp)… to each of the calls to onResult, I would not have to explicitly retain the objects httpReq and httpResp. But it seems that once the second http request arrives, it shares the same references as the first. What is the cleanest way for me to handle this?

Is it better for me to put onResult outside of the http.onrequest() handler and pass it references to httpReq and httpResp?
e.g. function(){ onResult({ result=“x” },httpReq,httpResp) }

beardedinventor has code for this, listed in this thread

http://forums.electricimp.com/discussion/comment/8997#Comment_8997

It is what I use. What you tried to attempt- sorry it is above my skill to answer it. To me the presence of a function definition ‘onResult’ inside inside the http handler looks improper but this could just be because I am not an expert.

You’re right, that does look wrong and might be a bug. Both on the agent and the device, the following simplified code:
`function f(req, res) {
function done® { server.log(res.a + “,” + r.b); }

switch (res.a) {
    case 0: imp.wakeup(0.0, function() { done({b=0}); }); break;
    case 1: imp.wakeup(1.0, function() { done({b=1}); }); break;
}

}

f(1,{a=1});
f(0,{a=0});`
prints “0,0” and “0,1” rather than the “0,0” and “1,1” that I’d kind-of expect. Surely the closure formed for done() should refer to the “res” in the current invocation of “f”. (You don’t need the bindenv, because “res” (“httpResp” in your code) is a local of an outer function, not a member of any context object.)

The workaround, of passing httpReq and httpResp to onResult directly, should work fine.

Peter

(Naturally I spot the problem just after posting saying that I can’t spot the problem.)

The statement “function onResult(…)” is shorthand for “onResult <- function(…)”: in other words, it stores the closure representing “onResult” in the context object. In your case, that’s the global object. So the second call to the handler overwrites that closure with the new closure.

What you want instead is:
local function onResult(response){
which will do the Right Thing. This isn’t a bug, it’s just Squirrel’s function declaration syntax being confusing.

Peter

You can also make the parameters local with something like this:

http.onrequest(function(httpReq,httpResp){ local httpReq = httpReq; local httpResp = httpResp; function onResult(response){ } })

Parameters always count as local anyway.

Peter

Thanks for comments. I’ve just taken this approach to solve the problem, which seems to work in my test cases:

`function onResult(response,httpReq,httpResp){
server.log(“onResult httpReq.path=”+httpReq.path+" httpResp="+httpResp+" func="+onResult);
httpResp.send(200,http.jsonencode(response));
}

http.onrequest(function(httpReq,httpResp){

server.log("onRequest httpReq.path="+httpReq.path+" httpResp="+httpResp+" func="+onResult);       
switch(httpReq.path){
    case "/first": imp.wakeup(10.0,function(){ onResult({ result="first" },httpReq,httpResp)}); break;
    case "/second": imp.wakeup(0.0,function(){ onResult({ result="second" },httpReq,httpResp)}); break;
    default:    onResult({ result="unknown path" });
}

});
`