sciter-sdk / go-sciter

Golang bindings of Sciter: the Embeddable HTML/CSS/script engine for modern UI development
https://sciter.com
2.57k stars 268 forks source link

No access to requestId needed in call to DataReadyAsync() #280

Closed AshfordN closed 3 years ago

AshfordN commented 3 years ago

I'm currently working on a project where sciter (i.e. a loaded script) needs to request resources (and other data) from a DBMS. So, I'm trying to implement a callback handler to respond to the SC_LOAD_DATA notifications of such requests. My intention is to return LOAD_DELAY immediately, query the database from a separate goroutine, and then call sciter.DataReadyAsync(), with the returned data, from that same goroutine. However, sciter.DataReadyAsync() takes the requestId as its third parameter, but the go-sciter API doesn't appear to give access to that value; and if I pass nil as the requestId, the entire DOM is reloaded with the returned data. Below is the basic design of my handler code:

func SampleHandler(s *sciter.Sciter) func(*sciter.ScnLoadData) int {
    return func(params *sciter.ScnLoadData) int {
        // capture the values while they are valid
        uri := params.Uri()
        //requestID := params.RequestID() // this accessor does not exist in the current API

        go func() {
            // NOTE: 'params' is no longer valid
            time.Sleep(time.Millisecond * 500) // simulated network lag
            buf := []byte("sample data")       // data returned from the DBMS
            s.DataReadyAsync(uri, buf, nil)
        }()

        return sciter.LOAD_DELAYED
    }
}

Now, as mentioned, when the above code is called to handle a request, the entire DOM is reloaded to display the text: "sample data". I've tried implementing the accessor function that is out-commented in the code above, as shown below:

// this was placed in types.go
func (s *ScnLoadData) RequestID() uintptr {
    return uintptr(s.requestId)
}

However, if I use the accessor function to get the requestId, and call sciter.DataReadyAsync() as follows:

s.DataReadyAsync(uri, buf, unsafe.Pointer(requestID))

the request simply times out.


Below is the TIScript code making the request:

function self.ready() {
    self.timer(500ms, ()=>{
        var data = self.request(1500, #get, "db:test")
        stdout.println(data.toString("utf-8"))
    })
}

EDIT: Also, in the first block of code, there's a comment that highlights the fact that params is no longer valid at that point. What this means, is that calling params.Uri() at that point sometimes return erroneous data, and causes a segfault. So, I feel like a big part of why this isn't working is because the underlying C++ object is being destroyed, and Go just ends up holding an invalid reference.

pravic commented 3 years ago

@AshfordN I've added the ScnLoadData.RequestId() function. As for the

the request simply times out.

May be Andrew @c-smile could spot a misusage in your code.

c-smile commented 3 years ago

Instead of synchronous request as in your case try asynchronous request like this:

function self.ready() {
    self.timer(500ms, ()=>{
               view.request {
                  url: "db:test",
                  output: #string,
                  complete: function(data,status)   {  stdout.println(data.toString("utf-8")) },
                  error: function(err,status)   {  stderr.println(err.toString("utf-8")) },
               };
    });
}
AshfordN commented 3 years ago

Thanks @c-smile, this works. @pravic I've made some suggestions to #281. With that, everything should be in order now.