What is the purpose of `tcp_handle_write`?

20 views
Skip to first unread message

xia rui

unread,
Aug 13, 2019, 5:05:45 AM8/13/19
to grpc.io
Hello, everyone.
I am trying to learn the code of `tcp_posix.cc`. There are some functions with similar names. I am confused about the purposes/use cases of these functions.

I write a simple grpc app, and use GRPC_TRACE=tcp and GRPC_VERBOSITY=ERROR (only show the breakpoints I am interested in).

I try to hack the code from the client, so I only focus on write operations.

Things start from `tcp_write()`, after preparing writing buffer with:
  tcp->outgoing_buffer = buf;
  tcp
->outgoing_byte_idx = 0;
it goes to `tcp_flush()`.

In `tcp_flush`, the writing operation is recursively invoked in a for loop. It first pushes `tcp->outgoing_buffer` to `iov` (It may be simple pointer copy, not memory copy). Then, the `tcp_send()` is invoked, which in fact sends buffer using socket. Finally, `tcp_flush` function counts the `sending_length` and `sent_length`.
My first question is: what is the purpose of `sending_length`?
The `sent_length` is the bytes sent by `tcp_send()` function. There is a `trailing` variable, which is `trailing = sending_length - static_cast<size_t>(sent_length);` and I don't know what is the purpose of the following check in `trailing`:

    while (trailing > 0) {
      size_t slice_length
;


      outgoing_slice_idx
--;
      slice_length
=
          GRPC_SLICE_LENGTH
(tcp->outgoing_buffer->slices[outgoing_slice_idx]);
     
if (slice_length > trailing) {
        tcp
->outgoing_byte_idx = slice_length - trailing;
       
break;
     
} else {
        trailing
-= slice_length;
     
}
   
}

When `tcp_flush()` returns, the writing opertion is finished.
My second question is: Who invoke `tcp_write()`?
There is no explicit call on `tcp_write()` in `tcp_posix.cc`. And at the end of `tcp_write()`, there is a callback:

if (!tcp_flush(tcp, &error)) {
    TCP_REF
(tcp, "write");
    tcp
->write_cb = cb;
   
if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
      gpr_log
(GPR_INFO, "write: delayed");
   
}
    notify_on_write
(tcp);
 
} else {
   
if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
     
const char* str = grpc_error_string(error);
      gpr_log
(GPR_INFO, "write: %s", str);
      gpr_log
(GPR_ERROR, "in tcp_write(), maybe finish writing");
   
}

   
GRPC_CLOSURE_SCHED(cb, error);
 
}
the callback `cb` is a argument of `tcp_write()`, and I was wondering who calls `tcp_write()` and what is the callback `cb`.

There are some functions that related to writing operations, and they are not invoked in my tracing log.
My third question is: what are the purposes of these uninvoked functions?
  1. static void notify_on_write(grpc_tcp* tcp)
  2. static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error* error)

Thank you for your time.

Best wishes,
Xia Rui


yas...@google.com

unread,
Jan 21, 2020, 7:49:40 PM1/21/20
to grpc.io
This is a relatively complex piece of code, and it would be hard to explain what each and every line of code does on a forum. Tracing the code would be the fastest way to understand.

For the second question, tcp_write is used through the grpc_endpoint_vtable. Effectively, grpc_endpoint_write calls would translate to tcp_write.

For the third question,  those functions ARE invoked through closures.

Reply all
Reply to author
Forward
0 new messages