On 5/7/25 3:41 PM, Martin Morgenstern via dev wrote:
> Extract two functions from the inner loop of jsonrpc_recv():
> 
> - a function that receives message chunks from the RPC layer, and
> - a function that assembles a complete message if parsing is complete.
> 
> This way, we can reuse some of the logic for a new set of functions in a
> later patch.  Furthermore, this streamlines the error handling in
> jsonrpc_recv(), because we can handle the results of the two functions
> separately.
> 
> Signed-off-by: Martin Morgenstern <martin.morgenst...@cloudandheat.com>
> ---
>  lib/jsonrpc.c | 145 +++++++++++++++++++++++++++++++++-----------------
>  1 file changed, 96 insertions(+), 49 deletions(-)
> 

Hi, Martin.  Thnkas for the patches!  And sorry for the late reply.

See some comments below.

> diff --git a/lib/jsonrpc.c b/lib/jsonrpc.c
> index 90723a42b..a352e01fb 100644
> --- a/lib/jsonrpc.c
> +++ b/lib/jsonrpc.c
> @@ -309,6 +309,96 @@ jsonrpc_send(struct jsonrpc *rpc, struct jsonrpc_msg 
> *msg)
>      return rpc->status;
>  }
>  
> +/* Receives a message chunk from 'rpc' and feeds it into the JSON parser.
> + *
> + * If successful, returns 0.
> + *
> + * Otherwise, it returns:
> + *
> + *   - EAGAIN: No data has been received.
> + *
> + *   - EOF: The remote end closed the connection gracefully.
> + *
> + *   - Otherwise an errno value that represents an error fatal to the
> + *     connection.  'rpc' will not send or receive any more messages.
> + * */
> +static int
> +jsonrpc_recv_chunk(struct jsonrpc *rpc)
> +{
> +    size_t n, used;
> +
> +    /* Fill our input buffer if it's empty. */
> +    if (byteq_is_empty(&rpc->input)) {
> +        size_t chunk;
> +        int retval;
> +
> +        byteq_fast_forward(&rpc->input);
> +        chunk = byteq_headroom(&rpc->input);
> +        retval = stream_recv(rpc->stream, byteq_head(&rpc->input), chunk);
> +        if (retval < 0) {
> +            if (retval == -EAGAIN) {
> +                return EAGAIN;
> +            } else {
> +                VLOG_WARN_RL(&rl, "%s: receive error: %s",
> +                                rpc->name, ovs_strerror(-retval));
> +                jsonrpc_error(rpc, -retval);
> +                return rpc->status;
> +            }
> +        } else if (retval == 0) {
> +            jsonrpc_error(rpc, EOF);
> +            return EOF;
> +        }
> +        byteq_advance_head(&rpc->input, retval);
> +    }
> +
> +    /* We have some input.  Feed it into the JSON parser. */
> +    if (!rpc->parser) {
> +        rpc->parser = json_parser_create(0);
> +    }
> +    n = byteq_tailroom(&rpc->input);
> +    used = json_parser_feed(rpc->parser,
> +                            (char *) byteq_tail(&rpc->input), n);
> +    byteq_advance_tail(&rpc->input, used);
> +
> +    return 0;
> +}
> +
> +/* Get the JSON-RPC message from 'rpc' if JSON parsing is complete.
> + *
> + * On success, stores the message in '*msgp' and returns 0.  The caller
> + * takes ownership of '*msgp' and must eventually destroy it with
> + * jsonrpc_msg_destroy().
> + *
> + * Otherwise, it returns:
> + *
> + *  - ENODATA: JSON parsing is not yet complete and we need to feed more
> + *    data into the parser.

The ENODATA errno doesn't exist on FreeBSD, that's why you see CirrusCI
failure reported in patchwork.

This made me think, however, why we need several custom error codes for
this function and the other ones that call it.  And I think we can get
rid of it.

There should not be a real reason to split the large jsonrpc_recv()
function.  We can re-use it for the _until() variant with just a slight
modification of the loop.  As seen in the second patch of this set, the
both variants are practically the same function with just for() replaced
with while().  So, what we can do is to replace the current loop with a
do {} while(); with an extra condition, e.g.:

  i = 0;
  do {
      ...
  } while (deadline ? time_msec() < deadline : i++ < 50);

This should cover both cases and reduce code duplication as we'll not
have two copies of essentially the same function.

Additionally, this should allow us to get rid of ENODATA return value
as we'll not need to decompose the function.

I see that some function higher in the stack are also relying on the
ENODATA return value indirectly to try again, however, I think, higher
level functions should not be concerned about this.  The CS/IDL code
is manipulating entire jsonrpc messages, so it should only keep going
when there was a full message received on jsonrpc level.  And the
jsonrpc level should ensure that it will keep trying to receive the
message until there is data to receive and it's not the deadline yet.

The jsonrpc session may return zero when it's an echo message, but it
may be something to solve on the jsonrpc session level.  I'll comment
on that in the later patches.

Best regards, Ilya Maximets.
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to