1
0
Fork 0
mirror of https://github.com/sourcegraph/jsonrpc2.git synced 2026-06-16 04:04:56 +02:00

Break the Call method into a dispatcher and waiter (#41)

Before, the Call method dispatched the JSON-RPC request and waited
until completion of the request before returning. This made it difficult
to release any locks that need to be released upon dispatch.

Now, the Call method is broken into a DispatchCall and Wait pair.
DispatchCall returns a proxy to the internal call object as soon as the
request is dispatched. When appropriate, the caller can invoke the Wait
method on this proxy to block until the result is ready.

The original Call method is not changed, but now implemented by
these primitives.
This commit is contained in:
Cornelius Weig 2021-02-01 09:28:50 +01:00 committed by GitHub
parent 15c2290dcb
commit 366fbb5207
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -416,24 +416,50 @@ func (c *Conn) send(_ context.Context, m *anyMessage, wait bool) (cc *call, err
// its result is stored in result (a pointer to a value that can be
// JSON-unmarshaled into); otherwise, a non-nil error is returned.
func (c *Conn) Call(ctx context.Context, method string, params, result interface{}, opts ...CallOption) error {
call, err := c.DispatchCall(ctx, method, params, opts...)
if err != nil {
return err
}
return call.Wait(ctx, result)
}
// DispatchCall dispatches a JSON-RPC call using the specified method
// and params, and returns a call proxy or an error. Call Wait()
// on the returned proxy to receive the response. Only use this
// function if you need to do work after dispatching the request,
// otherwise use Call.
func (c *Conn) DispatchCall(ctx context.Context, method string, params interface{}, opts ...CallOption) (Waiter, error) {
req := &Request{Method: method}
if err := req.SetParams(params); err != nil {
return err
return Waiter{}, err
}
for _, opt := range opts {
if opt == nil {
continue
}
if err := opt.apply(req); err != nil {
return err
return Waiter{}, err
}
}
call, err := c.send(ctx, &anyMessage{request: req}, true)
if err != nil {
return err
return Waiter{}, err
}
return Waiter{call: call}, nil
}
// Waiter proxies an ongoing JSON-RPC call.
type Waiter struct {
*call
}
// Wait for the result of an ongoing JSON-RPC call. If the response
// is successful, its result is stored in result (a pointer to a
// value that can be JSON-unmarshaled into); otherwise, a non-nil
// error is returned.
func (w Waiter) Wait(ctx context.Context, result interface{}) error {
select {
case err, ok := <-call.done:
case err, ok := <-w.call.done:
if !ok {
err = ErrClosed
}
@ -441,10 +467,10 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface
return err
}
if result != nil {
if call.response.Result == nil {
call.response.Result = &jsonNull
if w.call.response.Result == nil {
w.call.response.Result = &jsonNull
}
if err := json.Unmarshal(*call.response.Result, result); err != nil {
if err := json.Unmarshal(*w.call.response.Result, result); err != nil {
return err
}
}