wire_writer.cc
Go to the documentation of this file.
1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
18 
19 #ifndef GRPC_NO_BINDER
20 
21 #include <utility>
22 
23 #include "absl/cleanup/cleanup.h"
24 #include "absl/types/variant.h"
25 
26 #include <grpc/support/log.h>
27 
28 #define RETURN_IF_ERROR(expr) \
29  do { \
30  const absl::Status status = (expr); \
31  if (!status.ok()) return status; \
32  } while (0)
33 
34 namespace grpc_binder {
35 
37  return (tx.GetFlags() & kFlagMessageData) == 0 ||
38  static_cast<int64_t>(tx.GetMessageData().size()) <=
40 }
41 
42 // Simply forward the call to `WireWriterImpl::RunScheduledTx`.
43 void RunScheduledTx(void* arg, grpc_error_handle /*error*/) {
44  auto* run_scheduled_tx_args =
46  run_scheduled_tx_args->writer->RunScheduledTxInternal(run_scheduled_tx_args);
47 }
48 
50  WritableParcel* parcel) {
51  if (tx.IsClient()) {
52  // Only client sends method ref.
54  }
55  RETURN_IF_ERROR(parcel->WriteInt32(tx.GetPrefixMetadata().size()));
56  for (const auto& md : tx.GetPrefixMetadata()) {
59  }
60  return absl::OkStatus();
61 }
62 
64  WritableParcel* parcel) {
65  if (tx.IsServer()) {
66  if (tx.GetFlags() & kFlagStatusDescription) {
68  }
69  RETURN_IF_ERROR(parcel->WriteInt32(tx.GetSuffixMetadata().size()));
70  for (const auto& md : tx.GetSuffixMetadata()) {
73  }
74  } else {
75  // client suffix currently is always empty according to the wireformat
76  if (!tx.GetSuffixMetadata().empty()) {
77  gpr_log(GPR_ERROR, "Got non-empty suffix metadata from client.");
78  }
79  }
80  return absl::OkStatus();
81 }
82 
83 WireWriterImpl::WireWriterImpl(std::unique_ptr<Binder> binder)
84  : binder_(std::move(binder)), combiner_(grpc_combiner_create()) {
85  gpr_log(GPR_INFO, "%s write_mu_ = %p , flow_control_mu_ = %p", __func__,
87 }
88 
90  GRPC_COMBINER_UNREF(combiner_, "wire_writer_impl");
91  while (!pending_outgoing_tx_.empty()) {
92  delete pending_outgoing_tx_.front();
93  pending_outgoing_tx_.pop();
94  }
95 }
96 
97 // Flow control constant are specified at
98 // https://github.com/grpc/proposal/blob/master/L73-java-binderchannel/wireformat.md#flow-control
99 const int64_t WireWriterImpl::kBlockSize = 16 * 1024;
101 
104  std::function<absl::Status(WritableParcel*)> fill_parcel) {
106  RETURN_IF_ERROR(binder_->PrepareTransaction());
107  WritableParcel* parcel = binder_->GetWritableParcel();
108  RETURN_IF_ERROR(fill_parcel(parcel));
109  // Only stream transaction is accounted in flow control spec.
110  if (static_cast<int32_t>(tx_code) >= kFirstCallId) {
111  int64_t parcel_size = parcel->GetDataSize();
112  if (parcel_size > 2 * kBlockSize) {
114  "Unexpected large transaction (possibly caused by a very large "
115  "metadata). This might overflow the binder "
116  "transaction buffer. Size: %" PRId64 " bytes",
117  parcel_size);
118  }
119  num_outgoing_bytes_ += parcel_size;
120  gpr_log(GPR_INFO, "Total outgoing bytes: %" PRId64,
121  num_outgoing_bytes_.load());
122  }
124  is_transacting_ = true;
125  absl::Status result = binder_->Transact(tx_code);
126  is_transacting_ = false;
127  return result;
128 }
129 
130 absl::Status WireWriterImpl::RpcCallFastPath(std::unique_ptr<Transaction> tx) {
131  return MakeBinderTransaction(
132  BinderTransportTxCode(tx->GetTxCode()),
133  [this, tx = tx.get()](
135  RETURN_IF_ERROR(parcel->WriteInt32(tx->GetFlags()));
136  RETURN_IF_ERROR(parcel->WriteInt32(next_seq_num_[tx->GetTxCode()]++));
137  if (tx->GetFlags() & kFlagPrefix) {
139  }
140  if (tx->GetFlags() & kFlagMessageData) {
142  parcel->WriteByteArrayWithLength(tx->GetMessageData()));
143  }
144  if (tx->GetFlags() & kFlagSuffix) {
146  }
147  return absl::OkStatus();
148  });
149 }
150 
152  RunScheduledTxArgs::StreamTx* stream_tx, WritableParcel* parcel,
153  bool* is_last_chunk) {
154  Transaction* tx = stream_tx->tx.get();
155  // Transaction without data flag should go to fast path.
157 
159  GPR_ASSERT(stream_tx->bytes_sent <= static_cast<int64_t>(data.size()));
160 
161  int flags = kFlagMessageData;
162 
163  if (stream_tx->bytes_sent == 0) {
164  // This is the first transaction. Include initial
165  // metadata if there's any.
166  if (tx->GetFlags() & kFlagPrefix) {
167  flags |= kFlagPrefix;
168  }
169  }
170  // There is also prefix/suffix in transaction beside the transaction data so
171  // actual transaction size will be greater than `kBlockSize`. This is
172  // unavoidable because we cannot split the prefix metadata and trailing
173  // metadata into different binder transactions. In most cases this is fine
174  // because single transaction size is not required to be strictly lower than
175  // `kBlockSize`, as long as it won't overflow Android's binder buffer.
176  int64_t size = std::min<int64_t>(WireWriterImpl::kBlockSize,
177  data.size() - stream_tx->bytes_sent);
178  if (stream_tx->bytes_sent + WireWriterImpl::kBlockSize >=
179  static_cast<int64_t>(data.size())) {
180  // This is the last transaction. Include trailing
181  // metadata if there's any.
182  if (tx->GetFlags() & kFlagSuffix) {
183  flags |= kFlagSuffix;
184  }
185  size = data.size() - stream_tx->bytes_sent;
186  *is_last_chunk = true;
187  } else {
188  // There are more messages to send.
190  *is_last_chunk = false;
191  }
192  RETURN_IF_ERROR(parcel->WriteInt32(flags));
193  RETURN_IF_ERROR(parcel->WriteInt32(next_seq_num_[tx->GetTxCode()]++));
194  if (flags & kFlagPrefix) {
196  }
198  data.substr(stream_tx->bytes_sent, size)));
199  if (flags & kFlagSuffix) {
201  }
202  stream_tx->bytes_sent += size;
203  return absl::OkStatus();
204 }
205 
207  GPR_ASSERT(args->writer == this);
208  if (absl::holds_alternative<RunScheduledTxArgs::AckTx>(args->tx)) {
209  int64_t num_bytes =
210  absl::get<RunScheduledTxArgs::AckTx>(args->tx).num_bytes;
213  [num_bytes](WritableParcel* parcel) {
214  RETURN_IF_ERROR(parcel->WriteInt64(num_bytes));
215  return absl::OkStatus();
216  });
217  if (!result.ok()) {
218  gpr_log(GPR_ERROR, "Failed to make binder transaction %s",
219  result.ToString().c_str());
220  }
221  delete args;
222  return;
223  }
224  GPR_ASSERT(absl::holds_alternative<RunScheduledTxArgs::StreamTx>(args->tx));
225  RunScheduledTxArgs::StreamTx* stream_tx =
226  &absl::get<RunScheduledTxArgs::StreamTx>(args->tx);
227  // Be reservative. Decrease CombinerTxCount after the data size of this
228  // transaction has already been added to `num_outgoing_bytes_`, to make sure
229  // we never underestimate `num_outgoing_bytes_`.
230  auto decrease_combiner_tx_count = absl::MakeCleanup([this]() {
231  {
233  GPR_ASSERT(num_non_acked_tx_in_combiner_ > 0);
234  num_non_acked_tx_in_combiner_--;
235  }
236  // New transaction might be ready to be scheduled.
238  });
239  if (CanBeSentInOneTransaction(*stream_tx->tx.get())) { // NOLINT
241  if (!result.ok()) {
242  gpr_log(GPR_ERROR, "Failed to handle non-chunked RPC call %s",
243  result.ToString().c_str());
244  }
245  delete args;
246  return;
247  }
248  bool is_last_chunk = true;
250  BinderTransportTxCode(stream_tx->tx->GetTxCode()),
251  [stream_tx, &is_last_chunk, this](WritableParcel* parcel)
253  return RunStreamTx(stream_tx, parcel, &is_last_chunk);
254  });
255  if (!result.ok()) {
256  gpr_log(GPR_ERROR, "Failed to make binder transaction %s",
257  result.ToString().c_str());
258  }
259  if (!is_last_chunk) {
260  {
262  pending_outgoing_tx_.push(args);
263  }
265  } else {
266  delete args;
267  }
268 }
269 
270 absl::Status WireWriterImpl::RpcCall(std::unique_ptr<Transaction> tx) {
271  // TODO(mingcl): check tx_code <= last call id
272  GPR_ASSERT(tx->GetTxCode() >= kFirstCallId);
273  auto args = new RunScheduledTxArgs();
274  args->writer = this;
276  absl::get<RunScheduledTxArgs::StreamTx>(args->tx).tx = std::move(tx);
277  absl::get<RunScheduledTxArgs::StreamTx>(args->tx).bytes_sent = 0;
278  {
280  pending_outgoing_tx_.push(args);
281  }
283  return absl::OkStatus();
284 }
285 
287  // Ensure combiner will be run if this is not called from top-level gRPC API
288  // entrypoint.
290  gpr_log(GPR_INFO, "Ack %" PRId64 " bytes received", num_bytes);
291  if (is_transacting_) {
292  // This can happen because NDK might call our registered callback function
293  // in the same thread while we are telling it to send a transaction
294  // `is_transacting_` will be true. `Binder::Transact` is now being called on
295  // the same thread or the other thread. We are currently in the call stack
296  // of other transaction, Liveness of ACK is still guaranteed even if this is
297  // a race with another thread.
298  gpr_log(
299  GPR_INFO,
300  "Scheduling ACK transaction instead of directly execute it to avoid "
301  "deadlock.");
302  auto args = new RunScheduledTxArgs();
303  args->writer = this;
305  absl::get<RunScheduledTxArgs::AckTx>(args->tx).num_bytes = num_bytes;
306  auto cl = GRPC_CLOSURE_CREATE(RunScheduledTx, args, nullptr);
308  return absl::OkStatus();
309  }
310  // Otherwise, we can directly send ack.
313  [num_bytes](WritableParcel* parcel) {
314  RETURN_IF_ERROR(parcel->WriteInt64(num_bytes));
315  return absl::OkStatus();
316  });
317  if (!result.ok()) {
318  gpr_log(GPR_ERROR, "Failed to make binder transaction %s",
319  result.ToString().c_str());
320  }
321  return result;
322 }
323 
325  // Ensure combiner will be run if this is not called from top-level gRPC API
326  // entrypoint.
328  gpr_log(GPR_INFO, "OnAckReceived %" PRId64, num_bytes);
329  // Do not try to obtain `write_mu_` in this function. NDKBinder might invoke
330  // the callback to notify us about new incoming binder transaction when we are
331  // sending transaction. i.e. `write_mu_` might have already been acquired by
332  // this thread.
333  {
335  num_acknowledged_bytes_ = std::max(num_acknowledged_bytes_, num_bytes);
336  int64_t num_outgoing_bytes = num_outgoing_bytes_;
337  if (num_acknowledged_bytes_ > num_outgoing_bytes) {
339  "The other end of transport acked more bytes than we ever sent, "
340  "%" PRId64 " > %" PRId64,
341  num_acknowledged_bytes_, num_outgoing_bytes);
342  }
343  }
345 }
346 
348  while (true) {
350  if (pending_outgoing_tx_.empty()) {
351  // Nothing to be schduled.
352  break;
353  }
354  // Number of bytes we have scheduled in combiner but have not yet be
355  // executed by combiner. Here we make an assumption that every binder
356  // transaction will take `kBlockSize`. This should be close to truth when a
357  // large message is being cut to `kBlockSize` chunks.
358  int64_t num_bytes_scheduled_in_combiner =
359  num_non_acked_tx_in_combiner_ * kBlockSize;
360  // An estimation of number of bytes of traffic we will eventually send to
361  // the other end, assuming all tasks in combiner will be executed and we
362  // receive no new ACK from the other end of transport.
363  int64_t num_total_bytes_will_be_sent =
364  num_outgoing_bytes_ + num_bytes_scheduled_in_combiner;
365  // An estimation of number of bytes of traffic that will not be
366  // acknowledged, assuming all tasks in combiner will be executed and we
367  // receive no new ack message fomr the other end of transport.
368  int64_t num_non_acked_bytes_estimation =
369  num_total_bytes_will_be_sent - num_acknowledged_bytes_;
370  if (num_non_acked_bytes_estimation < 0) {
371  gpr_log(
372  GPR_ERROR,
373  "Something went wrong. `num_non_acked_bytes_estimation` should be "
374  "non-negative but it is %" PRId64,
375  num_non_acked_bytes_estimation);
376  }
377  // If we can schedule another transaction (which has size estimation of
378  // `kBlockSize`) without exceeding `kFlowControlWindowSize`, schedule it.
379  if ((num_non_acked_bytes_estimation + kBlockSize <
381  num_non_acked_tx_in_combiner_++;
383  pending_outgoing_tx_.front(), nullptr),
385  pending_outgoing_tx_.pop();
386  } else {
387  // It is common to fill `kFlowControlWindowSize` completely because
388  // transactions are send at faster rate than the other end of transport
389  // can handle it, so here we use `GPR_DEBUG` log level.
391  "Some work cannot be scheduled yet due to slow ack from the "
392  "other end of transport. This transport might be blocked if this "
393  "number don't go down. pending_outgoing_tx_.size() = %zu "
394  "pending_outgoing_tx_.front() = %p",
395  pending_outgoing_tx_.size(), pending_outgoing_tx_.front());
396  break;
397  }
398  }
399 }
400 
401 } // namespace grpc_binder
402 
403 #endif
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
GPR_INFO
#define GPR_INFO
Definition: include/grpc/impl/codegen/log.h:56
GRPC_ERROR_NONE
#define GRPC_ERROR_NONE
Definition: error.h:234
log.h
grpc_binder::WireWriterImpl::RunStreamTx
absl::Status RunStreamTx(RunScheduledTxArgs::StreamTx *stream_tx, WritableParcel *parcel, bool *is_last_chunk) ABSL_EXCLUSIVE_LOCKS_REQUIRED(write_mu_)
Definition: wire_writer.cc:151
grpc_binder::WireWriterImpl::is_transacting_
std::atomic_bool is_transacting_
Definition: wire_writer.h:120
grpc_binder::Transaction::GetSuffixMetadata
const Metadata & GetSuffixMetadata() const
Definition: transaction.h:84
grpc_binder::WireWriterImpl::RunScheduledTxArgs::StreamTx::bytes_sent
int64_t bytes_sent
Definition: wire_writer.h:58
absl::MakeCleanup
absl::Cleanup< cleanup_internal::Tag, Callback > MakeCleanup(Callback callback)
Definition: abseil-cpp/absl/cleanup/cleanup.h:127
grpc_core::MutexLock
Definition: src/core/lib/gprpp/sync.h:88
grpc_binder::Transaction::GetFlags
int GetFlags() const
Definition: transaction.h:80
grpc_binder::WireWriterImpl::kBlockSize
static const int64_t kBlockSize
Definition: wire_writer.h:71
grpc_binder::Transaction::GetStatusDesc
absl::string_view GetStatusDesc() const
Definition: transaction.h:86
absl::string_view
Definition: abseil-cpp/absl/strings/string_view.h:167
grpc_binder
Definition: connection_id_generator.cc:45
absl::OkStatus
Status OkStatus()
Definition: third_party/abseil-cpp/absl/status/status.h:882
grpc_binder::kFlagMessageDataIsPartial
const int kFlagMessageDataIsPartial
Definition: transaction.cc:30
grpc_binder::WritableParcel::WriteString
virtual absl::Status WriteString(absl::string_view s)=0
grpc_binder::Transaction::GetMessageData
absl::string_view GetMessageData() const
Definition: transaction.h:85
grpc_binder::WireWriterImpl::RunScheduledTxArgs::writer
WireWriterImpl * writer
Definition: wire_writer.h:54
GRPC_CLOSURE_CREATE
#define GRPC_CLOSURE_CREATE(cb, cb_arg, scheduler)
Definition: closure.h:160
tx_code
int tx_code
Definition: fake_binder_test.cc:241
grpc_binder::WireWriterImpl::SendAck
absl::Status SendAck(int64_t num_bytes) override
Definition: wire_writer.cc:286
grpc_binder::Transaction::IsClient
bool IsClient() const
Definition: transaction.h:77
grpc_binder::Transaction::GetTxCode
int GetTxCode() const
Definition: transaction.h:79
grpc_binder::BinderTransportTxCode
BinderTransportTxCode
Definition: binder_constants.h:31
grpc_binder::WireWriterImpl::TryScheduleTransaction
void TryScheduleTransaction()
Definition: wire_writer.cc:347
grpc_binder::kFlagStatusDescription
const int kFlagStatusDescription
Definition: transaction.cc:28
grpc_combiner_create
grpc_core::Combiner * grpc_combiner_create(void)
Definition: combiner.cc:54
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
GPR_ASSERT
#define GPR_ASSERT(x)
Definition: include/grpc/impl/codegen/log.h:94
int64_t
signed __int64 int64_t
Definition: stdint-msvc2008.h:89
absl::string_view::size
constexpr size_type size() const noexcept
Definition: abseil-cpp/absl/strings/string_view.h:277
max
int max
Definition: bloaty/third_party/zlib/examples/enough.c:170
grpc_binder::WireWriterImpl::RpcCallFastPath
absl::Status RpcCallFastPath(std::unique_ptr< Transaction > tx)
Definition: wire_writer.cc:130
gpr_log
GPRAPI void gpr_log(const char *file, int line, gpr_log_severity severity, const char *format,...) GPR_PRINT_FORMAT_CHECK(4
ABSL_EXCLUSIVE_LOCKS_REQUIRED
#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: abseil-cpp/absl/base/thread_annotations.h:145
grpc_binder::WireWriterImpl::WireWriterImpl
WireWriterImpl(std::unique_ptr< Binder > binder)
Definition: wire_writer.cc:83
grpc_binder::WireWriterImpl::RunScheduledTxArgs::StreamTx::tx
std::unique_ptr< Transaction > tx
Definition: wire_writer.h:56
arg
Definition: cmdline.cc:40
grpc_core::Combiner::Run
void Run(grpc_closure *closure, grpc_error_handle error)
Definition: combiner.cc:343
RETURN_IF_ERROR
#define RETURN_IF_ERROR(expr)
Definition: wire_writer.cc:28
grpc_binder::WritableParcel::GetDataSize
virtual int32_t GetDataSize() const =0
grpc_binder::RunScheduledTx
void RunScheduledTx(void *arg, grpc_error_handle)
Definition: wire_writer.cc:43
grpc_binder::WireWriterImpl::kFlowControlWindowSize
static const int64_t kFlowControlWindowSize
Definition: wire_writer.h:74
data
char data[kBufferLength]
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1006
GPR_ERROR
#define GPR_ERROR
Definition: include/grpc/impl/codegen/log.h:57
grpc_binder::WritableParcel
Definition: binder.h:44
grpc_core::ExecCtx
Definition: exec_ctx.h:97
grpc_binder::kFlagPrefix
const int kFlagPrefix
Definition: transaction.cc:23
benchmark.md
md
Definition: benchmark.py:86
grpc_binder::WireWriterImpl::RunScheduledTxInternal
void RunScheduledTxInternal(RunScheduledTxArgs *arg)
Definition: wire_writer.cc:206
absl::flags_internal
Definition: abseil-cpp/absl/flags/commandlineflag.h:40
grpc_binder::WritableParcel::WriteInt32
virtual absl::Status WriteInt32(int32_t data)=0
grpc_binder::BinderTransportTxCode::ACKNOWLEDGE_BYTES
@ ACKNOWLEDGE_BYTES
exec_ctx
grpc_core::ExecCtx exec_ctx
Definition: end2end_binder_transport_test.cc:75
grpc_binder::WritableParcel::WriteByteArrayWithLength
absl::Status WriteByteArrayWithLength(absl::string_view buffer)
Definition: binder.h:54
absl::Status
Definition: third_party/abseil-cpp/absl/status/status.h:424
grpc_binder::WireWriterImpl::write_mu_
grpc_core::Mutex write_mu_
Definition: wire_writer.h:97
grpc_binder::kFlagSuffix
const int kFlagSuffix
Definition: transaction.cc:25
std
Definition: grpcpp/impl/codegen/async_unary_call.h:407
grpc_binder::Transaction::GetPrefixMetadata
const Metadata & GetPrefixMetadata() const
Definition: transaction.h:83
grpc_binder::WireWriterImpl::RunScheduledTxArgs
Definition: wire_writer.h:53
wire_writer.h
grpc_binder::kFlagMessageData
const int kFlagMessageData
Definition: transaction.cc:24
arg
struct arg arg
grpc_binder::WriteInitialMetadata
absl::Status WriteInitialMetadata(const Transaction &tx, WritableParcel *parcel)
Definition: wire_writer.cc:49
grpc_binder::Transaction::IsServer
bool IsServer() const
Definition: transaction.h:78
grpc_binder::Transaction::GetMethodRef
absl::string_view GetMethodRef() const
Definition: transaction.h:82
GPR_DEBUG
#define GPR_DEBUG
Definition: include/grpc/impl/codegen/log.h:55
grpc_binder::WireWriterImpl::RunScheduledTxArgs::AckTx
Definition: wire_writer.h:60
grpc_binder::WireWriterImpl::MakeBinderTransaction
absl::Status MakeBinderTransaction(BinderTransportTxCode tx_code, std::function< absl::Status(WritableParcel *)> fill_parcel)
Definition: wire_writer.cc:102
grpc_binder::WireWriterImpl::OnAckReceived
void OnAckReceived(int64_t num_bytes) override
Definition: wire_writer.cc:324
grpc_binder::kFirstCallId
const int kFirstCallId
Definition: binder_constants.cc:26
grpc_error
Definition: error_internal.h:42
function
std::function< bool(GrpcTool *, int, const char **, const CliCredentials &, GrpcToolOutputCallback)> function
Definition: grpc_tool.cc:250
size
voidpf void uLong size
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
int32_t
signed int int32_t
Definition: stdint-msvc2008.h:77
grpc_binder::WireWriterImpl::~WireWriterImpl
~WireWriterImpl() override
Definition: wire_writer.cc:89
GRPC_COMBINER_UNREF
#define GRPC_COMBINER_UNREF(combiner, reason)
Definition: combiner.h:71
grpc_binder::Transaction
Definition: transaction.h:40
grpc_binder::WireWriterImpl::RpcCall
absl::Status RpcCall(std::unique_ptr< Transaction > tx) override
Definition: wire_writer.cc:270
grpc_binder::CanBeSentInOneTransaction
bool CanBeSentInOneTransaction(const Transaction &tx)
Definition: wire_writer.cc:36
grpc_binder::WriteTrailingMetadata
absl::Status WriteTrailingMetadata(const Transaction &tx, WritableParcel *parcel)
Definition: wire_writer.cc:63
grpc_binder::WireWriterImpl::RunScheduledTxArgs::StreamTx
Definition: wire_writer.h:55
grpc_binder::WireWriterImpl::combiner_
grpc_core::Combiner * combiner_
Definition: wire_writer.h:122
grpc_binder::WireWriterImpl::num_outgoing_bytes_
std::atomic< int64_t > num_outgoing_bytes_
Definition: wire_writer.h:106
port_platform.h
grpc_binder::WritableParcel::WriteInt64
virtual absl::Status WriteInt64(int64_t data)=0
grpc_binder::WireWriterImpl::flow_control_mu_
grpc_core::Mutex flow_control_mu_
Definition: wire_writer.h:109


grpc
Author(s):
autogenerated on Fri May 16 2025 03:00:54