Closed ccamel closed 1 year ago
Hi, you can capture the context.Context
passed in p.QueryContext()
or p.QuerySolutionContext()
with engine.Delay()
:
package main
import (
"context"
"errors"
"flag"
"fmt"
"net/http"
"time"
"github.com/ichiban/prolog"
"github.com/ichiban/prolog/engine"
)
func main() {
var (
u string
t time.Duration
)
flag.StringVar(&u, "url", "http://httpbin.org/status/200", "URL")
flag.DurationVar(&t, "timeout", 1*time.Second, "timeout")
flag.Parse()
p := prolog.New(nil, nil)
p.Register2(engine.NewAtom("get_status"), getStatus)
ctx, cancel := context.WithTimeout(context.Background(), t)
defer cancel()
var s struct {
Status int
}
switch err := p.QuerySolutionContext(ctx, `get_status(?, Status).`, u).Scan(&s); err {
case nil:
fmt.Printf("status: %d\n", s.Status)
case context.DeadlineExceeded:
fmt.Printf("timeout\n")
default:
panic(err)
}
}
func getStatus(vm *engine.VM, url, status engine.Term, k engine.Cont, env *engine.Env) *engine.Promise {
u, ok := env.Resolve(url).(engine.Atom)
if !ok {
return engine.Error(errors.New("not an atom"))
}
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return engine.Error(err)
}
return engine.Delay(func(ctx context.Context) *engine.Promise {
req := req.WithContext(ctx)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return engine.Error(err)
}
return engine.Unify(vm, status, engine.Integer(resp.StatusCode), k, env)
})
}
$ time go run main.go -url http://httpbin.org/delay/10
timeout
real 0m1.396s
user 0m0.302s
sys 0m0.175s
Oh, that's great! 😀 Well, problem solved. Thank you!
As far as I see, the current VM implementation does not allow transmitting the context to the custom implementation of a predicate:
To pass request-scoped values, cancelation signals, and deadlines that are required by some API calls, it is necessary to have a way to pass the context. IMHO, there are a few options for how to do this: adding a
ctx
parameter (though this would be a breaking change), adding a field to theengine.Env
, or binding a variable on the context (though it is unclear if this is a suitable solution).Thanks!