endpoint_cfstream.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2018 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
20 
22 
23 #ifdef GRPC_CFSTREAM_ENDPOINT
24 
25 #import <CoreFoundation/CoreFoundation.h>
26 
27 #include <grpc/slice_buffer.h>
28 #include <grpc/support/alloc.h>
30 
41 
43 
44 struct CFStreamEndpoint {
47 
48  CFReadStreamRef read_stream;
49  CFWriteStreamRef write_stream;
50  CFStreamHandle* stream_sync;
51 
54  grpc_slice_buffer* read_slices;
55  grpc_slice_buffer* write_slices;
56 
59 
60  std::string peer_string;
61  std::string local_address;
62 };
63 static void CFStreamFree(CFStreamEndpoint* ep) {
64  CFRelease(ep->read_stream);
65  CFRelease(ep->write_stream);
66  CFSTREAM_HANDLE_UNREF(ep->stream_sync, "free");
67  delete ep;
68 }
69 
70 #ifndef NDEBUG
71 #define EP_REF(ep, reason) CFStreamRef((ep), (reason), __FILE__, __LINE__)
72 #define EP_UNREF(ep, reason) CFStreamUnref((ep), (reason), __FILE__, __LINE__)
73 static void CFStreamUnref(CFStreamEndpoint* ep, const char* reason,
74  const char* file, int line) {
75  if (grpc_tcp_trace.enabled()) {
76  gpr_atm val = gpr_atm_no_barrier_load(&ep->refcount.count);
78  "CFStream endpoint unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep,
79  reason, val, val - 1);
80  }
81  if (gpr_unref(&ep->refcount)) {
82  CFStreamFree(ep);
83  }
84 }
85 static void CFStreamRef(CFStreamEndpoint* ep, const char* reason,
86  const char* file, int line) {
87  if (grpc_tcp_trace.enabled()) {
88  gpr_atm val = gpr_atm_no_barrier_load(&ep->refcount.count);
90  "CFStream endpoint ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep,
91  reason, val, val + 1);
92  }
93  gpr_ref(&ep->refcount);
94 }
95 #else
96 #define EP_REF(ep, reason) CFStreamRef((ep))
97 #define EP_UNREF(ep, reason) CFStreamUnref((ep))
98 static void CFStreamUnref(CFStreamEndpoint* ep) {
99  if (gpr_unref(&ep->refcount)) {
100  CFStreamFree(ep);
101  }
102 }
103 static void CFStreamRef(CFStreamEndpoint* ep) { gpr_ref(&ep->refcount); }
104 #endif
105 
106 static grpc_error_handle CFStreamAnnotateError(grpc_error_handle src_error,
107  CFStreamEndpoint* ep) {
108  return grpc_error_set_str(
111  GRPC_ERROR_STR_TARGET_ADDRESS, ep->peer_string);
112 }
113 
114 static void CallReadCb(CFStreamEndpoint* ep, grpc_error_handle error) {
115  if (grpc_tcp_trace.enabled()) {
116  gpr_log(GPR_DEBUG, "CFStream endpoint:%p call_read_cb %p %p:%p", ep,
117  ep->read_cb, ep->read_cb->cb, ep->read_cb->cb_arg);
118  size_t i;
119  gpr_log(GPR_DEBUG, "read: error=%s", grpc_error_std_string(error).c_str());
120 
121  for (i = 0; i < ep->read_slices->count; i++) {
122  char* dump = grpc_dump_slice(ep->read_slices->slices[i],
124  gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", ep, ep->peer_string.c_str(),
125  dump);
126  gpr_free(dump);
127  }
128  }
129  grpc_closure* cb = ep->read_cb;
130  ep->read_cb = nullptr;
131  ep->read_slices = nullptr;
133 }
134 
135 static void CallWriteCb(CFStreamEndpoint* ep, grpc_error_handle error) {
136  if (grpc_tcp_trace.enabled()) {
137  gpr_log(GPR_DEBUG, "CFStream endpoint:%p call_write_cb %p %p:%p", ep,
138  ep->write_cb, ep->write_cb->cb, ep->write_cb->cb_arg);
139  gpr_log(GPR_DEBUG, "write: error=%s", grpc_error_std_string(error).c_str());
140  }
141  grpc_closure* cb = ep->write_cb;
142  ep->write_cb = nullptr;
143  ep->write_slices = nullptr;
145 }
146 
147 static void ReadAction(void* arg, grpc_error_handle error) {
148  CFStreamEndpoint* ep = static_cast<CFStreamEndpoint*>(arg);
149  GPR_ASSERT(ep->read_cb != nullptr);
150  if (!GRPC_ERROR_IS_NONE(error)) {
152  CallReadCb(ep, GRPC_ERROR_REF(error));
153  EP_UNREF(ep, "read");
154  return;
155  }
156 
157  GPR_ASSERT(ep->read_slices->count == 1);
158  grpc_slice slice = ep->read_slices->slices[0];
159  size_t len = GRPC_SLICE_LENGTH(slice);
160  CFIndex read_size =
161  CFReadStreamRead(ep->read_stream, GRPC_SLICE_START_PTR(slice), len);
162  if (read_size == -1) {
164  CFErrorRef stream_error = CFReadStreamCopyError(ep->read_stream);
165  if (stream_error != nullptr) {
166  error = CFStreamAnnotateError(
167  GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "Read error"), ep);
168  CFRelease(stream_error);
169  } else {
171  }
172  CallReadCb(ep, error);
173  EP_UNREF(ep, "read");
174  } else if (read_size == 0) {
176  CallReadCb(ep,
177  CFStreamAnnotateError(
178  GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), ep));
179  EP_UNREF(ep, "read");
180  } else {
181  if (read_size < static_cast<CFIndex>(len)) {
182  grpc_slice_buffer_trim_end(ep->read_slices, len - read_size, nullptr);
183  }
184  CallReadCb(ep, GRPC_ERROR_NONE);
185  EP_UNREF(ep, "read");
186  }
187 }
188 
189 static void WriteAction(void* arg, grpc_error_handle error) {
190  CFStreamEndpoint* ep = static_cast<CFStreamEndpoint*>(arg);
191  GPR_ASSERT(ep->write_cb != nullptr);
192  if (!GRPC_ERROR_IS_NONE(error)) {
194  CallWriteCb(ep, GRPC_ERROR_REF(error));
195  EP_UNREF(ep, "write");
196  return;
197  }
198 
199  grpc_slice slice = grpc_slice_buffer_take_first(ep->write_slices);
200  size_t slice_len = GRPC_SLICE_LENGTH(slice);
201  CFIndex write_size = CFWriteStreamWrite(
202  ep->write_stream, GRPC_SLICE_START_PTR(slice), slice_len);
203  if (write_size == -1) {
205  CFErrorRef stream_error = CFWriteStreamCopyError(ep->write_stream);
206  if (stream_error != nullptr) {
207  error = CFStreamAnnotateError(
208  GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "write failed."), ep);
209  CFRelease(stream_error);
210  } else {
211  error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("write failed.");
212  }
213  CallWriteCb(ep, error);
214  EP_UNREF(ep, "write");
215  } else {
216  if (write_size < static_cast<CFIndex>(GRPC_SLICE_LENGTH(slice))) {
218  ep->write_slices, grpc_slice_sub(slice, write_size, slice_len));
219  }
220  if (ep->write_slices->length > 0) {
221  ep->stream_sync->NotifyOnWrite(&ep->write_action);
222  } else {
223  CallWriteCb(ep, GRPC_ERROR_NONE);
224  EP_UNREF(ep, "write");
225  }
226 
227  if (grpc_tcp_trace.enabled()) {
228  grpc_slice trace_slice = grpc_slice_sub(slice, 0, write_size);
229  char* dump = grpc_dump_slice(trace_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
230  gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", ep, ep->peer_string.c_str(),
231  dump);
232  gpr_free(dump);
233  grpc_slice_unref_internal(trace_slice);
234  }
235  }
237 }
238 
239 static void CFStreamRead(grpc_endpoint* ep, grpc_slice_buffer* slices,
240  grpc_closure* cb, bool urgent,
241  int /*min_progress_size*/) {
242  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
243  if (grpc_tcp_trace.enabled()) {
244  gpr_log(GPR_DEBUG, "CFStream endpoint:%p read (%p, %p) length:%zu", ep_impl,
245  slices, cb, slices->length);
246  }
247  GPR_ASSERT(ep_impl->read_cb == nullptr);
248  ep_impl->read_cb = cb;
249  ep_impl->read_slices = slices;
253  EP_REF(ep_impl, "read");
254  ep_impl->stream_sync->NotifyOnRead(&ep_impl->read_action);
255 }
256 
257 static void CFStreamWrite(grpc_endpoint* ep, grpc_slice_buffer* slices,
258  grpc_closure* cb, void* arg, int /*max_frame_size*/) {
259  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
260  if (grpc_tcp_trace.enabled()) {
261  gpr_log(GPR_DEBUG, "CFStream endpoint:%p write (%p, %p) length:%zu",
262  ep_impl, slices, cb, slices->length);
263  }
264  GPR_ASSERT(ep_impl->write_cb == nullptr);
265  ep_impl->write_cb = cb;
266  ep_impl->write_slices = slices;
267  EP_REF(ep_impl, "write");
268  ep_impl->stream_sync->NotifyOnWrite(&ep_impl->write_action);
269 }
270 
271 void CFStreamShutdown(grpc_endpoint* ep, grpc_error_handle why) {
272  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
273  if (grpc_tcp_trace.enabled()) {
274  gpr_log(GPR_DEBUG, "CFStream endpoint:%p shutdown (%s)", ep_impl,
276  }
277  CFReadStreamClose(ep_impl->read_stream);
278  CFWriteStreamClose(ep_impl->write_stream);
279  ep_impl->stream_sync->Shutdown(why);
280  if (grpc_tcp_trace.enabled()) {
281  gpr_log(GPR_DEBUG, "CFStream endpoint:%p shutdown DONE (%s)", ep_impl,
283  }
284 }
285 
286 void CFStreamDestroy(grpc_endpoint* ep) {
287  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
288  if (grpc_tcp_trace.enabled()) {
289  gpr_log(GPR_DEBUG, "CFStream endpoint:%p destroy", ep_impl);
290  }
291  EP_UNREF(ep_impl, "destroy");
292 }
293 
294 absl::string_view CFStreamGetPeer(grpc_endpoint* ep) {
295  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
296  return ep_impl->peer_string;
297 }
298 
299 absl::string_view CFStreamGetLocalAddress(grpc_endpoint* ep) {
300  CFStreamEndpoint* ep_impl = reinterpret_cast<CFStreamEndpoint*>(ep);
301  return ep_impl->local_address;
302 }
303 
304 int CFStreamGetFD(grpc_endpoint* ep) { return 0; }
305 
306 bool CFStreamCanTrackErr(grpc_endpoint* ep) { return false; }
307 
308 void CFStreamAddToPollset(grpc_endpoint* ep, grpc_pollset* pollset) {}
309 void CFStreamAddToPollsetSet(grpc_endpoint* ep, grpc_pollset_set* pollset) {}
310 void CFStreamDeleteFromPollsetSet(grpc_endpoint* ep,
311  grpc_pollset_set* pollset) {}
312 
313 static const grpc_endpoint_vtable vtable = {CFStreamRead,
314  CFStreamWrite,
315  CFStreamAddToPollset,
316  CFStreamAddToPollsetSet,
317  CFStreamDeleteFromPollsetSet,
318  CFStreamShutdown,
319  CFStreamDestroy,
320  CFStreamGetPeer,
321  CFStreamGetLocalAddress,
322  CFStreamGetFD,
323  CFStreamCanTrackErr};
324 
325 grpc_endpoint* grpc_cfstream_endpoint_create(CFReadStreamRef read_stream,
326  CFWriteStreamRef write_stream,
327  const char* peer_string,
328  CFStreamHandle* stream_sync) {
329  CFStreamEndpoint* ep_impl = new CFStreamEndpoint;
330  if (grpc_tcp_trace.enabled()) {
332  "CFStream endpoint:%p create readStream:%p writeStream: %p",
333  ep_impl, read_stream, write_stream);
334  }
335  ep_impl->base.vtable = &vtable;
336  gpr_ref_init(&ep_impl->refcount, 1);
337  ep_impl->read_stream = read_stream;
338  ep_impl->write_stream = write_stream;
339  CFRetain(read_stream);
340  CFRetain(write_stream);
341  ep_impl->stream_sync = stream_sync;
342  CFSTREAM_HANDLE_REF(ep_impl->stream_sync, "endpoint create");
343 
344  ep_impl->peer_string = peer_string;
345  grpc_resolved_address resolved_local_addr;
346  resolved_local_addr.len = sizeof(resolved_local_addr.addr);
347  CFDataRef native_handle = static_cast<CFDataRef>(CFReadStreamCopyProperty(
348  ep_impl->read_stream, kCFStreamPropertySocketNativeHandle));
349  CFSocketNativeHandle sockfd;
350  CFDataGetBytes(native_handle, CFRangeMake(0, sizeof(CFSocketNativeHandle)),
351  (UInt8*)&sockfd);
352  if (native_handle) {
353  CFRelease(native_handle);
354  }
356  if (getsockname(sockfd, reinterpret_cast<sockaddr*>(resolved_local_addr.addr),
357  &resolved_local_addr.len) < 0 ||
358  !(addr_uri = grpc_sockaddr_to_uri(&resolved_local_addr)).ok()) {
359  ep_impl->local_address = "";
360  } else {
361  ep_impl->local_address = addr_uri.value();
362  }
363  ep_impl->read_cb = nil;
364  ep_impl->write_cb = nil;
365  ep_impl->read_slices = nil;
366  ep_impl->write_slices = nil;
367  GRPC_CLOSURE_INIT(&ep_impl->read_action, ReadAction,
368  static_cast<void*>(ep_impl), grpc_schedule_on_exec_ctx);
369  GRPC_CLOSURE_INIT(&ep_impl->write_action, WriteAction,
370  static_cast<void*>(ep_impl), grpc_schedule_on_exec_ctx);
371 
372  return &ep_impl->base;
373 }
374 
375 #endif /* GRPC_CFSTREAM_ENDPOINT */
GRPC_CLOSURE_INIT
#define GRPC_CLOSURE_INIT(closure, cb, cb_arg, scheduler)
Definition: closure.h:115
grpc_endpoint_vtable
Definition: endpoint.h:39
vtable
static const grpc_transport_vtable vtable
Definition: binder_transport.cc:680
gpr_atm_no_barrier_load
#define gpr_atm_no_barrier_load(p)
Definition: impl/codegen/atm_gcc_atomic.h:53
GRPC_ERROR_NONE
#define GRPC_ERROR_NONE
Definition: error.h:234
sockaddr_utils.h
GRPC_STATUS_UNAVAILABLE
@ GRPC_STATUS_UNAVAILABLE
Definition: include/grpc/impl/codegen/status.h:143
read_cb
static void read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
Definition: benchmark-pound.c:138
grpc_dump_slice
char * grpc_dump_slice(const grpc_slice &s, uint32_t flags)
Definition: slice_string_helpers.cc:25
grpc_pollset_set
struct grpc_pollset_set grpc_pollset_set
Definition: iomgr_fwd.h:23
string.h
absl::string_view
Definition: abseil-cpp/absl/strings/string_view.h:167
gpr_free
GPRAPI void gpr_free(void *ptr)
Definition: alloc.cc:51
GRPC_SLICE_MALLOC
#define GRPC_SLICE_MALLOC(len)
Definition: include/grpc/slice.h:70
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
error
grpc_error_handle error
Definition: retry_filter.cc:499
grpc_resolved_address
Definition: resolved_address.h:34
file
Definition: bloaty/third_party/zlib/examples/gzappend.c:170
closure.h
grpc_slice_sub
GPRAPI grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end)
Definition: slice/slice.cc:268
GPR_DUMP_HEX
#define GPR_DUMP_HEX
Definition: string.h:34
grpc_slice_buffer_take_first
GPRAPI grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer *sb)
Definition: slice/slice_buffer.cc:438
GPR_LOG_SEVERITY_DEBUG
@ GPR_LOG_SEVERITY_DEBUG
Definition: include/grpc/impl/codegen/log.h:46
sockaddr.h
write_action
static void write_action(void *t, grpc_error_handle error)
Definition: chttp2_transport.cc:987
GPR_DUMP_ASCII
#define GPR_DUMP_ASCII
Definition: string.h:35
gpr_refcount
Definition: impl/codegen/sync_generic.h:39
grpc_error_set_str
grpc_error_handle grpc_error_set_str(grpc_error_handle src, grpc_error_strs which, absl::string_view str)
Definition: error.cc:650
GRPC_ERROR_STR_TARGET_ADDRESS
@ GRPC_ERROR_STR_TARGET_ADDRESS
peer that we were trying to communicate when this error occurred
Definition: error.h:117
grpc_tcp_trace
grpc_core::TraceFlag grpc_tcp_trace(false, "tcp")
DEBUG_LOCATION
#define DEBUG_LOCATION
Definition: debug_location.h:41
string_util.h
refcount
size_t refcount
Definition: abseil-cpp/absl/strings/internal/cordz_info.cc:122
GPR_ASSERT
#define GPR_ASSERT(x)
Definition: include/grpc/impl/codegen/log.h:94
gen_stats_data.c_str
def c_str(s, encoding='ascii')
Definition: gen_stats_data.py:38
slice
grpc_slice slice
Definition: src/core/lib/surface/server.cc:467
gpr_log
GPRAPI void gpr_log(const char *file, int line, gpr_log_severity severity, const char *format,...) GPR_PRINT_FORMAT_CHECK(4
GRPC_SLICE_START_PTR
#define GRPC_SLICE_START_PTR(slice)
Definition: include/grpc/impl/codegen/slice.h:101
slice_buffer.h
error_cfstream.h
arg
Definition: cmdline.cc:40
cfstream_handle.h
grpc_slice
Definition: include/grpc/impl/codegen/slice.h:65
grpc_resolved_address::len
socklen_t len
Definition: resolved_address.h:36
gen_synthetic_protos.base
base
Definition: gen_synthetic_protos.py:31
endpoint_cfstream.h
slice_internal.h
GRPC_ERROR_CREATE_FROM_STATIC_STRING
#define GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc)
Definition: error.h:291
GRPC_SLICE_LENGTH
#define GRPC_SLICE_LENGTH(slice)
Definition: include/grpc/impl/codegen/slice.h:104
grpc_core::TraceFlag
Definition: debug/trace.h:63
grpc_core::TraceFlag::enabled
bool enabled()
Definition: debug/trace.h:82
gpr_atm
intptr_t gpr_atm
Definition: impl/codegen/atm_gcc_atomic.h:32
GRPC_ERROR_REF
#define GRPC_ERROR_REF(err)
Definition: error.h:261
grpc_error_set_int
grpc_error_handle grpc_error_set_int(grpc_error_handle src, grpc_error_ints which, intptr_t value)
Definition: error.cc:613
port.h
grpc_error_std_string
std::string grpc_error_std_string(grpc_error_handle error)
Definition: error.cc:944
grpc_slice_buffer_undo_take_first
GPRAPI void grpc_slice_buffer_undo_take_first(grpc_slice_buffer *sb, grpc_slice slice)
Definition: slice/slice_buffer.cc:467
alloc.h
slices
SliceBuffer * slices
Definition: retry_filter.cc:631
regen-readme.line
line
Definition: regen-readme.py:30
ok
bool ok
Definition: async_end2end_test.cc:197
arg
struct arg arg
grpc_core::ExecCtx::Run
static void Run(const DebugLocation &location, grpc_closure *closure, grpc_error_handle error)
Definition: exec_ctx.cc:98
grpc_slice_buffer_trim_end
GPRAPI void grpc_slice_buffer_trim_end(grpc_slice_buffer *sb, size_t n, grpc_slice_buffer *garbage)
Definition: slice/slice_buffer.cc:402
GPR_DEBUG
#define GPR_DEBUG
Definition: include/grpc/impl/codegen/log.h:55
grpc_slice_buffer
Definition: include/grpc/impl/codegen/slice.h:83
absl::StatusOr::value
const T & value() const &ABSL_ATTRIBUTE_LIFETIME_BOUND
Definition: abseil-cpp/absl/status/statusor.h:687
gpr_ref_init
GPRAPI void gpr_ref_init(gpr_refcount *r, int n)
Definition: sync.cc:86
absl::StatusOr< std::string >
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
endpoint.h
gpr_unref
GPRAPI int gpr_unref(gpr_refcount *r)
Definition: sync.cc:103
grpc_error
Definition: error_internal.h:42
read_size
static int read_size
Definition: test-tcp-close-reset.c:48
write_cb
static void write_cb(uv_write_t *req, int status)
Definition: benchmark-pump.c:190
grpc_pollset
Definition: bm_cq_multiple_threads.cc:37
read_action
static void read_action(void *t, grpc_error_handle error)
Definition: chttp2_transport.cc:2505
grpc_closure
Definition: closure.h:56
grpc_sockaddr_to_uri
absl::StatusOr< std::string > grpc_sockaddr_to_uri(const grpc_resolved_address *resolved_addr)
Definition: sockaddr_utils.cc:260
grpc_resolved_address::addr
char addr[GRPC_MAX_SOCKADDR_SIZE]
Definition: resolved_address.h:35
grpc_endpoint
Definition: endpoint.h:105
GRPC_TCP_DEFAULT_READ_SLICE_SIZE
#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE
Definition: grpc_types.h:334
gpr_ref
GPRAPI void gpr_ref(gpr_refcount *r)
Definition: sync.cc:88
grpc_slice_buffer_reset_and_unref_internal
void grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer *sb)
Definition: slice/slice_buffer.cc:238
cb
OPENSSL_EXPORT pem_password_cb * cb
Definition: pem.h:351
slice_string_helpers.h
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
GRPC_ERROR_INT_GRPC_STATUS
@ GRPC_ERROR_INT_GRPC_STATUS
grpc status code representing this error
Definition: error.h:66
grpc_slice_unref_internal
void grpc_slice_unref_internal(const grpc_slice &slice)
Definition: slice_refcount.h:39
grpc_slice_buffer_add_indexed
GPRAPI size_t grpc_slice_buffer_add_indexed(grpc_slice_buffer *sb, grpc_slice slice)
Definition: slice/slice_buffer.cc:161
GRPC_ERROR_IS_NONE
#define GRPC_ERROR_IS_NONE(err)
Definition: error.h:241
port_platform.h


grpc
Author(s):
autogenerated on Fri May 16 2025 02:58:19